web-dev-qa-db-fra.com

En C, pourquoi certaines personnes lancent-elles le pointeur avant de le libérer?

Je travaille sur une ancienne base de code et pratiquement chaque invocation de free () utilise un argument pour son argument. Par exemple,

free((float *)velocity);
free((float *)acceleration);
free((char *)label);

où chaque pointeur est du type correspondant (et correspondant). Je ne vois pas l'intérêt de faire cela du tout. C'est un très vieux code, alors je me demande si c'est un problème de K & R. Si tel est le cas, je souhaite réellement aider les anciens compilateurs qui l’auraient peut-être demandé, aussi je ne souhaite pas les supprimer.

Existe-t-il une raison technique pour utiliser ces moulages? Je ne vois même pas beaucoup de raisons pragmatiques de les utiliser. Quel est l'intérêt de se rappeler le type de données juste avant de le libérer?

EDIT: Cette question est pas un duplicata de l'autre question. L'autre question est un cas particulier de cette question, qui, à mon avis, est évident si les électeurs proches lisent toutes les réponses.

Colophon: Je coche la case "réponse constante" parce que c'est une véritable raison pour laquelle cela pourrait devoir être fait; Cependant, la réponse selon laquelle il s'agissait d'une coutume pré-ANSI C (du moins chez certains programmeurs) semble être la raison pour laquelle il a été utilisé dans mon cas. Beaucoup de bons points par beaucoup de gens ici. Merci pour vos contributions.

164

Un casting peut être nécessaire pour résoudre les avertissements du compilateur si les pointeurs sont const. Voici un exemple de code qui provoque un avertissement sans avoir à lancer l'argument de free:

const float* velocity = malloc(2*sizeof(float));
free(velocity);

Et le compilateur (gcc 4.8.3) dit:

main.c: In function ‘main’:
main.c:9:5: warning: passing argument 1 of ‘free’ discards ‘const’ qualifier from pointer target type [enabled by default]
     free(velocity);
     ^
In file included from main.c:2:0:
/usr/include/stdlib.h:482:13: note: expected ‘void *’ but argument is of type ‘const float *’
 extern void free (void *__ptr) __THROW;

Si vous utilisez free((float*) velocity);, le compilateur cesse de se plaindre.

169
Manos Nikolaidis

Pré-standard C n'avait pas void* mais, seulement char*, vous avez donc dû convertir tous les paramètres transmis. Si vous rencontrez du code C ancien, vous pourriez donc trouver de tels moulages.

Question similaire avec références .

Lorsque le premier standard C a été publié, les prototypes pour malloc et free ont changé de char* à la void* qu'ils ont encore aujourd'hui.

Et bien sûr, dans la norme C, de tels moulages sont superflus et nuisent à la lisibilité.

60
Lundin

Voici un exemple où free échouerait sans casting:

volatile int* p = (volatile int*)malloc(5 * sizeof(int));
free(p);        // fail: warning C4090: 'function' : different 'volatile' qualifiers
free((int*)p);  // success :)
free((void*)p); // success :)

En C, vous pouvez recevoir un avertissement (en avoir un dans VS2012). En C++, vous obtiendrez une erreur.

Les cas rares mis à part, le casting gonfle le code ...

Edit: J'ai fait un casting sur void* ne pas int* pour démontrer l’échec. Cela fonctionnera comme int* sera converti en void* implicitement. Ajoutée int* code.

33
egur

Ancienne raison: 1. En utilisant free((sometype*) ptr), le code indique explicitement le type de pointeur à considérer comme faisant partie de l'appel free(). La conversion explicite est utile lorsque free() est remplacé par un (à faire soi-même) DIY_free().

#define free(ptr) DIY_free(ptr, sizeof (*ptr))

Une DIY_free() était (est) un moyen, notamment en mode débogage, de faire une analyse à l'exécution du pointeur en cours de libération. Ceci est souvent associé à une DIY_malloc() pour ajouter des messages, des comptes d'utilisation de la mémoire globale, etc. Mon groupe a utilisé cette technique pendant des années avant que des outils plus modernes n'apparaissent. Il était obligé que l'élément étant libre avait été jeté sur le type est alloué à l'origine.

  1. Étant donné les nombreuses heures consacrées à la recherche de problèmes de mémoire, etc., de petites astuces telles que la conversion de caractères gratuits aideraient à rechercher et à réduire le débogage.

Moderne: éviter les avertissements const et volatile comme adressés par Manos Nikolaidis @ et @ egur . Je pensais noter les effets des 3 qualificateurs : const, volatile et restrict .

[edit] Ajouté char * restrict *rp2 par @ R .. commentaire

void free_test(const char *cp, volatile char *vp, char * restrict rp, 
    char * restrict *rp2) {
  free(cp);  // warning
  free(vp);  // warning
  free(rp);  // OK
  free(rp2);  // warning
}

int main(void) {
  free_test(0,0,0,0);
  return 0;
}
30
chux

Voici une autre hypothèse alternative.

On nous dit que le programme a été écrit avant C89, ce qui signifie qu'il ne peut pas fonctionner avec une sorte de décalage avec le prototype de free, car non seulement const ni void * avant C89, il n’existait pas de prototype de fonction avant C89. stdlib.h Était une invention du comité. Si les en-têtes système avaient la peine de déclarer free, ils l'auraient fait comme ceci:

extern free();  /* no `void` return type either! */

Maintenant, le point clé ici est que l'absence de prototypes de fonctions signifie que le compilateur a fait pas de vérification du type d'argument. Il appliquait les promotions d'argument par défaut (les mêmes que celles qui s'appliquent toujours aux appels de fonctions variadiques) et c'était tout. La responsabilité de formuler les arguments à chaque site d’appel en fonction des attentes de l’appelé incombe entièrement au programmeur.

Cependant, cela ne signifie toujours pas qu'il était nécessaire de transtyper l'argument en tant que free sur la plupart des compilateurs K & R. Une fonction comme

free_stuff(a, b, c)
    float *a;
    char *b;
    int *c;
{
    free(a);
    free(b);
    free(c);
}

aurait dû être compilé correctement. Donc, je pense que ce que nous avons ici est un programme écrit pour faire face à un compilateur buggy pour un environnement inhabituel: par exemple, un environnement où sizeof(float *) > sizeof(int) et le compilateur non Utilisez la convention d'appel appropriée pour les pointeurs, à moins que vous ne les utilisiez au moment de l'appel.

Je ne suis pas au courant d'un tel environnement, mais cela ne signifie pas qu'il n'y en avait pas. Les candidats les plus probables qui viennent à l’esprit sont les compilateurs "C minuscules" découpés pour les micros 8 et 16 bits au début des années 1980. Je ne serais pas surpris d'apprendre que les premiers Crays avaient des problèmes comme celui-ci.

16
zwol

free prend en paramètre uniquement les pointeurs non const. Ainsi, dans le cas de pointeurs const, une conversion explicite en un pointeur non const est requise.

Impossible de libérer les pointeurs const en C

9
Nobody