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

Marc Mongenet marc at mongenet.ch
Tue Apr 1 21:51:18 CEST 2008


Le 01/04/08, Daniel Cordey<dc at mjt.ch> a écrit :
> On Tuesday 01 April 2008, Marc Mongenet wrote:
>  > Le prototype de la fonction C malloc est :
>  > void *malloc(size_t size);
>
> Oui, c'est la definition POSIX/ANSI (...) !
>
>  > Il existe deux « écoles » concernant l'assignation du pointeur
>  > retourné par malloc.
>
>
> A mon avis... plus. Il existait deux ecoles, mais ANSI n'en tolere qu'une; a
>  prtir du moment ou l'on desire coller completement a ANSI et que l'on declare
>  toutes les signatures des fonctions.
>
>  > Celle qui prône la conversion explicite
>  > (cast) du pointeur retourné ; et celle qui prône sa conversion
>  > implicite. Soit :
>  >
>  > int *p;
>  > p = (int*)malloc(sizeof(int));   /* école explicite */
>  > p = malloc(sizeof *p);   /* école implicite */
>
>
> Ce sont deux choses totalement differentes.
>

Je t'assure que ce sont deux manières de faire valides et
respectant tout à fait les normes. On peut encore écrire :
p = (int*)malloc(sizeof *p);  /* style peu courant */
p = malloc(sizeof(int));  /* style très courant */

>  Je sais que SUN a propage
>  cette "vision" pendant longtemps et cela a induit passablement de fausses
>  idees chez bon nombre de developeurs ! Entre autre l'interpretation de **argv
>  & *argv[]
>

Ah oui, les relations entre les pointeurs et les tables sont une
inépuisable source de subtilités en C et j'écrirai surement des
messages à ce propos à l'occasion. Je ne connais en revanche
pas cette histoire avec SUN.

>
>  > Les deux solutions sont valides en C car les conversions
>  > d'un pointeur de donnée de et vers void* sont implicites
>  > (c'est un peu le but de l'existence de void*).
>
> Cela depend du compilateur (options), de l'architecture et il est tres
>  dangereux de se reposer la-dessus. En effet, suivant l'architecture, cette
>  affirmation est erronnee.

Il doit y avoir un malentendu. Par conversion implicite de
pointeur de donnée de et vers void*, je voulais dire que :
int i;
int *pi = &i;
void *p;
p = pi;  /* pas besoin de cast */
pi = p;  /* pas besoin de cast */
/* Ici, le standard garantit que pi==&i. */

>  Il existe des architecture qui requierent un
>  alignement stricte sur N mots (N >= 1). Ce qui fait qu'un pointeur de 'int'
>  ne peut etre utillise comme pointeur de 'long' ou de 'char'... si j'ecris :

Absolument.

>  int *pi;
>  char *pc:
>
>  pi = (int *)(0)
>  pc = (char *)(0)
>
>  pi++;
>  pc++;
>
>  Alors pi != pc !!!

Ce n'est pas une expression valide en C (pointeurs
incompatibles), mais au niveau machine, les pointeurs
sont effectivement différents du moment que nous
avons sizeof(char)!=sizeof(int).
Mais je ne vois pas bien le rapport mes exemples.

> On ne peut non plus effectuer d'operation sur un void *
> pusique l'on en connait pas le type.

Je suppose que tu veux dire qu'on ne peut pas déréférencer
un void* ni faire de l'arithmétique dessus (sans utiliser des
extensions non standards, par exemple de GCC) ? Les
autres opérations restent possibles (assignations, tests,
adresse de, taille de...) :
void *p, **pp;
p = 0;
pp = &p;
if (p || pp) {}
...

>  De plus, il est bien dit que sizeof ne doit pas etre utilise pour calculer le
>  nombre de byte d'une structure, justement pour eviter ce probleme
>  d'alignement des pointeurs lors de l'utilisation de malloc/calloc.

L'opérateur sizeof retourne le nombre de bytes de son opérande ;
c'est sa raison d'être. Appliqué à une structure, il retourne la taille
de la structure, incluant les éventuels padding à l'intérieur et à la
fin de la structure.

Le standard garantit que la fonction malloc retourne un pointeur
correctement aligné, ou un pointeur nul en cas d'erreur.

Le Committe Draft du standard C99 que j'ai sous les yeux
donne les exemples suivants :

A principal use of the sizeof operator is in communications with
routines such as storage allocators and I/O systems. A storage-
allocation function might accept a size (in bytes) of an object to
allocate and return a pointer to void. For example:

extern void *alloc(size_t);
double *dp = alloc(sizeof *dp);

The implementation of the alloc function should ensure that its
return value is aligned suitably for conversion to a pointer to
double.

>  > Si int et int* ont par chance la même taille, alors
>  > on ne se rendra probablement compte de rien.
>
>
> Oui, trompeur et trop de developeurs paresseux se reposent la-dessus.
>
>
>  > Mais s'ils
>  > n'ont pas la même taille, alors on obtiendra probablement
>  > un crash à l'exécution.
>
>
> Pas... brobablement... cartainement. C'est un bus-error...
>

Si par hasard la valeur corrompue reste dans l'espace
d'adressage du processus et conserve un alignement
valide, alors il n'y a pas de bus error. On lira ou écrira
plutôt une valeur à une mauvaise adresse. Un bus error
peut en découler par la suite, ou pas.

>
> C++ est... ANSI :-)
>

Il a même été directement ISO.

>
> gcc compile en mode ANSI par defaut.

Plus ou moins. Il accepte aussi des extensions GNU par
défaut. Il faut utiliser les paramètres -ansi si l'on ne veut
pas des extensions qui peuvent poser problème. Et le
paramètre -pedantic est censer rejetter tout ce qui n'est
pas strictement conforme à la norme.


-- 
Marc Mongenet
Creator of the Web 2 Markup Language
http://w2ml.com



More information about the gull mailing list