En C (ou en C++ d'ailleurs), les pointeurs sont spéciaux s'ils ont la valeur zéro : On me conseille de mettre les pointeurs à zéro après avoir libéré leur mémoire, parce que cela signifie que libérer à nouveau le pointeur n'est pas dangereux ; quand j'appelle malloc, il renvoie un pointeur avec la valeur zéro s'il ne peut pas me procurer de mémoire ; j'utilise if (p != 0)
tout le temps pour s'assurer que les pointeurs passés sont valides, etc.
Mais puisque l'adressage de la mémoire commence à 0, 0 n'est-il pas une adresse aussi valide que n'importe quelle autre ? Comment peut-on utiliser 0 pour gérer les pointeurs nuls si c'est le cas ? Pourquoi un nombre négatif n'est-il pas plutôt nul ?
Editar:
Un tas de bonnes réponses. Je vais résumer ce qui a été dit dans les réponses exprimées comme mon propre esprit l'interprète et j'espère que la communauté me corrigera si j'ai mal compris.
-
Comme tout ce qui concerne la programmation, c'est une abstraction. Juste une constante, pas vraiment liée à l'adresse 0. C++0x met l'accent sur ce point en ajoutant le mot-clé
nullptr
. -
Ce n'est même pas une abstraction d'adresse, c'est la constante spécifiée par la norme C et le compilateur peut la traduire en un autre nombre tant qu'il s'assure qu'elle n'est jamais égale à une adresse "réelle", et égale d'autres pointeurs nuls si 0 n'est pas la meilleure valeur à utiliser pour la plate-forme.
-
Dans le cas où il ne s'agit pas d'une abstraction, ce qui était le cas dans les premiers temps, l'adresse 0 est utilisée par le système et est hors limites pour le programmeur.
-
Ma suggestion de chiffre négatif était un brainstorming un peu sauvage, je l'admets. L'utilisation d'un entier signé pour les adresses est un peu un gaspillage si cela signifie qu'à part le pointeur nul (-1 ou autre), l'espace de valeur est divisé de manière égale entre les entiers positifs qui font des adresses valides et les nombres négatifs qui sont juste gaspillés.
-
Si un nombre est toujours représentable par un type de données, c'est 0. (Probablement que 1 l'est aussi. Je pense à l'entier d'un bit qui serait 0 ou 1 s'il n'est pas signé, ou juste le bit signé s'il est signé, ou l'entier de deux bits qui serait [-2, 1]. Mais alors vous pourriez simplement opter pour 0 étant nul et 1 étant le seul octet accessible en mémoire).
Il y a encore quelque chose qui n'est pas résolu dans mon esprit. La question de Stack Overflow Pointeur vers une adresse fixe spécifique me dit que même si 0 pour le pointeur nul est une abstraction, les autres valeurs de pointeur ne le sont pas nécessairement. Cela m'amène à poster une autre question sur Stack Overflow, Est-ce que je pourrais un jour vouloir accéder à l'adresse zéro ? .
11 votes
Vous pourriez tout aussi bien changer
if (p != 0)
aif (p)
qui est un idiome courant en C et C++, même si vous devrez perdre cette habitude si vous vous lancez dans Java.14 votes
Si vous supprimez quelque chose deux fois, cela signifie que votre code est erroné. Je déconseille de mettre les pointeurs à null après pour que vous puissiez vous planter et corriger le problème, pas le supprimer. Dans tous les cas, vous faites l'erreur de supposer qu'une adresse est un entier quelconque. Ce n'est pas nécessairement vrai, et 0 simplement représente une valeur de pointeur réelle qui est spécifique à l'implémentation. Une "adresse négative" n'a pas de sens, conceptuellement.
6 votes
@GMan : C'est peut-être même une bonne idée de mettre le pointeur à une adresse qui forcera un crash, comme par exemple
0xDEADBEEF
.5 votes
La question qui ne mourra jamais !
5 votes
Ne suivez pas les conseils de GMan. Suppression multiple mai indique une question théorique mais ne causera jamais de problème. Le déréférencement/la suppression d'un pointeur invalide, en revanche, est toujours un problème, MAIS il n'entraîne pas nécessairement un crash. Il existe de très nombreux cas dans lesquels vous pouvez déréférencer un pointeur non valide et cela entraîne simplement une folie totale et peut-être un plantage. plus tard . La seule fois où vous pouvez réellement parier (et pas par standard) que vous obtiendrez un crash est si vous accédez à l'extérieur de votre espace de programme, ce que null fera certainement. UB peut causer des heures de débogage supplémentaires.
8 votes
@Noah : Le point est set to null -> cacher les erreurs de programmation, ne pas set to null -> trouver les erreurs de programmation. Je ne sais pas pour vous, mais j'aimerais que mon code soit correct.
1 votes
Les adresses sont non signées, une valeur négative peut simplement représenter une valeur très élevée dans la mémoire, selon la façon dont le système d'exploitation mappe la mémoire virtuelle.
1 votes
@Robert : Puisque cette question se situe au pays des langues, je précise que les adresses ne sont pas nécessairement des entiers non signés.
0 votes
Comme les pointeurs non-char* sont pratiquement toujours alignés (souvent sur une frontière de 8 octets de nos jours), il peut être avantageux de remplir la mémoire désallouée avec des octets inutiles dont le LSB est défini. J'utilise 0x61 mais c'est arbitraire. (L'avantage ? Cela vous donne une erreur/signal due à un accès non aligné lorsque vous le déréférencez).
5 votes
@gman - vous ne rendrez pas votre code correct en invoquant des démons nasaux. Supprimer deux fois des pointeurs n'est jamais un code incorrect. Il peut s'agir ou non d'un problème de conception, mais c'est un code incorrect. jamais une erreur.
4 votes
@Noah Roberts : D'après la norme C, libérer un pointeur deux fois entraîne un comportement indéfini. Je ne sais pas pour vous, mais j'essaie d'éviter cela dans mes programmes.
1 votes
@Daniel - oui, bien sûr j'ai négligé de réaffirmer que ce n'est le cas que lorsque le pointeur a été mis à 0 puisque libérer un ptr à 0 est un nop. Je ne l'ai pas jugé nécessaire puisque la position contre laquelle je me bats est que le fait de ne pas mettre un pointeur à null rend en quelque sorte plus facile de trouver des erreurs de code causées par la suppression d'un pointeur deux fois.
0 votes
@Noah : N'êtes-vous pas d'accord que supprimer deux fois un pointeur est parfois une erreur de logique ?
0 votes
Le fait que j'aie mentionné le conseil de mettre le pointeur à zéro après la suppression a suscité une vive discussion. Je n'ai pas vraiment de position personnelle, et je ne me fie certainement pas à l'une ou l'autre de ces pratiques. Pour les intéressés, cette discussion a sa page ici : stackoverflow.com/questions/1931126/
0 votes
Gman a raison. Le but du langage C est d'éviter la surcharge de la vérification des contraintes à l'exécution, sinon vous coderiez dans un langage sain comme Java. Il n'y a que quelques rares cas où vous libérez deux fois quelque chose, d'autant plus qu'il doit d'abord être NULL
1 votes
@Longpoke - êtes-vous sûr de vouloir dire que vous êtes d'accord avec gman->ne pas mettre un pointeur à null après l'avoir supprimé parce que vous voulez planter si vous le libérez à nouveau par accident.... Votre formulation semble différente.
1 votes
@dennis - définissez "erreur logique". Il se peut parfois qu'un pointeur nul soit supprimé parce qu'il se passe quelque chose d'autre qui ne devrait pas l'être. Si cela s'expose comme un bug dans le programme, alors c'est une erreur. Si la seule chose qui va "mal" est qu'un pointeur nul est passé à free() ou delete alors je pense que vous devez repenser votre définition d'"erreur". De plus, l'alternative proposée par gman ne garantit rien. La suppression d'un pointeur qui n'est pas valide est un UB, pas un "crash". Le plus souvent, il se manifestera de manière totalement insensée.
0 votes
@Noah Roberts, je suis d'accord avec gman pour dire qu'il ne faut pas mettre le pointeur à tout ce qui est pour le marquer comme invalide, sauf si c'est votre drapeau "vivant"... Ie : Si vous avez une structure avec 3 pointeurs, et un drapeau disant si la structure est vivante, vous ne mettez que ce drapeau et laissez les pointeurs tels quels après les avoir libérés.
0 votes
@Noah : Si vous passez un pointeur à une fonction qui croit que le pointeur est toujours vivant, et qu'il ne l'est pas, alors c'est une erreur logique. Même lorsqu'elles ne provoquent pas directement des crashs, les éviter n'est pas sans mérite. Cela tend à faciliter les modifications si le code fonctionne comme prévu plutôt que comme promis. Je ne dis pas qu'il ne faut pas mettre les pointeurs morts à null, mais dire que la double suppression n'est jamais une erreur est tout simplement faux.
0 votes
"l'adressage de la mémoire commence à 0" [citation nécessaire]
0 votes
Un autre détail délicat Un nombre entier signé d'un bit a deux valeurs distinctes : 0 et -1, représentés respectivement par 0 et 1 comme la valeur du bit unique :)
1 votes
Mettre un pointeur à null après l'avoir libéré peut cacher des erreurs de conception de double-free. Non En le mettant à null, on risque de cacher des erreurs de type "used-after-freed". Ces dernières sont beaucoup plus graves. Si un pointeur n'est plus valide, il faut alors veulent toute tentative de déréférencement entraîne un comportement bien défini au lieu d'une corruption de mémoire imprévisible. Ces bogues sont un enfer à traquer et peuvent constituer une faille de sécurité.