133 votes

Pourquoi l'adresse zéro est-elle utilisée pour le pointeur nul ?

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) a if (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 .

73voto

Michael Burr Points 181287

2 points :

  • seule la valeur constante 0 dans le code source est le pointeur nul - l'implémentation du compilateur peut utiliser la valeur qu'elle veut ou dont elle a besoin dans le code en cours d'exécution. Certaines plates-formes ont une valeur de pointeur spéciale qui est "invalide" et que l'implémentation peut utiliser comme pointeur nul. La FAQ C pose une question, "Sérieusement, est-ce que des machines réelles ont vraiment utilisé des pointeurs nuls non nuls, ou des représentations différentes pour des pointeurs de types différents ?". qui met en évidence plusieurs plates-formes qui utilisent cette propriété de 0 étant le pointeur nul dans la source C tout en étant représenté différemment au moment de l'exécution. La norme C++ contient une note qui précise que la conversion "d'une expression constante intégrale ayant la valeur zéro produit toujours un pointeur nul, mais la conversion d'autres expressions qui ont la valeur zéro ne produit pas nécessairement un pointeur nul".

  • une valeur négative pourrait être tout aussi utilisable par la plate-forme qu'une adresse - la norme C a simplement dû choisir quelque chose à utiliser pour indiquer un pointeur nul, et zéro a été choisi. Honnêtement, je ne suis pas sûr que d'autres valeurs sentinelles aient été envisagées.

Les seules exigences pour un pointeur nul sont :

  • il est garanti que la comparaison ne sera pas égale à un pointeur vers un objet réel.
  • deux pointeurs nuls seront comparés de manière égale (le C++ affine ce point de manière à ce que cela ne soit nécessaire que pour les pointeurs du même type).

13 votes

+1 Je soupçonne que 0 a été choisi simplement pour des raisons historiques. (0 étant une adresse de départ et invalide, la plupart du temps.) Bien sûr, en général, une telle hypothèse n'est pas toujours vraie, mais 0 fonctionne plutôt bien.

8 votes

L'espace peut aussi avoir été un facteur déterminant. À l'époque où le C a été développé, la mémoire était BEAUCOUP plus coûteuse qu'aujourd'hui. Le nombre zéro peut être calculé de manière pratique en utilisant une instruction XOR ou sans avoir besoin de charger une valeur immédiate. En fonction de l'architecture, cela peut permettre de gagner de l'espace.

6 votes

@GMan - Vous avez raison. Sur les premiers processeurs, l'adresse mémoire zéro était spéciale et bénéficiait d'une protection matérielle contre l'accès par un logiciel en cours d'exécution (dans certains cas, il s'agissait du début du vecteur de réinitialisation, et sa modification pouvait empêcher la réinitialisation ou le démarrage du processeur). Les programmeurs utilisaient cette protection matérielle comme une forme de détection d'erreur dans leur logiciel, laissant la logique de décodage d'adresse du CPU vérifier les pointeurs non initialisés ou invalides au lieu de devoir dépenser des instructions du CPU pour le faire. Cette convention est toujours en vigueur aujourd'hui, même si l'objectif de l'adresse zéro a pu changer.

34voto

Aviad P. Points 9351

Historiquement, l'espace d'adressage commençant à 0 était toujours de la ROM, utilisée pour un système d'exploitation ou des routines de traitement des interruptions de bas niveau. De nos jours, comme tout est virtuel (y compris l'espace d'adressage), le système d'exploitation peut affecter n'importe quelle allocation à n'importe quelle adresse, il ne peut donc PAS spécifiquement affecter quoi que ce soit à l'adresse 0.

6 votes

C'est à peu près tout. C'est par convention historique, et les premières adresses étaient utilisées pour les gestionnaires d'interruptions, donc inutilisables pour les programmes normaux. De plus, 0 est "vide", ce qui peut être interprété comme aucune valeur / aucun pointeur.

0 votes

Il n'est pas vrai que l'adresse 0 est toujours une adresse non valide, mais je suppose que c'est la raison pour laquelle la norme permet l'utilisation de toute constante. Je ne sais toujours pas comment il est garanti que l'adresse NULL n'est pas une adresse valide, à moins que le nombre de bits utilisés pour stocker la valeur d'un pointeur soit supérieur au nombre de bits du bus d'adresse REM.

16voto

rmeador Points 15107

IIRC, la valeur du "pointeur nul" n'est pas garantie comme étant nulle. Le compilateur traduit 0 en n'importe quelle valeur "nulle" appropriée au système (qui, en pratique, est probablement toujours zéro, mais pas nécessairement). La même traduction est appliquée lorsque vous comparez un pointeur à zéro. Parce que vous ne pouvez comparer les pointeurs qu'entre eux et avec cette valeur spéciale 0, le programmeur ne sait rien de la représentation mémoire du système. Quant à savoir pourquoi ils ont choisi 0 au lieu de 42 ou quelque chose comme ça, je suppose que c'est parce que la plupart des programmeurs commencent à compter à 0 :) (De plus, sur la plupart des systèmes, 0 est la première adresse mémoire et ils voulaient que ce soit pratique, puisque dans la pratique, les traductions comme celles que je décris ont rarement lieu ; le langage ne fait que les permettre).

5 votes

@Justin : Vous avez mal compris. La constante 0 est toujours le pointeur nul. Ce que @meador veut dire, c'est qu'il est possible que le pointeur nul (indiqué par la constante 0) ne corresponde pas à l'adresse zéro. Sur certaines plateformes, la création d'un pointeur nul ( int* p = 0 ) pourrait créer un pointeur contenant la valeur 0xdeadbeef ou toute autre valeur qu'il préfère. 0 est un pointeur nul, mais un pointeur nul n'est pas nécessairement un pointeur à l'adresse zéro. :)

0 votes

Un pointeur NULL est une valeur réservée et, selon le compilateur, peut être n'importe quel modèle de bit. Un pointeur NULL ne signifie pas qu'il pointe vers l'adresse 0.

3 votes

Mais @Jalf, la constante 0 n'est pas toujours le pointeur nul. C'est ce que nous écrivons lorsque nous voulons que le compilateur remplisse le code de la plate-forme. réel pointeur nul pour nous. En pratique, le pointeur nul est généralement hace correspondent à l'adresse zéro, cependant, et j'interprète la question de Joel comme demandant pourquoi il en est ainsi. Il y a supposément un octet de mémoire valide à cette adresse, après tout, alors pourquoi ne pas utiliser une adresse inexistante d'un octet inexistant au lieu de supprimer un octet valide du jeu ? (J'écris ce que j'imagine que Joel pensait, et non une question que je me pose).

16voto

AndreyT Points 139512

Vous devez mal comprendre la signification de la constante zéro dans le contexte des pointeurs.

Ni en C ni en C++, les pointeurs ne peuvent "avoir la valeur zéro". Les pointeurs ne sont pas des objets arithmétiques. Ils ne peuvent pas avoir de valeurs numériques comme "zéro" ou "négatif" ou quoi que ce soit de cette nature. Votre affirmation selon laquelle "les pointeurs ... ont la valeur zéro" n'a donc aucun sens.

En C et C++, les pointeurs peuvent avoir la réserve valeur de null-pointer . La représentation réelle de la valeur du pointeur nul n'a rien à voir avec les "zéros". Elle peut être absolument tout ce qui est approprié pour une plateforme donnée. Il est vrai que sur la plupart des plates-formes, la valeur du pointeur nul est représentée physiquement par une valeur d'adresse zéro. Toutefois, si l'adresse 0 est utilisée dans un but précis sur une plate-forme donnée (par exemple, si vous devez créer des objets à l'adresse 0), la valeur du pointeur nul sur cette plate-forme sera très probablement différente. Elle pourrait être représentée physiquement par 0xFFFFFFFF ou comme valeur d'adresse 0xBAADBAAD valeur de l'adresse, par exemple.

Néanmoins, quelle que soit la façon dont la valeur du pointeur nul est représentée sur une plateforme donnée, dans votre code, vous continuerez à désigner les pointeurs nuls par la constante 0 . Pour attribuer une valeur de null-pointer à un pointeur donné, vous continuerez à utiliser des expressions telles que p = 0 . C'est la responsabilité du compilateur de réaliser ce que vous voulez et de le traduire dans la représentation correcte de la valeur du pointeur nul, c'est-à-dire de le traduire dans le code qui mettra la valeur de l'adresse de 0xFFFFFFFF dans le pointeur p par exemple.

En bref, le fait que vous utilisiez 0 dans votre code sorcier pour générer des valeurs de pointeur nul ne signifie pas que la valeur du pointeur nul est liée d'une manière ou d'une autre à l'adresse 0 . Le site 0 que vous utilisez dans votre code source n'est qu'un "sucre syntaxique" qui n'a absolument aucun rapport avec l'adresse physique réelle vers laquelle "pointe" la valeur du pointeur nul.

4 votes

<quote>Les pointeurs ne sont pas des objets arithmétiques</quote> L'arithmétique des pointeurs est assez bien définie en C et C++. Une partie de l'exigence est que les deux pointeurs pointent dans le même composite. Le pointeur nul ne pointe vers aucun composite et son utilisation dans des expressions arithmétiques de pointeurs est donc illégale. Par exemple, il n'est pas garanti que (p1 - nullptr) - (p2 - nullptr) == (p1 - p2) .

7 votes

@Ben Voigt : La spécification du langage définit la notion de type arithmétique . Tout ce que je dis, c'est que les types de pointeurs n'appartiennent pas à la catégorie des types arithmétiques. Arithmétique des pointeurs est une histoire différente et sans aucun rapport, une simple coïncidence linguistique.

1 votes

Comment quelqu'un peut-il lire objets arithmétiques censé savoir que cela signifie "au sens des types arithmétiques" et non "au sens des opérateurs arithmétiques" (dont plusieurs sont utilisables sur les pointeurs) ou "au sens de l'arithmétique des pointeurs". En ce qui concerne les coïncidences linguistiques, objet arithmétique a plus de lettres en commun avec arithmétique des pointeurs que types arithmétiques . En même temps, la norme parle de valeur du pointeur . L'affiche originale voulait probablement dire représentation entière d'un pointeur plutôt que valeur du pointeur y NULL explicitement ne doit pas être représenté par 0.

8voto

ChrisW Points 37322

Mais comme l'adressage de la mémoire commence à 0, 0 n'est-il pas une adresse aussi valable que n'importe quelle autre ?

Sur certains/nombreux/toutes les systèmes d'exploitation, l'adresse mémoire 0 est spéciale d'une certaine manière. Par exemple, elle est souvent mappée sur une mémoire invalide/non existante, ce qui provoque une exception si vous essayez d'y accéder.

Pourquoi un nombre négatif n'est-il pas plutôt nul ?

Je pense que les valeurs des pointeurs sont généralement traitées comme des nombres non signés : sinon, par exemple, un pointeur de 32 bits ne pourrait adresser que 2 Go de mémoire, au lieu de 4 Go.

4 votes

J'ai codé sur un dispositif où l'adresse zéro était une adresse valide, et il n'y avait pas de protection de la mémoire. Les pointeurs nuls étaient également tous bits-zéro ; si vous écriviez accidentellement dans un pointeur nul, vous faisiez sauter les paramètres du système d'exploitation qui se trouvaient à l'adresse zéro ; l'hilarité ne s'ensuivait généralement pas.

2 votes

Oui : sur un processeur x86 en mode non protégé, par exemple, l'adresse 0 est l'adresse de l'utilisateur. table de ve ve ve d'interruption .

1 votes

@ChrisW : Sur x86 en mode non protégé, l'adresse zéro en particulier est le vecteur d'interruption de division par zéro, que certains programmes peuvent avoir des raisons tout à fait légitimes d'écrire.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X