Twitter iPhone pliant OnePlus 11 PS5 Disney+ Orange Livebox Windows 11

cast de pointeur *, ** différence ?

114 réponses
Avatar
Nico
salut,

j'ai un petit code qui m'étonne... soit une fonction prenant un void*
en paramètre, lors de son appel, si je lui passe un double*, mon
compilateur dit rien... ok.
soit une fonction retournant un void *, lorsque j'assigne un double*
avec le retour de cette fonction, mon compilateur ne dit rien...

ici donc, pas besoin de caster ni le paramètre (en (void*)), ni le
retour de fonction (en (double*))...

maintenant, on prend les même et on recommence, à la différence près
que mon argument n'est plus un void* mais un void**, et mon retour
n'est plus un void* mais aussi un void **. là plus rien ne passe...
lors de l'appel de la fonction je suis obligé de caster mon double** en
void**, et lors du retour, je suis obligé de caster en double**,
pourquoi ?

pour un exemple plus parlant, considérez le code suivant :

void *
vect_new(size_t len, size_t size_type)
{
void *v = malloc(len * size_type);
return v;
}


void
vect_free(void *vect)
{
if(vect) free(vect);
}

/*declaration du vecteur...
pas besoin de faire un cast (double*)*/

double *vect = vect_new(3,sizeof(double));

/*appel sans probleme a la compilation :
pas besoin de faire (void*)vect*/
vect_free(vect);


et maintenant avec des void** :

void **
mat_new(unsigned int nrow, unsigned int ncol, size_t size_type)
{
int i,j;
void **m = malloc(nrow * size_type);
if(m)
{
for(i=0; i<nrow; i++)
{
m[i] = malloc(ncol * size_type);
if(!m[i])
{
for(j=0; j<=i; j++)
free(m[i]);
free(m);
m = NULL;
}
}
}
return m;
}

void
mat_free(void **m,unsigned int nrow)
{
int i;
if(m)
{
for(i=0; i<nrow; i++)
if(m[i])
free(m[i]);
free(m);
}
}

/*declaration de la matrice*/
double **matrice = (double**)mat_new(4,3,sizeof(double));

/*appel sans probleme a la compilation : */
mat_free((void**)matrice,4);

là si je cast pas l'un et l'autre... warning... où se situe la
différence avec le 1er exemple ?

merci de m'éclairer à ce propos.

--
Nico,
http://astrosurf.com/nicoastro
http://nicolas.aunai.free.fr

10 réponses

1 2 3 4 5
Avatar
Jean-Marc Bourguet
Nico writes:

salut,

j'ai un petit code qui m'étonne... soit une fonction prenant un void* en
paramètre, lors de son appel, si je lui passe un double*, mon compilateur
dit rien... ok.
soit une fonction retournant un void *, lorsque j'assigne un double* avec
le retour de cette fonction, mon compilateur ne dit rien...

ici donc, pas besoin de caster ni le paramètre (en (void*)), ni le retour
de fonction (en (double*))...

maintenant, on prend les même et on recommence, à la
différence près que mon argument n'est plus un void* mais
un void**, et mon retour n'est plus un void* mais aussi un
void **. là plus rien ne passe... lors de l'appel de la
fonction je suis obligé de caster mon double** en void**,
et lors du retour, je suis obligé de caster en double**,
pourquoi ?


void* est un cas particulier. Il faut un cast pour les
autres. Note que ça peut être dangereux dans les cas où
double* n'a pas la même taille que void*.

A+

--
Jean-Marc
FAQ de fclc: http://www.isty-info.uvsq.fr/~rumeau/fclc
Site de usenet-fr: http://www.usenet-fr.news.eu.org

Avatar
Nico
Jean-Marc Bourguet avait soumis l'idée :


void* est un cas particulier.


pourquoi ?

Il faut un cast pour les
autres. Note que ça peut être dangereux dans les cas où
double* n'a pas la même taille que void*.



c'est à dire ?
peut-on faire ce genre de fonction (allocation vecteur/matrice)
agréable sans cast ? j'ai fait ça pour éviter d'avoir une fonction par
type (vecteur de double, de int, de char etc...)

--
Nico,
http://astrosurf.com/nicoastro
http://nicolas.aunai.free.fr

Avatar
Jean-Marc Bourguet
Nico writes:

Jean-Marc Bourguet avait soumis l'idée :

void* est un cas particulier.


pourquoi ?


Parce que la norme le dit (Gabriel te dira que le comité a
voulu innover...)

Il faut un cast pour les autres. Note que ça peut être
dangereux dans les cas où double* n'a pas la même taille
que void*.


c'est à dire ?


Essaie d'imaginer ce qui se passe dans ton exemple si
sizeof(void*) != sizeof(double*).

peut-on faire ce genre de fonction (allocation
vecteur/matrice) agréable sans cast ?


En restant conforme non: tu supposes que void* a la même
taille que les autres pointeurs ce qui n'est pas garanti.

Si cette hypothèse ne te gène pas, retourne simplement un
void*, tu pourras l'assigner sans cast à un double**.

A+

--
Jean-Marc
FAQ de fclc: http://www.isty-info.uvsq.fr/~rumeau/fclc
Site de usenet-fr: http://www.usenet-fr.news.eu.org


Avatar
Emmanuel Delahaye
Nico wrote on 07/11/04 :
j'ai un petit code qui m'étonne... soit une fonction prenant un void* en
paramètre, lors de son appel, si je lui passe un double*, mon compilateur dit
rien... ok.


Ok.

soit une fonction retournant un void *, lorsque j'assigne un double* avec le
retour de cette fonction, mon compilateur ne dit rien...


Ok.

ici donc, pas besoin de caster ni le paramètre (en (void*)), ni le retour de
fonction (en (double*))...


Ok.

maintenant, on prend les même et on recommence, à la différence près que mon
argument n'est plus un void* mais un void**,


Rien à voir.

et mon retour n'est plus un
void* mais aussi un void **. là plus rien ne passe... lors de l'appel de la
fonction je suis obligé de caster mon double** en void**, et lors du retour,
je suis obligé de caster en double**, pourquoi ?


Parce que c'est comme ça. void * est le seul type qui soit compatible
avec tous les types pointeurs sur objet. C'est garanti par la norme.
void** n'est pas du tout void*.


pour un exemple plus parlant, considérez le code suivant :

void *
vect_new(size_t len, size_t size_type)
{
void *v = malloc(len * size_type);
return v;
}


Moui, mais je préfèrerais que le type soit un peu moins anonyme que
void *.

http://mapage.noos.fr/emdel/tad.htm

Mais tu cherches peut être à être très générique. Admettons...

void
vect_free(void *vect)
{
if(vect) free(vect);
}


Même remarque. De plus, je préfère être explicite :

if (vect != NULL)
{
free (vect);
}

mais free(NULL); est défini (ne fait rien). Le test n'est pas exigé.

/*declaration du vecteur...
pas besoin de faire un cast (double*)*/

double *vect = vect_new(3,sizeof(double));


ou tout simplement (facilite la maintenance)

double *vect = vect_new (3, sizeof *vect);

ne pas oublier la vérification...

if (vect != NULL)
{

/*appel sans probleme a la compilation :
pas besoin de faire (void*)vect*/
vect_free(vect);


Ok.

et maintenant avec des void** :


Pourquoi des void**? Le type void** n'est pas générique. Si on t'a dit
le contraire, c'est une erreur, et celui qui a dit ça mérite le goudron
et les plumes.

void **
mat_new(unsigned int nrow, unsigned int ncol, size_t size_type)
{
int i,j;
void **m = malloc(nrow * size_type);
if(m)
{
for(i=0; i<nrow; i++)
{
m[i] = malloc(ncol * size_type);
if(!m[i])
{
for(j=0; j<=i; j++)
free(m[i]);
free(m);
m = NULL;
}
}
}
return m;
}



Il suffit de laisser void* et c'est OK. C'est le pointeur qui va
recevoir l'adresse du bloc alloué qui fera la conversion implicite
gràce à son type.

void *
mat_new(unsigned int nrow, unsigned int ncol, size_t size_type)

double **pp = mat_new (3, 5, sizeof **pp)

par contre, pour que ça fonctionne, il faut allouer un tableau de
pointeurs et des tableaux pour chaque pointeurs, comme tu l'as fait.
Dans ce cas, la notation pp[lin][col] a un sens.

--
Emmanuel
The C-FAQ: http://www.eskimo.com/~scs/C-faq/faq.html
The C-library: http://www.dinkumware.com/refxc.html

"C is a sharp tool"

Avatar
Emmanuel Delahaye
Jean-Marc Bourguet wrote on 07/11/04 :

void* est un cas particulier. Il faut un cast pour les
autres. Note que ça peut être dangereux dans les cas où
double* n'a pas la même taille que void*.


C'est possible ça? Si c'est vrai, ça remet en cause beaucoup de code
qui compte là dessus...

--
Emmanuel
The C-FAQ: http://www.eskimo.com/~scs/C-faq/faq.html
The C-library: http://www.dinkumware.com/refxc.html

"C is a sharp tool"

Avatar
Horst Kraemer
Nico wrote:

salut,

j'ai un petit code qui m'étonne... soit une fonction prenant un void*
en paramètre, lors de son appel, si je lui passe un double*, mon
compilateur dit rien... ok.
soit une fonction retournant un void *, lorsque j'assigne un double*
avec le retour de cette fonction, mon compilateur ne dit rien...

ici donc, pas besoin de caster ni le paramètre (en (void*)), ni le
retour de fonction (en (double*))...

maintenant, on prend les même et on recommence, à la différence près
que mon argument n'est plus un void* mais un void**, et mon retour
n'est plus un void* mais aussi un void **. là plus rien ne passe...
lors de l'appel de la fonction je suis obligé de caster mon double** en
void**, et lors du retour, je suis obligé de caster en double**,
pourquoi ?

pour un exemple plus parlant, considérez le code suivant :

void *
vect_new(size_t len, size_t size_type)
{
void *v = malloc(len * size_type);
return v;
}


void
vect_free(void *vect)
{
if(vect) free(vect);
}

/*declaration du vecteur...
pas besoin de faire un cast (double*)*/

double *vect = vect_new(3,sizeof(double));

/*appel sans probleme a la compilation :
pas besoin de faire (void*)vect*/
vect_free(vect);


et maintenant avec des void** :

void **
mat_new(unsigned int nrow, unsigned int ncol, size_t size_type)
{
int i,j;
void **m = malloc(nrow * size_type);


C'est faux.

void **m = malloc(nrow * sizeof (void*));

ou bien

void **m = malloc(nrow * sizeof *m);


Cela ne fonctionne que par hasard parce que dans ton exemple size_type
est sizeof(double) et dans ton système sizeof(double)==sizeof(void*).


if(m)
{
for(i=0; i<nrow; i++)
{
m[i] = malloc(ncol * size_type);
if(!m[i])
{
for(j=0; j<=i; j++)
free(m[i]);
free(m);
m = NULL;
}
}
}
return m;
}

void
mat_free(void **m,unsigned int nrow)
{
int i;
if(m)
{
for(i=0; i<nrow; i++)
if(m[i])
free(m[i]);
free(m);
}
}

/*declaration de la matrice*/
double **matrice = (double**)mat_new(4,3,sizeof(double));

/*appel sans probleme a la compilation : */
mat_free((void**)matrice,4);

là si je cast pas l'un et l'autre... warning... où se situe la
différence avec le 1er exemple ?

merci de m'éclairer à ce propos.


Les fonctions mat_new et mat_free fonctionnent seulement si un void* a
la mème taille et la même représentation binaire qu'un double*.
Puisque ces deux propriétés ne sont pas imposées par la norme du
langage C la conversion n'est pas sure et il faut un cast pour dire au
compilateur "je sais que cela fonctionnne".

Si tu veux éliminer le cast lors de l'appel des fonctions et si tu es
sur que void* et double* sont identiques dans tous les système qui
font tourner ton code tu peux moodifier les fonctions:

void *
mat_new(unsigned int nrow, unsigned int ncol, size_t size_type)
{
int i,j;
void **m = malloc(nrow * sizeof *m);
...
return m;
}

void
mat_free(void *mm,unsigned int nrow)
{

void **m = mm:
...
}

--
Horst

Avatar
Nico
Emmanuel Delahaye a exposé le 07/11/2004 :

void* est un cas particulier. Il faut un cast pour les
autres. Note que ça peut être dangereux dans les cas où
double* n'a pas la même taille que void*.


C'est possible ça? Si c'est vrai, ça remet en cause beaucoup de code qui
compte là dessus...



j'ai tjrs un peu de mal a comprendre, est-ce aussi dangereux si je cast
? le cast n'est-il pas sensé convertir mes pointeurs ?
mat_free((void**)matrice,4); est-ce correct et propre ?

--
Nico,
http://astrosurf.com/nicoastro
http://nicolas.aunai.free.fr


Avatar
Nico

Les fonctions mat_new et mat_free fonctionnent seulement si un void* a
la mème taille et la même représentation binaire qu'un double*.
Puisque ces deux propriétés ne sont pas imposées par la norme du
langage C la conversion n'est pas sure et il faut un cast pour dire au
compilateur "je sais que cela fonctionnne".



je compile avec l'option -ansi, pourquoi mon compilateur ne me le dit
pas ?

--
Nico,
http://astrosurf.com/nicoastro
http://nicolas.aunai.free.fr

Avatar
Gabriel Dos Reis
Nico writes:

| Jean-Marc Bourguet avait soumis l'idée :
|
|
| > void* est un cas particulier.
|
| pourquoi ?

Because someone got clever.

-- Gaby
Avatar
Gabriel Dos Reis
Emmanuel Delahaye writes:

| Jean-Marc Bourguet wrote on 07/11/04 :
|
| > void* est un cas particulier. Il faut un cast pour les
| > autres. Note que ça peut être dangereux dans les cas où
| > double* n'a pas la même taille que void*.
|
| C'est possible ça?

double** n'est pas censé avoir la même tailler que void**.

| Si c'est vrai, ça remet en cause beaucoup de code
| qui compte là dessus...

Il y a beaucoup de codes douteux dans la nature ; je ne pense pas qu'il
y a une proportion significatrice qui compte sur double** ~~ void**
sans information complémentaire.

-- Gaby
1 2 3 4 5