OVH Cloud OVH Cloud

Art de programmation

4 réponses
Avatar
ben64
Bonjour,

J'ai une petite question (en fait 2) sur la programmation en C.
Je pense que je connais bien les techniques du C, mais je ne sais pas
bien programmer. Je vous donne un exemple :
Supposons que j'ai une structure avec plusieurs champs qui sont des
pointeurs (par exemple des chaînes de caractères char * et pas char []).
Ces char* représentents des informations que je récupère par un read sur
une socket par exemple. Je ne connais pas à l'avance la taille de ces
chaînes, donc je ne peux pas faire de char[]. Je pourrais évidemment
tronqué les chaines et donc les déclarer de type char[MAX_SIZE], ce que
je dois faire ni plus ni moins avec des char * pour ne pas lire
indéfiniment. Mais dans ce cas si je ne lis que 5 octets je perds
beaucoup de place (car MAX_SIZE >> 5). Donc pour en revenir au problème,
je fais des malloc sur chaque char*. Le problème est que quand je dois
libérer cette structure, je dois faire autant de free qu'il n'y a de
champs. Pire si une ou plusieurs fonctions remplissent au fur et à
mesure cette structure et que celles ci échouent, je dois libérer les
champs qui ont été préalablement alloué.

ex:

typedef struct ma_struct
{
char * un;
char * deux;
char * trois;
char * quatre;
} ma_struct;


int remplit_un(ma_struct st)
{
st.un = (char *)malloc(10);
if( !st.un )
return 0;
else
return 1;
}

int remplit_deux(ma_struct st)
{
st.deux = (char *)malloc(10);
if( !st.deux )
return 0;
else
return 1;
}

int remplit_trois(ma_struct st)
{
st.trois = (char *)malloc(10);
if( !st.trois )
return 0;
else
return 1;
}

int remplit_quatre(ma_struct st)
{
st.quatre = (char *)malloc(10);
if( !st.quatre )
return 0;
else
return 1;
}

int main(int argc, char**argv)
{
ma_struct st;

if( !remplit_un(st) )
exit(1);

if( !remplit_deux(st) )
{
free(st.un);
exit(1);
}

if( !remplit_trois(st) )
{
free(st.un);
free(st.deux);
exit(1);
}

if( !remplit_quatre(st) )
{
free(st.un);
free(st.deux);
free(st.trois);
exit(1);
}

return 0;
}


Donc techniquement pas de problème, par contre je trouve que le code
est vraiment lourd. Bien entendu, ce n'est pas tout a fait le problème
que j'ai, mais il est de ce style : revenir à un état cohérent quand une
fonction a échoué (si on ouvre une socket, et que l'instruction suivante
échoue, il faut fermer la socket ...)
Quand je regarde les codes source d'Apache, PostgreSQL, ... j'ai
l'impression qu'ils ne sont pas confronté à ce genre de problème, ou
alors qu'ils le gèrent différement. La technique que j'utilise (en ce
qui concerne l'allocation, mais je n'ai pas de solution dans le cas
général) est que mes structures ont une grande zone de mémoire (char *)
et que les char * suivant ne sont que des références à certaines parties
du permier char *. Ainsi si j'ai un problème, je ne libère que le
premier char *. Mais bon, je réalise que même si je connais
techniquement le C, je ne sais pas programmer. Donc si vous pouviez me
donner une ou deux conseils, ou me conseiller un livre. J'ai cherché,
mais tout ce que j'ai trouvé ne fait qu'expliquer la technique, et pas
la façon de programmer (réutilisable, maintenable). Je ne connais pas
les règles de programmation : par exemple est ce que quand une fonction
prend comme paramètre un char ** on s'attend a ce qu'elle alloue de la
mémoire ?

Merci

Benoît

4 réponses

Avatar
Bruno Desthuilliers
Frédéri MIAILLE wrote:
(snip)

typedef struct ma_struct
{
Reset(){...un=NULL; deux=NULL;...}
Clear(){if(un)Free(un); if(deux)free(deux); Reset();}
Insert(void *_Dataun, void *_Datadeux...){mallocmachin, memcopytruc...} //
ou encore faire une méthode pour chaque allocation, voire une fonction à
nombre d'arguments variable...
char * un;
char * deux;
char * trois;
char * quatre;
} ma_struct;



Heu ? Tu es sûr que c'est du C, ça ?

/* methods.c */
typedef struct mystruct
{
int nothing(void)
{
return 0;
}

int num;
} mystruct;

int main(void)
{
mystruct m;
int res = m.nothing();
m.num = 1;
return 0;
}

[ dev]$ gcc -Wall -ansi -pedantic -o methods methods.c
methods.c:4: field `nothing' declared as a function
methods.c:4: warning: no semicolon at end of struct or union
methods.c:4: parse error before '{' token
methods.c:9: parse error before '}' token
methods.c:9: warning: type defaults to `int' in declaration of `mystruct'
methods.c:9: ISO C forbids data definition with no type or storage class
methods.c: In function `main':
methods.c:13: parse error before "m"
methods.c:14: `m' undeclared (first use in this function)
methods.c:14: (Each undeclared identifier is reported only once
methods.c:14: for each function it appears in.)
methods.c:14: warning: unused variable `res'

Tu ne confondrais pas avec C++, par hasard ?-)

Bruno

Avatar
Frédéri MIAILLE
Un petit peu peut-être oui... :/
Je sors alors ?
Tiens, tu m'indiques la porte que je la prenne discrètement...

--
Frédéri MIAILLE
fr.comp.lang.c
fr.comp.lang.c++
fr.comp.os.ms-windows.programmation
fr.comp.graphisme.programmation
Avatar
Richard Delorme

/* methods.c */
typedef struct mystruct
{
int nothing(void)
{
return 0;
}

int num;
} mystruct;

int main(void)
{
mystruct m;
int res = m.nothing();
m.num = 1;
return 0;
}

[ dev]$ gcc -Wall -ansi -pedantic -o methods methods.c
methods.c:4: field `nothing' declared as a function
methods.c:4: warning: no semicolon at end of struct or union
methods.c:4: parse error before '{' token
methods.c:9: parse error before '}' token
methods.c:9: warning: type defaults to `int' in declaration of `mystruct'
methods.c:9: ISO C forbids data definition with no type or storage class
methods.c: In function `main':
methods.c:13: parse error before "m"
methods.c:14: `m' undeclared (first use in this function)
methods.c:14: (Each undeclared identifier is reported only once
methods.c:14: for each function it appears in.)
methods.c:14: warning: unused variable `res'

Tu ne confondrais pas avec C++, par hasard ?-)


Pourtant, avec quelques modifications, ça marche :

/* methods.c */
typedef struct mystruct
{
int (*nothing)(void);
int num;
} mystruct;

int nothing(void)
{
return 0;
}

int main(void)
{
mystruct m = {nothing, 0};
int res = m.nothing();
m.num = 1;
return 0;
}

$ gcc -W -Wall -ansi -pedantic -O2 methods.c -o methods
methods.c: Dans la fonction « main »:
methods.c:17: AVERTISSEMENT: variable inutilisée « res »

--
Richard

Avatar
Laurent Deniau
Richard Delorme wrote:
Je n'ai pas de titre à conseiller, mais peut-être un de ceux décrit ici te
conviendrais :
http://www.accu.org/bookreviews/public/reviews/0hr/writing_solid_code.htm


Il manque Unleashed C dans cette liste qui, bien que d'un niveau moyen, couvre
pas mal de sujets.

a+, ld.


--
[ Laurent Deniau -- Scientific Computing & Data Analysis ]
[ CERN -- European Center for Nuclear Research ]
[ - http://cern.ch/Laurent.Deniau ]
[ -- One becomes old when dreams become regrets -- ]