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

travail sur les pointeurs

17 réponses
Avatar
Vincent
Bonjour,

Dans le code ci-dessous, j'utilise des tableaux de pointeurs sur des
chaines de char (char **). Afin de permettre à la fonction foo() de
travailler sur le contenue, je passe une référence à ce tableau de
pointeur (char ***). foo() semble faire correctement sont travail, du
moins tant qu'on est dans la fonction (gdb me confirme que les chaines
ont bien été copiée). Or je ne récupère pas le résultat une fois sortie
de foo().

Je ne sais pas si j'ai été bien clair, dans le cas contraire, je
tenterai d'expliquer différemment mon problème.

#include <windows.h>
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <winsock2.h>
#include <ws2tcpip.h>

#define MALLOC(x) HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, (x))
#define FREE(x) do { HeapFree(GetProcessHeap(), HEAP_NO_SERIALIZE, (x));
(x)=NULL; } while(0)
#define REALLOC(pt, x) HeapReAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY,
(pt), (x))

static void foo (char ***RcptList, char **email)
{
int i = 0, j = 0;
if (!email || !RcptList || !*RcptList)
return;

while (*RcptList[i])
i++;

while(email[j])
{
/* if (!IsPresentInList(*RcptList, email[j]))
{*/
*RcptList[i] = MALLOC(strlen(email[j])+1);
memset(*RcptList[i], '\0', strlen(email[j])+1);
strcpy(*RcptList[i], email[j]);
i++;
*RcptList = REALLOC(*RcptList, (i+1)*sizeof(char *));
*RcptList[i] = NULL;
/*}*/
j++;
}
return;
}

int main (int argc, char ** argv)
{
char ** to = NULL;
char ** rcpt = NULL;
to = MALLOC(3*sizeof(char*));
to[0] = MALLOC(50);
to[1] = MALLOC(50);
to[2] = NULL;

rcpt = MALLOC(sizeof(char *));
rcpt[0] = NULL;

sprintf(to[0],"toto@titi.com");
sprintf(to[1],"qrsfrferf@zer.com");

foo(&rcpt,to);
int i = 0;
while (rcpt[i]) {
printf("%d:%s\n",i,rcpt[i]);
i++;
}
return 0;
}

7 réponses

1 2
Avatar
Marc Boyer
On 2008-09-02, Vincent wrote:

3 *, ça me donne mal à la tête.



C'est vrai que ce n'est pas le plus simple pour l'esprit, mais passons
les arguments qui m'ont poussé à l'utiliser, disons simplement que c'est
un exercice qui, normalement, devrait fonctionner.

Ceci fonctionne :


static char **foo (char **RcptList, char **email)

Mais il y a des précautions à prendre quand on utilise realloc() :

http://mapage.noos.fr/emdel/notes.htm#realloc



Je suis entièrement d'accord la dessus, ainsi qu'avec les remarques de
M.Boyer sur les défauts de ce code. Il doit y en avoir d'autres, mais je
souhaite tout d'abord comprendre le non fonctionnement de ce que je
propose.



Comme dit par Antoine: tu t'es emméllé les pinceaux dans ton ***.
Il y a un niveau d'indirection pour panipuler des char*, un autre
pour le tableau, et un troisième pour le mode in/out (ie, modif
de la valeur). Et tu penses d'une manière, et tu utilise d'une autre.

Maintenant retournons à la demande initiale, soit un exercice sur char
***. J'ai beau retourner le pb dans tous les sens, faire des petits
dessins définissant les zone mémoire et empilement de pointeurs, je ne
comprend pas pourquoi ca ne fonctionne pas.



Parce que *RcpList[i] est interprété comme *(RcpList[i]) alors
que tu dois faire tes dessins avec (*RcpList)[i].

Marc Boyer
--
Si tu peux supporter d'entendre tes paroles
Travesties par des gueux pour exciter des sots
IF -- Rudyard Kipling (Trad. André Maurois)
Avatar
Vincent
> Non. D'abord cela s'écrit char*[]. Tableau ([]) de chaînes (char*), écrit à
l'envers comme il se doit. Ensuite, ce ne sont pas des pointeurs vers des
chaînes, ce sont des chaînes tout court.


[...]
Et si on se ramène à l'écriture ci-dessus, on obtient plutôt char *(*)[] :
j'ai rajouté l'indirection au bon endroit, c'est-à-dire au milieu, et cela
impose donc de rajouter une paire de parenthèse.

Cette digression semble peut-être inutile et superfétatoire, mais comme par
la suite ton code semble vouloir utiliser exactement mon formalisme...



D'accord, mais pas inutile pour moi en tout cas (manifestement).


foo() semble faire correctement sont travail, du
moins tant qu'on est dans la fonction



Donc mon petit doigt me dit que tes contrôles et autres assertions (qui ont
été supprimés du code que tu as posté certainement dans la louable intention
d'alléger le message) ne font complètement leur travail

(gdb me confirme que les chaines ont bien été copiée).



Mais il ne semble pas te dire où...



Je n'ai pas la sortie de gdb pour le montrer, mais à moins que j'ai fait
une erreur, les adresses pointés étaient les même dans l'un et l'autre
fonction. Le contenu pointé n'étant pas le même, c'est cela qui m'a
poussé à écrire. Si nécessaire, je rejouerai le debogage pour fournir
les sorties.

static void foo (char *(*RcptList)[], char *entetes[])

{ int i = 0, j = 0;
while (*RcptList[i]) i++;



Et nous y voilà.
Si on se reporte au prototype, il saute aux yeux que l'on ne fait pas
l'indexation au bon endroit, et qu'il manque une paire de parenthèses.


[...]
Mais non : il faut corriger
de la même manière toutes les occurences de RcptList.




J'ai effectué tous les changements que je pense nécessaires, mais butte
sur le REALLOC() :
*RcptList = REALLOC(*RcptList, (i+1)*sizeof(char *));

Je ne vois pas comment modifier l'écriture de *RcptList.



Pour continuer avec les commentaires de code en passant

#define MALLOC(x) HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, (x))


^^^^^^^^^^^^^^^^
*RcptList[i] = MALLOC(strlen(email[j])+1);
memset(*RcptList[i], '', strlen(email[j])+1);



Ceinture et bretelles ?



Tout a fait inutile dans ce contexte je vous l'accorde volontier

strcpy(*RcptList[i], email[j]);



man strlcpy


sprintf(to[0],"");
sprintf(to[1],"");



Mauvaise habitude, le jour où tu le feras avec un argument HTTP ou une
adresse mail encodée avec un % au milieu, tu t'en mordras les doigts.
strlcpy() c'est mieux.



Malheureusement j'en suis plein de mauvaises habitudes. Je vais tenter
d'en corriger certaines à l'issue de ce thread.

Merci de votre aide et commentaires.

Vincent
Avatar
Antoine Leca
En news:48c014a8$0$26984$, Vincent va escriure:
J'ai effectué tous les changements que je pense nécessaires, mais
butte sur le REALLOC() :
*RcptList = REALLOC(*RcptList, (i+1)*sizeof(char *));

Je ne vois pas comment modifier l'écriture de *RcptList.



Que veux-tu modifier ?
*RcptList désigne le tableau, tu le redimensionne et tu mets à jour l'objet,
il n'y a pas de raison ÀMHA pour écrire autre chose.

Cela ne marche toujours pas avec le REALLOC tel qu'écrit ci-dessus ?
Bien évidemment le flag HEAP_GENERATE_EXCEPTIONS est posé sur le tas obtenu
par GetProcessHeap (ou bien tu teste la valeur de retour d'une manière ou
d'une autre et tu te déroutes en cas d'erreur), n'est-ce-pas ?

Essaye avec moins d'optimisation de la part du compilateur (ou change de
version), ce ne serait pas la première fois qu'on verrait un bogue de
compilateur dans ce genre de code (le compilo range la valeur pointée par
RcptList dans une variable qu'il ne met pas à jour avec le retour de
realloc(), et si le système a déplacé le tableau cela fait boum).


Antoine
Avatar
Vincent
Antoine Leca wrote:
En news:48c014a8$0$26984$, Vincent va escriure:
J'ai effectué tous les changements que je pense nécessaires, mais
butte sur le REALLOC() :
*RcptList = REALLOC(*RcptList, (i+1)*sizeof(char *));

Je ne vois pas comment modifier l'écriture de *RcptList.



Que veux-tu modifier ?



En fait je fait référence à l'erreur de gcc (mingw32) qui me dit :
emc.c:29: error: invalid use of array with unspecified bounds

sur la ligne *RcptList = REALLOC(*RcptList, (i+1)*sizeof(char *));

J'ai sans doute fait une erreur quelque part. Voici la fonction. Notez
que IsPresentInList() est une coquille vide pour l'instant et peut tout
a fait être omise.

static void foo (char *(*RcptList)[], char *email[])
{
int i = 0, j = 0;
if (!email || !RcptList || !(*RcptList))
return;

while ((*RcptList)[i])
i++;

while(email[j])
{
if (!IsPresentInList(*RcptList, email[j]))
{
(*RcptList)[i] = MALLOC(strlen(email[j])+1);
strcpy((*RcptList)[i], email[j]);
i++;
*RcptList = REALLOC(*RcptList, (i+1)*sizeof(char *));
(*RcptList)[i] = NULL;
}
j++;
}
return;
}
Avatar
Erwan David
Vincent écrivait :

Antoine Leca wrote:
En news:48c014a8$0$26984$, Vincent va escriure:
J'ai effectué tous les changements que je pense nécessaires, mais
butte sur le REALLOC() :
*RcptList = REALLOC(*RcptList, (i+1)*sizeof(char *));

Je ne vois pas comment modifier l'écriture de *RcptList.



Que veux-tu modifier ?



En fait je fait référence à l'erreur de gcc (mingw32) qui me dit :
emc.c:29: error: invalid use of array with unspecified bounds

sur la ligne *RcptList = REALLOC(*RcptList, (i+1)*sizeof(char *));

J'ai sans doute fait une erreur quelque part. Voici la fonction. Notez
que IsPresentInList() est une coquille vide pour l'instant et peut
tout a fait être omise.

static void foo (char *(*RcptList)[], char *email[])
{
int i = 0, j = 0;
if (!email || !RcptList || !(*RcptList))
return;

while ((*RcptList)[i])
i++;

while(email[j])
{
if (!IsPresentInList(*RcptList, email[j]))
{
(*RcptList)[i] = MALLOC(strlen(email[j])+1);
strcpy((*RcptList)[i], email[j]);
i++;
*RcptList = REALLOC(*RcptList, (i+1)*sizeof(char *));
(*RcptList)[i] = NULL;
}
j++;
}
return;
}





MALLOC et REALLOC ont quelles définitions ? En majuscule ce sont des
macros (a priori).

(sinon le code va planter sur un échec d'allocation ou de réallocation :
pas de traitement des cas d'erreur).

--
Le travail n'est pas une bonne chose. Si ça l'était,
les riches l'auraient accaparé
Avatar
Antoine Leca
En news:48c817d6$0$591$, Vincent va escriure:
En fait je fait référence à l'erreur de gcc (mingw32) qui me dit :
emc.c:29: error: invalid use of array with unspecified bounds

sur la ligne *RcptList = REALLOC(*RcptList, (i+1)*sizeof(char *));



Groumpf :-(

Pour mémoire, j'écrivis dans news:g9j725$2pe$:
: En news:48b867f2$0$16334$, Vincent va escriure:
:> Dans le code ci-dessous, j'utilise des tableaux de pointeurs sur des
:> chaines de char
:
: D'accord.
:
:> (char **).
:
: Non. D'abord cela s'écrit char*[]. Tableau ([]) de chaînes (char*),
: écrit à l'envers comme il se doit. Ensuite, ce ne sont pas des
: pointeurs vers des chaînes, ce sont des chaînes tout court. Pas
: besoin de faire plus compliqué que cela ne l'est au départ.
:
:
:> Afin de permettre à la fonction foo() de
:> travailler sur le contenue, je passe une référence à ce tableau de
:> pointeur (char ***).
:
: Et si on se ramène à l'écriture ci-dessus, on obtient plutôt char
: *(*)[] : j'ai rajouté l'indirection au bon endroit, c'est-à-dire au
: milieu, et cela impose donc de rajouter une paire de parenthèse.
[...]
:> static void foo (char ***RcptList, char **email)
:
: static void foo (char *(*RcptList)[], char *entetes[])

En fait, cette explication très bien au plan didactique.
Sauf sur un point.
Ce n'est pas du C correct. Groumpf.
En effet, en C seul le _dernier_ qualificateur de tableau est transformé en
pointeur, ce n'est pas le cas pour les autres qualificateurs de tableau.

Ainsi, dans

int machin(int tableau[N][M][P])

on définit un paramètre tableau qui est en fait un pointeur vers...
int[M][P], pointeur vers une matrice à deux dimensions, et cette chose n'est
*pas* un simple pointeur ; entre autre, il est nécessaire de spécifier les
dimensions de la matrice (c'est tout le problème du passage des tableaux à
plusieurs dimensions).
Notons bien qu'ici, le compilateur néglige l'indication de la taille N qui
indique la taille du « cube », et il est possible et même courant d'écrire

int machin(int tableau[][M][P])

Ce détour étant fait, si l'on revient à

static void foo (char *(*RcptList)[], char *entetes[])

on y lit que entetes est un pointeur vers des chaînes (rangées dans un
tableau, mais c'est juste la syntaxe qui nous renseigne, pour le compilateur
c'est la même chose que char**, un pointeur vers pointeur vers un
caractère), mais que par contre RcptList est un pointeur vers un tableau
_incomplet_ de chaînes. BUG.

Évidemment, lorsque tu demandes à redimensionner le tableau incomplet, le
compilateur proteste, on est pas en Basic Microsoft ici.

Donc en fait, il n'y a pas de moyen d'échapper à l'horrible ***.
De fait, si l'on regarde le cas des chaînes, en fait ce sont bien des char[]
mais dans la pratique toutes les fonctions les manipulent comme des char* :
c'est pour la même raison.

En conséquence, mon écriture du prototype était très bien pour [faire]
comprendre le problème, mais pas correcte pour le compilateur. Snif+Groumpf.


J'ai sans doute fait une erreur quelque part.



Non non. L'erreur est de ma part. L'exemple était trop parlant, et j'ai trop
fait de Pascal dans ma vie.
D'ailleurs en C, comme je l'écrivais, je n'aurais pas utilisé des char*[]
mais des (vraies) listes chaînées ; et cette discussion est une preuve
supplémentaire de la justesse de ce point de vue.


Antoine
Avatar
Erwan David
Mickaël Wolff écrivait :

Erwan David a écrit :


MALLOC et REALLOC ont quelles définitions ? En majuscule ce sont des
macros (a priori).



Tu devrais relire le premier article du thread ;)



Oups je n'étais pas remonté assez haut...

--
Le travail n'est pas une bonne chose. Si ça l'était,
les riches l'auraient accaparé
1 2