La norme C n'exige pas que les pointeurs nuls soient à l'adresse zéro de la machine. CEPENDANT, le casting d'un 0
à une valeur de type pointeur doit donner lieu à une valeur de type NULL
(§6.3.2.3/3), et l'évaluation du pointeur nul en tant que booléen doit être fausse. Cela peut être un peu gênant si l'on souhaite vraiment faire veulent une adresse zéro, et NULL
n'est pas l'adresse zéro.
Néanmoins, en apportant des modifications (lourdes) au compilateur et à la bibliothèque standard, il n'est pas impossible de disposer de NULL
être représenté par une autre configuration binaire tout en restant strictement conforme à la bibliothèque standard. Il est pas Il suffit de modifier la définition de NULL
cependant, comme à l'époque NULL
serait évalué à true.
Plus précisément, vous devrez :
- Faites en sorte que les zéros littéraux dans les affectations aux pointeurs (ou les casts vers les pointeurs) soient convertis en une autre valeur magique telle que
-1
.
- Prévoir des tests d'égalité entre les pointeurs et un entier constant
0
pour vérifier la valeur magique à la place (§6.5.9/6)
- Faire en sorte que tous les contextes dans lesquels un type de pointeur est évalué comme un booléen vérifient l'égalité avec la valeur magique au lieu de vérifier la présence de zéro. Cela découle de la sémantique des tests d'égalité, mais le compilateur peut l'implémenter différemment en interne. Voir §6.5.13/3, §6.5.14/3, §6.5.15/4, §6.5.3.3/5, §6.8.4.1/2, §6.8.5/4.
- Comme l'a souligné Caf, il faut mettre à jour la sémantique de l'initialisation des objets statiques (§6.7.8/10) et des initialisateurs composés partiels (§6.7.8/21) pour refléter la nouvelle représentation des pointeurs nuls.
- Créez un autre moyen d'accéder à la véritable adresse zéro.
Il y a certaines choses que vous faites pas doivent gérer. Par exemple :
int x = 0;
void *p = (void*)x;
Après ça, p
n'est PAS garanti comme étant un pointeur nul. Seules les affectations constantes doivent être gérées (c'est une bonne approche pour accéder à la véritable adresse zéro). De même :
int x = 0;
assert(x == (void*)0); // CAN BE FALSE
Aussi :
void *p = NULL;
int x = (int)p;
x
n'est pas garanti comme étant 0
.
En bref, cette condition a apparemment été prise en compte par le comité du langage C, et des considérations ont été faites pour ceux qui choisiraient une représentation alternative pour NULL. Il ne vous reste plus qu'à apporter des modifications majeures à votre compilateur, et voilà, vous avez terminé :)
Accessoirement, il peut être possible d'implémenter ces changements avec une étape de transformation du code source avant le compilateur proprement dit. C'est-à-dire qu'au lieu du flux normal de préprocesseur -> compilateur -> assembleur -> éditeur de liens, vous ajouteriez un préprocesseur -> transformation NULL -> compilateur -> assembleur -> éditeur de liens. Alors vous pourriez faire des transformations comme :
p = 0;
if (p) { ... }
/* becomes */
p = (void*)-1;
if ((void*)(p) != (void*)(-1)) { ... }
Cela nécessiterait un analyseur syntaxique C complet, ainsi qu'un analyseur syntaxique de type et une analyse des typedefs et des déclarations de variables pour déterminer quels identifiants correspondent aux pointeurs. Cependant, en faisant cela, vous pourriez éviter d'avoir à apporter des modifications aux portions de génération de code du compilateur proprement dit. clang peut s'avérer utile pour mettre en œuvre cette démarche - je crois savoir qu'il a été conçu avec des transformations de ce type à l'esprit. Bien entendu, vous devrez également apporter des modifications à la bibliothèque standard.