[gull] Subtilité de C n° 1 : NULL et 0

Marc Mongenet marc at mongenet.ch
Wed Mar 19 17:09:50 CET 2008


Bonjour, j'écris cette modeste présentation d'une subtilité
du langage C suite à la lecture de l'article "Who maintains
dpkg?" de LWN.net (Linux Weekly News) :
http://lwn.net/Articles/273714/.

Note : cet article est réservé pour encore quelques jours
aux abonnés de LWN.net. Je profite d'un abonnement
offert par le GULL. Un article de 2004, "NULL v. zero",
aborde aussi le sujet des pointeurs nuls en C et est
publiquement accessible : http://lwn.net/Articles/93574/.

Dans le débat enflammé autour de la maintenance de dpkg
("Re: dpkg semi-hijack - an announcement (also, triggers)"
http://article.gmane.org/gmane.linux.debian.devel.general/125926)
il y a entre autres une dispute technique autour du diff
suivant :
http://git.debian.org/?p=dpkg/dpkg.git;a=commit;h=4e5846ccd3dcc33504aba8ef35a8962bccfd562e
Use NULL instead of '(char *)0'.

On y trouve des modifications ressemblant à cela :
-      execlp(TAR,"tar",buffer,"-",(char*)0);
+      execlp(TAR, "tar", buffer, "-", NULL);


Que peut-on en dire ?

La fonction execlp a le prototype suivant :
int execlp(const char *file, const char *arg, ...);
Il s'agit d'une fonction à nombre variable de paramètres.
Les paramètres optionnels sont traités comme étant de
type const char* d'après mon man sur Linux.

D'après le standard C, la macro NULL doit être définie en
une "null pointer constant" et "integer constant expression
with the value 0, or such an expression cast to type void *,
is called a null pointer constant". Bref, il est généralement
admis que NULL peut être définie en 0 ou en ((void*)0).

Or sizeof 0 peut être différent de sizeof((void*)0).
Sur Motorola 68000 et certains modes de fonctionnement
des i386, on pouvait notamment avoir sizeof 0==2 et
sizeof((void*)0)==4 (entiers 16 bits, pointeurs 32 bits).
Avec la transition actuelle au 64 bits, on peut avoir
sizeof 0==4 et sizeof((void*)0)==8.
Bref, en écrivant NULL au lieu de (char*)0, on risque de
passer une donnée plus petite qu'attendue à la fonction,
et causer un crash. Cela dit, on peut argumenter
qu'aucune bibliothèque C serait assez perverse pour
définir NULL en 0 alors que sizeof 0!=sizeof((void*)0).

Cependant, même avec NULL définit en ((void*)0), il
reste la question de la compatibilité avec ((char*)0).
En effet, le standard C n'oblige pas tous les pointeurs
à avoir la même taille. Cependant, le standard C prend
la peine de préciser que les pointeurs sur void et les
pointeur sur un type caractère doivent être
interchangeables. Ouf.

Pour conclure, ma page man execlp en anglais précise :
The list of arguments must be terminated by a NULL pointer,
and, since these are variadic functions, this pointer must be
cast (char *) NULL.
Dans la documentation de l'Open Group on trouve (char*)0 :
http://www.opengroup.org/onlinepubs/007908799/xsh/exec.html

Cette subtilité semble avoir échappé au traducteur français (à
moins que la traduction soit basée sur une ancienne version
anglaise incomplète) :
Le tableau de pointeur doit se terminer par un pointeur NULL.


Marc Mongenet



More information about the gull mailing list