• [^] # Re: Petites precisions

    Posté par . En réponse au journal J'adore Linus. Évalué à 10.

    AAARRRRGGGGHHHH !!!!
    Et dire que j'avais pensé sortir de ce troll collant depuis que je ne fréquantais plus les newsgroup de prog.

    Pour faire simple et rapide :
    Il existe un tas d'architecture pour laquelle l'adresse 0 est tout a fait valable. Et parmis ces architecture on trouve notre bonne vieille x86. De facon assez simple :
    - Toutes les machines capables de gérer nativement la mémoire en mode segment ou en mode page ont besoin de l'adresse 0 (violament même).
    - Pas mal de machines utilisent l'adresse 0 comme reset (Zilog et certains PICS si je me souviens bien + truc plus exotique a coté).
    - Dans le cas ou l'adresse 0 est un pointeur vers une fonction hardcodé (reset, init, firmware load etc..) alors
    a) cette fonction devrait être de type void (on voit mal a quoi une fonction d'init pourrait renvoyer des infos)
    b) elle pointe vers 0

    Sur toutes ces architectures, normalement aussi bien (void *)0 que 0 sont des pointeurs on ne peut plus valide.
    Donc difficile de faire
    #define NULL (void *)0
    ou
    #define NULL 0

    Si sizeof(int) != sizeof(void *) (typiquement le cas sur machine 64 bits), des problèmes sont à prévoir.

    Le problème c'est que de par le type (bon le non type) de void * on peut avoir sur certaines architectures :
    sizeof(void *) != sizeof(void *), ceux qui ont déjà joué un peu avec des jump far et des jump near en assembleur x86 (ou autre) voient de quoi je parle.
    La taille des pointeurs (et ce jusqu'au niveau des registres qui les stoquent) peut varier en fonction du contexte CPU. Et là c'est la fête au village quelque soit la définition de int ou de void ou même de long...

    Bon maintenant, on en est au moment ou on se rend compte que si on veut un truc qui marche sur toutes les archis, on ne peut pas définir NULL dans le code.
    A partir de là , il est assez facile de déduire que c'est le boulot du preprocesseur/precompileur de faire le tri la dedans.
    Par exemple si l'adresse 0 est valide c'est a lui de repérer les appels de type *p = NULL ou *p = 0 et de les remplacer par l'adresse non valide de son choix. Dans le cas de contextes ou les types ne font pas la même longueur c'est au dev de permettre au préprocesseur de faire la différence :
    - soit en créant des signatures de fonctions qui ne laissent pas la place au doute
    genre si toto(int i, typea j, typeb k) existe éviter de faire une autre fonction toto(void* i, typea j, typeb k), mais faire : toto(typea j, typeb k, void* j)
    Ou alors s'arranger pour que le type soit forcément défini quand il arrive dans la fonction, genre test if i!=0 then toto(i,j,k).

    Grosso modo l'erreur de prog ne vient pas quand on fait *p=0 mais quand on balade des pointeurs dont on ne connait pas le type et dont on ne sait pas si ils sont nuls ou pas dans un contexte tel que le préprocesseur ne peut pas faire la différence.

    A noter que les chances de se planter sont souvent infimes avec NULL et plutôt élevée avec 0, sauf si vous vous amusez a redéfinir NULL. Auquel cas a vous de vous débrouiller avec votre préprocesseur pour ne pas qu'il s'enmèle les pinceaux.


    Kha