Réglage variable à NULL après libre
Dans mon entreprise, il y a un codage règle qui dit que, après la libération de la mémoire, de réinitialiser la variable à NULL. Par exemple ...
void some_func ()
{
int *nPtr;
nPtr = malloc (100);
free (nPtr);
nPtr = NULL;
return;
}
J'ai l'impression que, dans des cas comme le code ci-dessus, le réglage de la valeur NULL n'a pas de sens. Ou ai-je raté quelque chose?
Si il n'y a pas de sens dans ce cas, je vais le prendre avec le "la qualité de l'équipe" pour supprimer cette règle de codage. S'il vous plaît conseils.
- il est toujours utile d'être en mesure de vérifier si
ptr == NULL
avant de faire quelque chose avec elle. Si vous n'avez pas annuler votre libre avais pointeurs, vous obtiendrezptr != NULL
mais toujours inutilisable pointeur. - Balançant des pointeurs peut conduire à des vulnérabilités exploitables tels que use-After-Free.
Vous devez vous connecter pour publier un commentaire.
Paramètre inutilisé pointeurs à NULL est un style défensif, la protection contre les balançant pointeur de bugs. Si un bancales pointeur est accessible après il est libéré, vous pouvez lire ou écraser à la mémoire vive. Si un pointeur null est accessible, vous obtenez immédiatement un crash sur la plupart des systèmes, vous dire tout de suite quelle est l'erreur.
Pour les variables locales, il peut être un peu inutile si il est "évident" que le pointeur n'est pas plus accessible après avoir été libéré, de sorte que ce style est plus approprié pour les données des membres et des variables globales. Même pour les variables locales, il peut être une bonne approche si la fonction se poursuit après la mémoire est libérée.
Pour compléter le style, vous devez également initialiser les pointeurs à NULL avant on leur attribue une véritable valeur du pointeur.
int *nPtr=NULL;
. Maintenant, je suis d'accord que ce serait redondant, avec un malloc suite à droite dans la ligne suivante. Toutefois, si le code entre la déclaration et l'initialisation premier, quelqu'un pourrait démarrer à l'aide de la variable, même si elle n'a pas encore de valeur. Si vous null-initialiser, vous obtenez l'erreur de segmentation; sans, vous pouvez de nouveau de lecture ou d'écriture de mémoire vive. De même, si la variable plus tard est initialisé que conditionnellement, plus tard défectueux accède devrait vous donner instant se bloque si vous rappeler à null-initialiser.Paramètre un pointeur vers
NULL
aprèsfree
est une pratique douteuse qui est souvent popularisé comme une "bonne programmation" règle manifestement fausse prémisse. C'est l'une de ces fausses vérités qui appartiennent à la "sonne juste" de la catégorie, mais en réalité atteindre absolument rien d'utile (et parfois conduit à des conséquences négatives).Prétendument, paramètre un pointeur vers
NULL
aprèsfree
est censé empêcher le terrible "double" problème lors de la même valeur du pointeur est passé àfree
plus d'une fois. En réalité, si, dans 9 cas sur 10, le véritable "double" problème se produit lorsque différents pointeur des objets ayant la même valeur de pointeur sont utilisés comme arguments pourfree
. Inutile de dire que, paramètre un pointeur versNULL
aprèsfree
réalise absolument rien pour empêcher le problème dans ce cas.Bien sûr, il est possible d'exécuter en "double" problème lors de l'utilisation de la même pointeur de l'objet comme un argument de
free
. Cependant, dans la réalité des situations comme ça normalement indiquer un problème avec le général de la structure logique du code, non pas un simple accidentelle "double". Une bonne façon de traiter le problème dans ce cas est de revoir et de repenser la structure du code, afin d'éviter la situation où le même pointeur est passé àfree
plus d'une fois. Dans de tels cas, le réglage du pointeur deNULL
et en considérant que le problème est "résolu" n'est rien de plus qu'une tentative de balayer le problème sous le tapis. Il ne fonctionnera pas dans le cas général, parce que le problème avec la structure du code qui sera toujours à trouver une autre façon de se manifester.Enfin, si votre code est spécifiquement conçu pour s'appuyer sur la valeur du pointeur d'être
NULL
ou pasNULL
, il est parfaitement bien pour définir la valeur du pointeur pourNULL
aprèsfree
. Mais en général les "bonnes pratiques" de la règle (comme dans "toujours définir le pointeur de votre sourisNULL
aprèsfree
"), il est, une fois de plus, un fait bien connu et assez inutile, faux, souvent suivis par certains pour des raisons purement religieux, le vaudou comme raisons.foo* bar=getFoo(); /*more_code*/ free(bar); /*more_code*/ return bar != NULL;
. Ici, le réglage debar
àNULL
après l'appel àfree
sera la cause de la fonction de penser que c' jamais avait un bar et retourner la valeur faux!La plupart des réponses ont mis l'accent sur la prévention d'une double gratuit, mais le réglage du pointeur à NULL a un autre avantage. Une fois que vous libérer un pointeur, qui de mémoire est disponible pour être réaffectés par un autre appel à malloc. Si vous avez toujours l'original pointeur autour de vous pourriez vous retrouver avec un bug où vous essayez d'utiliser le pointeur après libre et corrompre une autre variable, et ensuite votre programme entre dans un état inconnu, et toutes sortes de mauvaises choses peuvent se produire (se bloquer si vous avez de la chance, la corruption de données si vous êtes malchanceux). Si vous deviez définir le pointeur à NULL après libre, toute tentative de lecture/écriture par le biais de ce pointeur plus tard, le résultat serait une erreur de segmentation, qui est généralement préférable au hasard à une corruption de la mémoire.
Pour ces deux raisons, il peut être une bonne idée de mettre le pointeur à NULL après free(). Il n'est pas toujours nécessaire, cependant. Par exemple, si le pointeur de variable est hors de portée immédiatement après free(), il n'y a pas beaucoup de raisons de le mettre à NULL.
free
, mais cela fait vraiment sens.C'est considéré comme une bonne pratique pour éviter d'écraser la mémoire. Dans la fonction ci-dessus, il est inutile, mais souvent quand c'est fait, il peut trouver des erreurs d'application.
Essayer quelque chose comme ceci à la place:
La DEBUG_VERSION vous permet de profil libère le débogage de code, mais les deux sont fonctionnellement identiques.
Modifier: Ajouté le faire ... tout comme suggéré ci-dessous, merci.
do { } while(0)
bloc de sorte queif(x) myfree(x); else dostuff();
ne rompt pas.do {X} while (0)
est IMO le meilleur moyen de faire une macro corps qui "se sent comme et fonctionne comme" une fonction. La plupart des compilateurs optimiser la boucle de toute façon.Si vous atteignez pointeur qui a été free()d, il pourrait se casser ou pas. Que la mémoire peut être réaffectée à une autre partie de votre programme, puis vous obtenez une corruption de la mémoire
Si vous réglez le pointeur à NULL, alors si vous y accédez, le programme se bloque toujours avec une erreur de segmentation. Pas plus , parfois ça marche", ni plus,les accidents dans la propriété plutôt imprévisible façon". C'est beaucoup plus facile à déboguer.
Paramètre le pointeur de la
free
'd de la mémoire signifie que toute tentative d'accès à cette mémoire via le pointeur sera immédiatement crash, au lieu de provoquer un comportement indéfini. Il rend beaucoup plus facile de déterminer où les choses ont mal tourné.Je peux voir votre argument: depuis
nPtr
est hors de portée juste aprèsnPtr = NULL
, il ne semble pas être une raison pour le mettre enNULL
. Toutefois, dans le cas d'unstruct
membre ou à tout autre endroit où le pointeur n'est pas immédiatement d'aller hors de portée, il fait plus de sens. Il n'est pas immédiatement évident de savoir si ou non le pointeur sera à nouveau utilisée par le code qui ne devrait pas l'utiliser.Il est probable que la règle est déclaré sans faire une distinction entre ces deux cas, car il est beaucoup plus difficile d'appliquer automatiquement la règle, encore moins pour les développeurs de le suivre. Il ne fait pas de mal à définir des pointeurs vers
NULL
après chaque gratuit, mais il a le potentiel de mettre en évidence de gros problèmes.les plus courantes bug c est le double gratuit. Fondamentalement, vous faites quelque chose comme ça
et ce que tout cela finira très mal, les OS essayer de libérer déjà libéré de la mémoire et généralement c'erreur de segmentation. Donc, la bonne pratique est de mettre à
NULL
, de sorte que vous pouvez faire tester et de vérifier si vous avez vraiment besoin de libérer cette mémoireégalement de noter que
free(NULL)
ne fera rien si vous n'avez pas à écrire si l'instruction. Je ne suis pas vraiment un OS gourou, mais je suis encore aujourd'hui, la plupart des Systèmes d'exploitation de crash sur double gratuit.C'est également la raison principale pourquoi toutes les langues avec la collecte des ordures (Java, dotnet) était si fier de ne pas avoir ce problème et aussi de ne pas avoir à quitter des développeurs la gestion de la mémoire dans son ensemble.
if(p!=null){.....}
, il est inutile de définir la valeur NULL?NULL
de sorte que vous peut pré-vérifier. (ou tout simplement appelerfree()
de nouveau.)p = (char *)malloc(.....); free(p); if(p!=null) //p!=null is true, p is not null although freed { free(p); //Note: checking doesnot prevent error here }
free()
vous ne pouvez pas modifier la valeur du pointeur. Pour ce faire, il faudraitfree(void **ptr)
ou une macro#define free(x) do { __real_free(x); x = NULL; } while(0)
de le faire.free()
, quels changements ont eu lieu après que nous avons appelé lefree()
, un peu perplexemalloc()
etfree()
(et amis). Fondamentalement, lorsque vous appelezmalloc()
le système des marques d'une partie de la mémoire comme "utilisé" et puis le donne à vous, et lorsque vous appelezfree()
il marque que la partie de la mémoire comme "non utilisé" et fait ce qu'il veut avec (comme le fait de donner à une autremalloc()
appel, ou (plus moderne) démarrer la mise à zéro pour la sécurité et de la rapidité decalloc()
). On ne change pas la pointeur lui-même, mais ça change le de données qui a été souligné (ou il peut, de toute façon, c'est pourquoi le pointeur ne doit pas être utilisé après avoir été libéré).free()
...je thnk qui sera plus pratiquefree(void *ptr)
pas modifier la valeur du pointeur est passé. Il peut changer le table des matières du pointeur, la les données stockées à cette adresse, mais pas le adresse ou valeur du pointeur. Qui aurait besoinfree(void **ptr)
(qui n'est apparemment pas autorisé par la norme) ou une macro (ce qui est permis et parfaitement portable, mais les gens n'aiment pas les macros). Aussi, C n'est pas une question de commodité, il s'agit de donner des programmeurs autant de contrôle qu'ils veulent. Si ils ne veulent pas la charge supplémentaire de réglage des pointeurs versNULL
, il ne devrait pas être imposé.free
" (avec des choses telles que "casting de la suite de fonctions d'allocation de mémoire" ou "irréfléchie à l'aide de noms de type avecsizeof
").L'idée derrière cela, est à l'arrêt accidentel de la réutilisation des libérés pointeur.
Cette (peut effectivement être important. Bien que vous libérer de la mémoire, une partie ultérieure du programme pourrait allouer quelque chose de nouveau qui arrive à la terre dans l'espace. Votre ancien pointeur serait maintenant pointer vers un valide partie de la mémoire. Il est alors possible que quelqu'un serait d'utiliser le pointeur, résultant dans invalide programme de l'état.
Si vous NULL le pointeur, alors toute tentative d'utilisation il va de déréférencement 0x0 et de planter là, qui est facile à déboguer. Aléatoire des pointeurs pointant vers la mémoire vive est difficile à déboguer. Il n'est évidemment pas nécessaire, mais ensuite c'est pourquoi il est dans un document sur les pratiques exemplaires.
Du C ANSI standard:
"le comportement non défini" est presque toujours un plantage du programme. Afin d'éviter cela, il est sûr pour réinitialiser le pointeur à NULL. free() lui-même ne peut pas faire ce qu'il est adopté seulement un pointeur, pas un pointeur vers un pointeur. Vous pouvez également écrire un endroit plus sûr de la version de free() que les valeurs Null le pointeur:
ptr=NULL
, ou*ptr=NULL
????NULL
pour éviter le masquage des erreurs. stackoverflow.com/questions/1025589/... Il semble que dans les deux cas, certaines erreurs se cachent.Je trouve ce peu d'aide que dans mon expérience lors de l'accès à un libéré de l'allocation de mémoire c'est presque toujours parce qu'ils ont un autre pointeur vers quelque part. Et puis il est en conflit avec un autre personnel de codage standard qui est "Éviter l'encombrement inutile", donc je ne le fais pas car je pense qu'il a rarement de l'aide et rend le code un peu moins lisible.
Cependant - je ne vais pas définir la variable à null si le pointeur n'est pas censé être utilisé à nouveau, mais, souvent, le niveau plus élevé de la conception donne-moi une raison de le mettre à null de toute façon. Par exemple, si le pointeur est un membre d'une classe et j'ai supprimé ce qu'il désigne alors le "contrat" si vous voulez, de la classe, c'est que le membre aura point à quelque chose de valable à tout moment, de sorte qu'il doit être mis à null pour cette raison. Une petite distinction, mais je pense important.
En c++, il est important de toujours penser à qui possède ces données lorsque vous allouez de la mémoire (à moins d'utiliser des pointeurs intelligents, mais même alors, certains de la pensée est nécessaire). Et ce processus a tendance à conduire à des pointeurs généralement un membre de la classe et, en général, vous voulez une classe d'être dans un état valide en tout temps, et de la façon la plus simple de le faire est de définir la variable membre à NULL pour indiquer qu'il dirige à rien maintenant.
Un modèle commun est de mettre tous les membres de pointeurs à NULL dans le constructeur et ont le destructeur appeler delete sur un pointeur vers les données de votre design dit que la classe possède. Clairement, dans ce cas, vous devez définir le pointeur à NULL lorsque vous supprimez quelque chose pour indiquer que vous ne possédez pas toutes les données avant de.
Donc pour résumer, oui, j'ai souvent le pointeur à NULL après la suppression de quelque chose, mais c'est dans le cadre de la conception et de réflexions sur qui est le propriétaire des données plutôt qu'à l'aveuglette à la suite d'une norme de codage de la règle. Je ne voudrais pas le faire dans votre exemple, comme je le pense il n'y a aucun avantage à le faire et il ajoute "fouillis", qui dans mon expérience est tout aussi responsable pour les bugs et les mauvais code que ce genre de chose.
Récemment, j'ai trouver la même question après j'ai été à la recherche pour la réponse. Je suis arrivé à cette conclusion:
Il est de bonne pratique, et l'on doit suivre ce pour le rendre portable sur tous les (embedded systems).
free()
est une fonction de la bibliothèque, qui varie comme l'un des changements de la plate-forme, de sorte que vous ne devrait pas s'attendre à ce que après le passage du pointeur de cette fonction et après la libération de la mémoire, ce pointeur sera la valeur NULL. Cela peut ne pas être le cas pour certains de la bibliothèque mise en œuvre de la plate-forme.donc toujours aller pour
Cette règle est utile lorsque vous êtes en essayant d'éviter les scénarios suivants:
1) Vous avez une très longue fonction compliquée avec de la logique et de la gestion de la mémoire et vous ne voulez pas accidentellement réutiliser le pointeur à supprimé de la mémoire plus tard en la fonction.
2) Le pointeur est une variable membre d'une classe qui a assez complexe comportement, et vous ne voulez pas accidentellement réutiliser le pointeur à supprimé de la mémoire dans d'autres fonctions.
Dans votre scénario, il ne fait pas beaucoup de sens, mais si la fonction a été plus longtemps, il pourrait la matière.
Vous pouvez faire valoir que le paramètre NULL peut effectivement masque les erreurs de logique, plus tard, ou dans le cas où vous supposez que c'est valide, vous pouvez toujours bloquer sur la valeur NULLE, et il n'a pas d'importance.
En général, je vous conseille de le mettre à NULL quand vous pensez que c'est une bonne idée, et pas la peine quand vous pensez qu'il n'est pas la peine. Plutôt se concentrer sur l'écriture de courts fonctions et classes.
À ajouter à ce que d'autres ont dit, une bonne méthode d'utilisation du pointeur est toujours vérifier si il est un pointeur valide ou pas. Quelque chose comme:
Explicitement marquant le pointeur à NULL après la libération il permet pour ce type d'utilisation en C/C++.
Ce serait peut-être plus un argument pour initialiser les pointeurs à NULL, mais quelque chose comme cela peut être un très sournois bug:
p
se termine à la même place sur la pile de l'anciennPtr
, de sorte qu'il pourrait encore contenir un semblant de pointeur valide. En assignant à la*p
peut écraser toutes sortes de choses sans rapport entre elles et conduisent à laide de bugs. Surtout si le compilateur initialise des variables locales à zéro en mode debug, mais n'a pas une fois les optimisations sont activés. Si les versions de débogage de ne pas montrer de signes de l'un bug lors de l'release coup au hasard...Définir le pointeur qui vient d'être libéré à la valeur NULL n'est pas obligatoire, mais une bonne pratique. De cette façon , vous pouvez éviter 1) à l'aide d'un libéré pointu 2)gratuit il towice
Paramètres d'un pointeur NULL est de protéger contre un double-gratuit - une situation lors de la free() est appelé plus d'une fois pour la même adresse sans la réaffectation de la bloquer à cette adresse.
Double-libre conduit à un comportement indéterminé - généralement de corruption de segment ou immédiatement le plantage du programme. Appeler free() pour un pointeur NULL ne fait rien et est donc la garantie d'être en sécurité.
Donc, la meilleure pratique à moins de vous assurer que le pointeur quitte portée immédiatement ou très rapidement après free() est de mettre le pointeur à NULL de sorte que même si free() est appelée de nouveau, il s'appelle désormais pour un pointeur NULL et undefined comportement est éludé.
L'idée est que si vous essayez de déréférencer les non-plus-pointeur valide après la libération, vous voulez échouer dur (erreur de segmentation) plutôt que le silence et mystérieusement.
Mais... attention. Pas tous les systèmes de provoquer une erreur de segmentation si vous déréférencer NULL. Sur certaines versions de l') AIX, *(int *)0 == 0, et Solaris a option de compatibilité avec cette AIX "fonctionnalité".
À la question initiale:
Paramètre le pointeur à NULL directement après la libération du contenu est une perte de temps totale, à condition que le code répond à toutes les exigences, est entièrement de débogage et ne sera jamais à nouveau modifié. D'autre part, sur la défensive Micropression un pointeur qui a été libérée peut être très utile quand quelqu'un sans réfléchir ajoute un nouveau bloc de code en dessous de la free(), lors de la conception du module d'origine n'est pas correct, et dans le cas de l'il-compile-mais-ne-fais-ce-que-je-veux bugs.
Dans tout système, il est impossible d'obtenir l'objectif de le rendre plus facile pour la bonne chose, et l'irréductible coût des mesures inexactes. En C, nous te proposons un ensemble de très forte, très forte, outils, ce qui peut créer beaucoup de choses dans les mains d'un travailleur qualifié, et d'infliger toutes sortes de métaphorique de blessures lorsqu'il est manipulé correctement. Certains sont difficiles à comprendre ou à utiliser correctement. Et les gens, naturellement enclins à prendre des risques, faire des choses irrationnelles comme la vérification d'un pointeur NULL, la valeur avant d'appeler gratuitement avec elle...
La mesure du problème, c'est que chaque fois que vous essayez de diviser le bon du moins bon, le plus complexe, le cas, le plus probable que vous obtenez ambigu dans la mesure. Si le but est de ne garder que les bonnes pratiques, puis un peu ambigu ceux sont mis de côté avec la réalité, pas bon. SI votre objectif est d'éliminer le pas, les ambiguïtés peuvent rester avec la bonne. Les deux objectifs, de ne garder que de bons ou d'éliminer clairement mauvais, semblent diamétralement opposés, mais il est généralement un troisième groupe qui n'est ni l'un ni l'autre, l'un des deux.
Avant de faire une affaire avec le service qualité, essayez de regarder à travers le bug de la base de données pour voir comment souvent, voire jamais, de pointeur non valide valeurs causé des problèmes qui ont dû être écrit vers le bas. Si vous voulez faire vraiment la différence, d'identifier le problème le plus commun dans votre code de production et de proposer des trois façons de le prévenir
Il y a deux raisons:
À éviter les collisions lors de la double libération de la
Écrit par RageZ dans un double question.
Éviter d'utiliser déjà libéré des pointeurs
Écrit par Martin v. Löwis dans un une autre réponse.
Que vous avez une assurance de la qualité de l'équipe en place, permettez-moi d'ajouter un point mineur sur l'AQ. Certains qualité automatisés outils pour C drapeau affectations visant à libérer les pointeurs comme "inutile d'affectation à
ptr
". Par exemple PC-lint/FlexeLint de Gimpel Logiciel dittst.c 8 Warning 438: Last value assigned to variable 'nPtr' (defined at line 5) not used
Il existe des moyens pour supprimer sélectivement des messages, de sorte que vous pouvez toujours satisfaire à la fois les exigences d'assurance qualité, votre équipe doit décider de le faire.
Il est toujours conseillé de déclarer un pointeur de variable avec NULL comme,
Disons, ptr vers 0x1000 adresse mémoire.
Après l'utilisation de
free(ptr)
, il est toujours préférable d'annuler le pointeur de variable en déclarant de nouveau à NULL.par exemple:
Si pas re-déclaré à NULL, le pointeur de variable tient encore à souligner à la même adresse (0x1000), ce pointeur est une variable appelée balançant pointeur.
Si vous définissez un autre pointeur de variable (disons, q) et allocation dynamique d'adresse pour le nouveau pointeur, il ya une chance de prendre la même adresse (0x1000) par de nouvelles pointeur de variable. Si dans le cas, vous utilisez le même pointeur (ptr) et mise à jour de la valeur à l'adresse pointée par le pointeur de même (ptr), puis le programme va finir l'écriture d'une valeur à l'endroit où q pointe (depuis p et q pointent vers la même adresse (0x1000)).
par exemple
Longue histoire courte: Vous ne voulez pas accidentellement (par erreur) accéder à l'adresse que vous avez libéré. Parce que, quand vous libérer de l'adresse, vous permettez à cette adresse dans le segment de mémoire à allouer à une autre application.
Toutefois, si vous ne définissez pas le pointeur à NULL, et, par erreur, essayez de référence le pointeur, ou de modifier la valeur de cette adresse, VOUS POUVEZ TOUJOURS le FAIRE. MAIS PAS QUELQUE CHOSE QUE VOUS DEVRIEZ LOGIQUEMENT VOULEZ FAIRE.
Pourquoi ne puis-je toujours accéder à l'emplacement de mémoire que j'ai libéré? Parce que: Vous devrez libérer la mémoire, mais le pointeur de variable avait encore des informations sur le tas adresse mémoire. Donc, comme une stratégie défensive, veuillez le régler à la valeur NULL.