C: Appel gratuit sur une variable automatique?

int main() { int x = 0; free(x); } 

Cela comstack et semble être un no-op. Qu’est-ce qui se passe réellement? Ce comportement est-il défini?

Merci!

Non, le comportement n’est pas défini. De plus, le code n’est pas censé comstackr.

Tout d’abord, le code n’est pas supposé comstackr car il contient une violation de contrainte. L’expression que vous transmettez en tant qu’opérande pour free type int . Le paramètre de free a void * type. Le seul cas où une valeur int peut être convertie implicitement en type void * est le cas où la valeur int est une expression constante intégrale (ICE) avec la valeur 0 . Dans votre cas, x n’est pas une ICE, ce qui signifie qu’elle ne peut pas être convertie implicitement en void * . La seule raison pour laquelle votre code est compilé est que, pour des raisons historiques (pour prendre en charge le code hérité), votre compilateur ignore discrètement la violation de contrainte présente dans l’appel free(x) . Je suis sûr que si vous élevez le niveau des avertissements dans votre compilateur, il se plaindra (au moins avec un avertissement). Un compilateur pédant produira immédiatement une erreur pour free(x) appel free(x) . Essayez Comeau Online, par exemple en mode C89 / 90:

 "ComeauTest.c", line 6: error: argument of type "int" is incompatible with parameter of type "void *" free(x); ^ 

(En outre, avez-vous stdlib.h à inclure stdlib.h avant d’appeler free ?)

Deuxièmement, supposons que le code soit compilé, c’est-à-dire qu’il soit interprété par le compilateur comme étant free((void *) x) . Dans ce cas, une valeur intégrale non constante x est convertie en pointeur de type void * . Le résultat de cette conversion est défini par l’implémentation. Notez que le langage garantit que lorsqu’une ICE avec la valeur 0 est convertie en type de pointeur, le résultat est un pointeur nul. Mais dans votre cas, x n’est pas une ICE, le résultat de la conversion est donc défini par l’implémentation. En C, rien ne garantit que vous obtiendrez un pointeur null en convertissant un entier non-ICE de valeur 0 en type pointeur. Dans votre implémentation, il est probablement arrivé que (void *) x avec une valeur non-ICE x égale à 0 génère une valeur de pointeur nulle de type void * . Cette valeur de pointeur null, lorsqu’elle est passée à free , a pour résultat un non-op, selon la spécification de free .

En règle générale, passer un tel pointeur à free entraîne un comportement indéfini. Les pointeurs que vous pouvez légalement passer à free sont des pointeurs obtenus lors d’appels précédents aux pointeurs malloc / calloc / realloc et null. Votre pointeur viole cette contrainte en général, le comportement n’est donc pas défini.

C’est ce qui se passe dans votre cas. Mais, encore une fois, votre code contient une violation de contrainte. Et même si vous annulez la violation, le comportement n’est pas défini.

PS Notez, BTW, que de nombreuses réponses déjà publiées ici font la même grave erreur. Ils supposent que (void *) x avec zéro x est supposé produire un pointeur nul. Ceci est absolument incorrect. Encore une fois, le langage ne donne aucune garantie sur le résultat de (void *) x lorsque x n’est pas une ICE. (void *) 0 est garanti pointeur null, mais (void *) x avec zéro x n’est pas garanti pointeur nul.

Ceci est couvert dans la FAQ C http://c-faq.com/null/runtime0.html . Pour ceux qui sont intéressés à mieux comprendre pourquoi il en est ainsi, il pourrait être judicieux de lire toute la section sur les pointeurs nuls http://c-faq.com/null/index.html

Dans votre cas, cela fonctionne car appeler gratuitement (NULL) (c’est-à-dire que libre (0)) est un NOOP. Toutefois, si vous l’appeliez avec une valeur autre que 0 (NULL), le comportement serait indéfini – les plantages et / ou la corruption de mémoire sont des candidats probables.

EDIT: Comme d’autres l’ont souligné plus tard, free (x) (avec x = 0) et free (NULL) ne sont pas la même chose. (Bien qu’il soit souvent égal à 0, la valeur du pointeur NULL est définie par l’implémentation et ne peut pas être utilisée.) Veuillez consulter la réponse de AndreyT pour une très bonne clarification.

Libérer NULL (ou 0) ne fait rien. C’est un no-op.

Avez-vous essayé d’augmenter le niveau d’alerte de votre compilateur? Par exemple, gcc -ansi -pedantic -W -Wall rapporte:

tmp.c: 6: warning: passer l’argument 1 de ‘free’ rend le pointeur à partir d’un entier sans transt

Le comportement est indéfini. Ne le fais pas.

De la page de manuel free :

free () libère l’espace mémoire désigné par ptr, qui doit avoir été renvoyé par un précédent appel à malloc (), calloc () ou realloc (). Sinon, ou si free (ptr) a déjà été appelé auparavant, un comportement indéfini se produit. Si ptr est NULL, aucune opération n’est effectuée.

Dans votre exemple, vous appelez réellement free(0) , car free accepte un pointeur en tant qu’argument. Vous dites essentiellement au moteur d’exécution de libérer la mémoire à l’adresse 0, qui n’a pas encore été allouée par malloc .

Puisque ‘0’ est NULL, rien ne se passera. (Merci aux commentaires pour avoir souligné mon erreur stupide).

free () fonctionne sur la mémoire allouée dynamicment, c’est-à-dire la mémoire allouée avec malloc, calloc ou realloc. En outre, il faut un pointeur et vous transmettez la valeur de x. Il n’y a aucune raison de l’appeler sur la stack. Les variables allouées seront libérées lorsque la stack se déroulera et que ce comportement sera indéfini. Toujours bien de lire la documentation.

http://www.cplusplus.com/reference/clibrary/cstdlib/free/

Il devrait jeter une violation d’access sur la plupart des architectures. OK c’est un 0 donc ça marche, mais si ce n’était pas 0 ça échouerait. C’est indéfini. Vous ne pouvez pas free() une variable allouée à une stack.

  

 libre (0);

 "Appeler la fonction free avec la valeur NULL n'aura aucun effet."

En fait, cela aura un impact lorsque nous libérerons la mémoire utilisée.

Ici, x est égal à zéro, de sorte que cela ne pose aucun problème. Si x a une valeur autre que

zéro, dans ce cas, il peut y avoir une erreur de segmentation. Parce que x peut-être utiliser comme un

représentation de la mémoire d’autres variables.