Contenu | Rechercher | Menus

Annonce

Si vous avez des soucis pour rester connecté, déconnectez-vous puis reconnectez-vous depuis ce lien en cochant la case
Me connecter automatiquement lors de mes prochaines visites.

À propos de l'équipe du forum.

#1 Le 21/05/2017, à 13:57

DonutMan75

[RESOLU] [C] getsubopt

Bonjour à tous,

j'ai une question concernant la fonction getsubopt qui permet d'interpréter les options du style

$./mon_programme -o auto,noexec,delai=3,frequence=8

Le prototype de cette fonction est le suivant :

int getsubopt(char **optionp, char * const *tokens, char **valuep);

La documentation indique :

man getsubopt a écrit :

getsubopt()  parses  the list of comma-separated suboptions provided in
       optionp.  [...]  The following is
       an example of the kind of string that might be passed in optionp:

           ro,name=xyz

Voici ce que je ne comprends pas :
l'argument optionp est sensé être une chaîne de caractère contenant la liste des options, donc il serait du type char *optionp...
Mais le définir comme char **optionp veut dire que c'est un tableau de pointeurs chacun pointant vers une chaîne de caractère (un peu comme le serait char **argv du main).

Je ne comprends pas cela... Pourriez-vous m'éclairer ?

Ma seule (maigre) piste de compréhension c'est cette note de la documentation :

man getsubopt a écrit :

NOTES
       Since  getsubopt()  overwrites  any  commas  it  finds  in  the  string
       *optionp, that string must be writable; it cannot be a string constant.

Si je comprends bien, getsubopt va modifier la chaîne de caractère passée en argument c'est pourquoi il faut lui passer l'adresse de cette chaîne et non pas la chaîne elle-même.
Je suppose qu'en interne la fonction getsubopt va recréer une nouvelle chaîne "from scratch" ?

Ca expliquerait par ailleurs la ligne "subopts = optarg;" du code donné en exemple dans le man getsubopt : en appelant getsubopt(&subopts, token, &value), on fait pointer subopt vers une nouvelle chaîne expurgée des "," sans modifier pour cela optarg.

 while ((opt = getopt(argc, argv, "o:")) != -1) {
               switch (opt) {
               case 'o':
                   subopts = optarg;
                   while (*subopts != '\0' && !errfnd) {

                   switch (getsubopt(&subopts, token, &value)) {
                   case RO_OPT:
                       readonly = 1;
[...]

Enfin bref, je suis un peu perdu... Est-ce que quelqu'un pourrait m'expliquer le fonctionnement de cette fonction ?

Merci d'avance et bon dimanche smile

Donut

Dernière modification par DonutMan75 (Le 22/05/2017, à 09:31)

Hors ligne

#2 Le 21/05/2017, à 17:48

Compte anonymisé

Re : [RESOLU] [C] getsubopt

Bonjour.

EN premier lieu, il faut oublier le concept de string en C : il n'y en a pas ! En C, on a au mieux des caractères alloués dynamiquement de manière contigüe dans le tas. Le fait que l'on puisse écrire

char *c = "chaine"

provient du fait que le compilateur est malin pour savoir combien de caractères il doit allouer en mémoire (ici 6 caractères).

Maintenant, considérons ta fonction

int getsubopt(char **optionp, char * const *tokens, char **valuep);

Il est dit dans la doc que

getsubopt()  parses  the list of comma-separated suboptions provided in
           optionp.  [...]  The following is
           an example of the kind of string that might be passed in optionp:

               ro,name=xyz

.
Je mets en gras ce qui m'intéresse :

getsubopt()  parses  the list of comma-separated suboptions provided in
           optionp.
  [...]  The following is
           an example of the kind of string characters chain  that might be passed in optionp:

              ro,name=xyz

Je comprends donc qu'il s'agit d'une chaîne de caractères contigus qui est passée en paramètre 1 .

Enfin, la fonction renvoie un entier. Or, on veut une interprétation (parse) du paramètre 1 . => Donc le paramètre 1 est passé en entrée/sortie
confirmé par :

man a écrit :

NOTES
       Since  getsubopt()  overwrites  any  commas  it  finds  in  the  string
       *optionp, that string must be writable; it cannot be a string characters chain constant.

Là, pas de problème, on pourra passer en paramètre notre char *c = "chaîne" car c est une allocation dynamique de caractères. Ce qui sera interdit, c'est un char[] ou un const char * voire un const char [] , ces deux derniers cas étant des allocations constantes.

Dernière modification par Compte anonymisé (Le 22/05/2017, à 07:15)

#3 Le 21/05/2017, à 20:13

claudius01

Re : [RESOLU] [C] getsubopt

Bonsoir,

jojo81 a écrit :

il faut oublier le concept de chaîne de caractères en C : il n'y en a pas ! En C, on a au mieux des caractères alloués dynamiquement de manière contigüe dans le tas. Le fait que l'on puisse écrire

char *c = "chaine"

provient du fait que le compilateur est malin pour savoir combien de caractères il doit allouer en mémoire (ici 6 caractères).

Bizarre, j'aurai plutôt vu 7 caractères réservés dans la zone programme ou données mais surtout pas alloués sur le tas (en plus si c'est 6, je change tout de suite de compilateur ;-)

Je ne comprends pas pourquoi tu remets en cause le concept de chaîne de caractères en C qui est, pour beaucoup, une suite de caractères terminée par le caractère '\0'.

Question: Ce qui suit est-t'il une chaîne de caractères ou autre chose ? Et  comment manipuler cette définition ?

char c[6] = { 'c', 'h', 'a', 'i', 'n', 'e' };

Dernière modification par claudius01 (Le 21/05/2017, à 21:49)

Hors ligne

#4 Le 22/05/2017, à 07:27

Compte anonymisé

Re : [RESOLU] [C] getsubopt

String est du C++ , d'ailleurs, String dispose d'une méthode c_str() qui convertit ta string en char *

claudius01 a écrit :

Question: Ce qui suit est-t'il une chaîne de caractères ou autre chose ? Et  comment manipuler cette définition ?

char c[6] = { 'c', 'h', 'a', 'i', 'n', 'e' };

Un tableau de char ?

#5 Le 22/05/2017, à 09:17

DonutMan75

Re : [RESOLU] [C] getsubopt

Bonjour,
merci pour vos réponses et désolé pour l'abus de langage ^^
Je me permets de faire une petite synthèse ci-dessous avec vos remarques.
Je ne suis pas sûr de bien comprendre le type de optionp mais ça a l'air de bien marcher pourtant...

Merci pour vos contributions en tout cas !

Donut

Premier cas

char c[128] = "Bonjour";

Ca c'est un tableau de 128 caractères stockés de façon contiguë en mémoire.
c est l'adresse du premier éléments (i.e. le caractère 'B')
Les 8 premiers caractères sont 'B', 'o', 'n', 'j', 'o', 'u', 'r' et '\0'.
Le reste n'est pas initialisé.

Quand je fais printf("%s", c) alors le processus va afficher tous les caractères stockés à partir de c (i.e. adresse du premier élément) jusqu'à rencontrer le caractère '\0'. A noter que le compilateur ajoute automatiquement '\0' à chaque déclaration de tableau de caractère.

Deuxième cas

char *c

Ici nous avons un pointeur sur un caractère.
c est donc toujours une adresse.
*c pourra par exemple contenir le caractère 'B'

char *c;
c = malloc(sizeof(char));
*c='B';

Je peux l'imprimer en faisant :

printf("%c", *c);

A ce stade, si je fais :

printf("%s", c);

J'ai un comportement pas très propre parce que rien ne me garantit que c+1 pointe vers '\0'

Mais si maintenant je fais

c = calloc(128, sizeof(char));
char d[128] = "Toto";
strcpy(c, d);

On se retrouve dans le cas précedent.
Je peux à nouveau imprimer la chaîne en faisant (proprement):

printf("%s", c);

Troisième (et dernier cas)

char **c;

Dans ce cas, c est un pointeur sur un pointeur sur un caractère.
On peut en faire un tableau de cette façon :

c = calloc(3, sizeof(char*));

*c = calloc(128, sizeof(char));
*(c+1) = calloc(64, sizeof(char));
*(c+2) = calloc(32, sizeof(char));

Puis on affecte les éléments  de cette façon :

char chaine[10] = "coucou";
strcpy(*c, chaine);
strcpy(*(c+1), chaine);
strcpy(*(c+2), chaine);

et finalement l'affichage des valeurs :

int i;
for (i=0; i<3; i++)
	printf("- *(c+%d) : %s\n", i, *(c+i));


Pour en revenir à ma questions et au regard de vos réponses smile

Quand jojo81 dit "Je comprends donc qu'il s'agit d'une chaîne de caractères contigus qui est passée en paramètre 1 .", je comprends qu'on est dans le deuxième cas de figure, à savoir :

char *optionp;
optionp = calloc(128, sizeof(char));
optionp = "rw,mount=auto"

ou encore plus directement :

char option[128] = "rw,mount=auto";

Mais alors ça me paraît pas cohérent avec le prototype de la fonction qui, lui, indique clairement qu'on est dans le 3eme cas de figure :

int getsubopt(char **optionp, char * const *tokens, char **valuep);

Avec optionp définit tel que ci-dessus, ça marche nickel si je passe &optionp en paramètre, mais pas du tout si je passe optionp...

Hors ligne

#6 Le 22/05/2017, à 09:30

DonutMan75

Re : [RESOLU] [C] getsubopt

Bon et en mettant des printf un peu partout, j'ai trouvé la solution !

Il est à noter que l'appel à getsubopt de cette façon :

int getsubopt(char **optionp, char * const *tokens, char **valuep);

Cet appel là va modifier à chaque itération la valeur de optionp !!

Exemple de code où &liste représente char **optionp.
Autrement dit : char liste[] = "auto,delai=45"

void sous_options(char *liste, int *oauto, char *odelai)
{
int subopt;
char *tokens[] = {"auto", "noauto", "delai", NULL};
char *valeur;

fprintf(stdout, "DEBUG >> sous_option called with list='%s' and &list = %p\n", liste, &liste);

while ((subopt = getsubopt(&liste, tokens, &valeur)) != -1)
	{
	fprintf(stdout, "   DEBUG >> list=%p, list='%s' and &list = %p\n", liste,liste, &liste);
	switch (subopt)
		{
		case 0 :
			*(oauto) = 1;
			break;
		case 1 :
			*(oauto) = 0;
			break;
		case 2 :
			strcpy(odelai, valeur);
			break;
		}
	}

}

Voici un exemple d'éxecution :

$ ./prototype_getopt --adresse toto.fr --port 8032 --option auto,delai=45 fichier1.txt fichier2.txt
DEBUG >> sous_option called with list='auto,delai=45' and &list = 0x7ffe855ce4a8
   DEBUG >> list=0x7ffe855d024c, list='delai=45' and &list = 0x7ffe855ce4a8
   DEBUG >> list=0x7ffe855d0254, list='' and &list = 0x7ffe855ce4a8
- Serveur 	: toto.fr
- Port 		: 8032
- Auto 		:1
- Delai 	:45
- Argument restant : fichier1.txt
- Argument restant : fichier2.txt

On remarque que l'adresse &liste ne change pas à chaque appel (normal).
En revanche, la valeur stockée dans liste, c'est à dire l'adresse du premier élément de "auto,delai=45" est modifée à chaque appel !!
Par ailleurs, la chaîne elle-même change passant de "auto,delai=45" à "delai=45" puis finalement à "".

J'en déduis que l'appel de getsubopt(char **optionp, etc....) doit faire quelque part un

free(*optionp);
*optionp = calloc(longueur_nouvelle_liste, sizeof(char));
strcpy(*optionp, nouvelle_liste);

Ceci pour économiser de la place à chaque appel ???
Du coup char **optionp est UN UNIQUE POINTEUR vers un pointeur vers une chaîne de caractère de type {'r', 'w', ',', 'm', 'o', 'u', 'n', 't', '=', 'a', 'u', 't', 'o'};

Ca vous paraît plausible comme fonctionnement ?

En tout cas, je passe le fil en résolu smile

Merci encore pour vos commentaires !

Bonne journée à tous,

Donut

Hors ligne

#7 Le 22/05/2017, à 09:43

DonutMan75

Re : [RESOLU] [C] getsubopt

Ou encore, sans faire de free() et calloc(), on peut aussi imaginer un mécanisment du style

int getsubopt(char **optionp, etc....)
{
// On lit N caractères jusqu'à tomber sur un ','
*optionp = *optionp+N;
}

Hors ligne