Re: [gull] Subtilité de C n° 2 : type de retour de malloc

Marc Mongenet marc at mongenet.ch
Thu Apr 3 16:33:32 CEST 2008


Le 02/04/08, Daniel Cordey<dc at mjt.ch> a écrit :
> On Wednesday 02 April 2008, Leopoldo Ghielmetti wrote:
>
>  > > p = (int*)malloc(sizeof *p);  /* style peu courant */
>  > > p = malloc(sizeof(int));  /* style très courant */
>  >
>  > Moi j'utilise ce que tu appelles le style peu courant et très couramment
>  > d'ailleurs. :-)
>
>
> Bravo !
>
>  Le "soit-disant" style peu courant devrait etre le standard de tout
>  developpeur. La place "reservee" par malloc(sizeof(* o)) ne sera pas la meme
>  avec un systeme 32 ou 64 bits. Alors que malloc(sizeof(int)) n'engendredra
>  pas de differences. Le pire etant que cela ne se voit pas immediatement.
>  Ecrire :
>
>          p = malloc(sizeof(int));
>
>  est tout simplement non-portable. Punkt !
>
>  Il est sans doute vrais que le "style tres courant" est le plus repandu, mais
>  cela est inquietant... quand on veut reserver une espace de memoire pour un
>  pointeur, on se doit de lui passer la taille necessaire a l'obtention de la
>  bonne valeur.

Ah, voilà l'origine de notre malentendu, comme Leo l'a bien relevé (merci !),
les appels à malloc ci-dessus servent à réserver de l'espace mémoire pour
un int, pas pour un pointeur.

J'aurais peut-être dû présenter les deux écoles ainsi :
p = (int*)malloc(sizeof(int));  /* école du cast explicite */
p = malloc(sizeof(int));  /* école du cast implicite */

Mais ce dernier style est vraiment criticable, car si p n'est pas
un pointeur de int, c'est buggué et le compilateur n'a pas moyen
de s'en rendre compte !
Tandis qu'avec la notation suivante, on est sûr de toujours
transmettre à malloc la taille de l'objet pointé par p :
p = malloc(sizeof *p);

Cela dit, le style de Leo me semble être le meilleur, car à la
fois clair et solide :
p = (int*)malloc(sizeof *p);

La seule faiblesse de ce style est de masquer un éventuel oubli de
l'inclusion de stdlib.h. Mais en cas d'oubli le compilateur peut
facilement émettre un avertissement comme quoi malloc est appelé
sans que son prototype ne soit connu. C'est donc une faiblesse facile
à détecter.

>  Il est donc incorrecte d'utiliser la reservation pour le
>  stockage d'un 'int', alors qu'il n'y a AUNCUNE garantie qu'un 'int' puisse
>  contenir un pointeur. C'est une "chance" que cela soitr vrais sur la plupart
>  des systemes ayant un processeur 32 bits.

Je suis tout à fait d'accord qu'il ne faut jamais compter sur le fait que
sizeof(int)==sizeof(int*). Je ne me permettrais jamais une hypothèse
aussi folle. ;-) Si vraiment on a besoin de convertir un pointeur en entier,
il faut le convertir en unsigned long, en espérant que
sizeof(void*)<=sizeof(unsigned long).

>  qu'en 64 bits, la discussion n'a pas lieu d'etre. Sur une machine 64 bits,
>  un 'int' est stocke dans 4 bytes, alors qu'un pointeur est stocke en 8
>  bytes...

Tout à fait, et lors de la transition du 16 au 32 bits, j'ai lu qu'on
rencontrait des int de 2 bytes avec des pointeurs de 4 bytes.
C'est pas tout à fait illogique, par exemple le Motorola 68000 avait
des pointeurs 32 bits mais calculait plus vite en 16 bits qu'en 32 bits
(4 cycles au lieu de 6 pour une addition si mes souvenirs sont exacts).

>
> La consequence en est que de tres nombreux developeurs continuent a penser que
>  **argv est equivalent a *argv[].

Ah ben je comptais écrire quelque-chose à ce sujet ;-), mais c'est une
très longue histoire qui remonte au premier compilateur C.

>  faut faire preuve de rigueur. Par exemple, ecrire des macros qui permettent
>  de calculer les alignements etc. Tel que :
>
>  #define RoundN(_v_,_b_) (((unsigned int)(_v_) + _b_) & ~(_b_))

Si j'ai bien compris ces macros, il faudrait un (unsigned long) ci-dessus
puisqu'on peut avoir sizeof(unsigned)<sizeof(void*). Le cast (unsigned int)
va tronquer le pointeur sur la plupart des CPU 64 bits.

>  #define Round16(_s_)    RoundN(_s_,0xf)
>  #define Round8(_s_)     RoundN(_s_,0x7)
>  ...
>  #define Addr16(_a_)     (void *)Round16(_a_)
>  ...
>  #define CRSIZEOF(_v_)   (Round16(sizeof(_v_)))
>
>  Ainsi, je peux m'affranchir de l'aproxinmation de sizeof() sur les structure.
>  Et non... sizeof ne calcul pas le padding a la fin de la structure... !

Ben si.

>
>  Inutile d'utiliser des "extension non-standards". Le standard me permet de
>  manipuler des pointeurs de tous types et d'effectuer des operations entre
>  eux. Seulement, effectuer une incremention (++) sur un pointeur de void n'est
>  pas la meme chose que sur un pointeur de double. Les calculs sur des
>  pointeurs doivent etre ramenes au 'type' du pointeur de l'architecture
>  (unsigned !).

Je ne suis pas sûr que le standard donne de telles garanties.
Pour être clair :

char *p1, *p2 = malloc(1);
unsigned long address = (unsigned long)p2;
p1 = (char*)address;
/* Le standard assure-t-il que p1==p2? Je n'en suis pas sûr. */

>
>  dc
>

Marc Mongenet



More information about the gull mailing list