IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

 C Discussion :

Fonction et tableau de strings


Sujet :

C

Vue hybride

Message précédent Message précédent   Message suivant Message suivant
  1. #1
    Membre averti
    Profil pro
    Inscrit en
    Novembre 2006
    Messages
    20
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Novembre 2006
    Messages : 20
    Par défaut Fonction et tableau de strings
    Bonjour à tous,

    Une première pour moi dans ce forum - même si d'autres sujets sur le site m'ont déjà inspirés sur developpez.net : je débute en C et plus particulièrement sur Arduino !
    Oui, je sais, peut être aurait il fallu que j'écrive dans le forum Arduino, mais ma question relève plus du C que d'Arduino en lui même

    Mon problème est le suivant :

    J'ai une fonction "test" qui doit me renvoyer un chiffre puis plusieurs chaines de caractères vers la fonction main(). J'ai donc écrit ce morceau de code mais au final, je n'arrive pas à retrouver les valeurs quand je tente de les ressortir dans main().
    J'ai beau chercher un peu partout sur les moteurs de recherche, mais mes connaissances en C et encore plus sur les pointeurs & la mémoire système, sont très limités.
    Il me semblait avoir compris que pour retrouver la valeur dans un pointeur, il suffisait de mettre "&" devant le pointeur... Ici ça semble ne pas fonctionner

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
     
    #include <stdio.h>
    #include <stdlib.h>
    int *test(){
        char *p;
        p = malloc(4*sizeof(char));
        p[0] = 3;
        p[1] = "Bonjour";
        p[2] = "moi";
        p[3] = "toi";
        return p;
    }
    int main(){
        char *tab;
        int i;
        tab = malloc(4*sizeof(char));
        tab = test();
        for(i=0;i<4;i++) {
            printf("%d\n",&tab[i]);
        }
        free(tab);
        return 0;
    }
    A noter que la première valeur est un int ("3" que j'ai inclus dans le tableau char... bof, bof...) puis les autres phrases sont bien des Strings.

    Une idée (je suppose que oui !) sur ce qui ne va pas sur mon code ??? Comment puis-je retrouver mes valeurs initiales ?

    Merci à tous,


    Thierry

  2. #2
    Membre Expert
    Inscrit en
    Mars 2005
    Messages
    1 431
    Détails du profil
    Informations forums :
    Inscription : Mars 2005
    Messages : 1 431
    Par défaut
    Une idée ? Oui.. plusieurs. Il y a des erreurs quasiment à chaque ligne. Dans l'ordre du flux d'exécution :

    • tu alloues une première fois ton buffer dans main, puis tu écrases l'adresse obtenue avec celle retournée par test : qui est responsable de la gestion mémoire ? L'appelé (test) ou l'appelant (main), mais pas les deux ;
    • test ne retourne pas le bon type de pointeur (ça peut se faire, mais ici c'est une erreur) ;
    • tu mélanges nativement des éléments de types différents au sein d'un même tableau ;
    • si tu interprêtes les éléments de p comme des char (un seul caractère), tu ne peux pas y stocker des chaînes de caractères ;
    • tu ne peux pas affecter / copier des chaînes dans ce contexte en utilisant l'opérateur = ;
    • le spécificateur de format passé à printf n'est pas le bon ;
    • tu confonds l'opérateur unaire d'indirection & et son inverse, l'opérateur unaire de déréférencement * ;
    • j'en ai peut-être oublié.


    Commente tout ton programme, puis réintègre chaque ligne une par une. Tu dois comprendre tout ce que tu fais. Si tu as un doute, nous t'aiderons.

    Active tous les warnings de ton compilateur (options -pedantic -Wall -Wextra pour GCC) puis traque-les tous. Ne t'arrête pas tant qu'il en reste : ce n'est pas parce que ça compile que l'exécutable obtenu a un comportement correct.

  3. #3
    Membre averti
    Profil pro
    Inscrit en
    Novembre 2006
    Messages
    20
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Novembre 2006
    Messages : 20
    Par défaut
    Merci pour la réponse...
    L’environnement Arduino n'étant pas TOP pour faire du debug, j'ai installé CodeBlock pour tester mes routines.

    J'ai repris début le début en refaisant un nouveau programme simple pour comprendre, mais j'ai quelques erreur et le résultat final n'est... tout à fait cela !
    Seule le 1er champ du tableau est correctement retrouvé dans main(); les autres affichent n'importe quoi... et je suppose qu'il doit falloir indiquer une taille quelque part, mais là, j'avoue que je pêche et les erreurs du "return" de la fonction test(), je ne comprend pas pourquoi il y a des warnings ???

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
     
    #include <stdio.h>
    #include <stdlib.h>
     
    int test(){
     
        char *variable[4];          // Déclaration du pointeur "variable" avec 4 champs possible
        variable[0] = "3";          // Définition des données "string" dans chaque ligne de la "variable"
        variable[1] = "Bonjour";
        variable[2] = "moi";
        variable[3] = "toi";
     
        return &variable;           // Je renvoie l'adresse du pointeur mais GCC renvoie "warning: function returns address of local variable [enabled by default]" et "warning: return makes integer from pointer without a cast [enabled by default]"
    }
     
    int main(){
     
        int *AdrTableau = test();   // on récpère l'adresse du pointeur renvoyé par la fonction test()
        int i;                       // Init de la variable temporaire et locale
        for(i=0; i<4; i++) {        // Boucle pour lister tous les champs
            // %d: affiche l'int "i"; %s: affiche le string char*
            printf("%d : %s\n", i, AdrTableau[i] );
        }
     
        return 0;
    }

    Que pensez vous de ce code ?

  4. #4
    Membre averti
    Homme Profil pro
    Étudiant
    Inscrit en
    Décembre 2014
    Messages
    17
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : Autre

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : Décembre 2014
    Messages : 17
    Par défaut
    J'ai trois erreurs lorsque je compile avec les flags que Matt_Houston t'a conseillé :

    ligne 12 => error: incompatible pointer to integer conversion returning 'char *(*)[4]' from a function with result type 'int' [-Werror,-Wint-conversion]
    ligne 17 => error: incompatible integer to pointer conversion initializing 'int *' with an expression of type 'int' [-Werror,-Wint-conversion]

    ligne 21 => error: format specifies type 'char *' but the argument has type 'int' [-Werror,-Wformat]


    Les comprendre t'aidera vraiment à corriger tes erreurs.

  5. #5
    Membre averti
    Profil pro
    Inscrit en
    Novembre 2006
    Messages
    20
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Novembre 2006
    Messages : 20
    Par défaut
    Citation Envoyé par Personne.c Voir le message
    Les comprendre t'aidera vraiment à corriger tes erreurs.
    Le problème, c'est que je ne comprend pas pourquoi...
    Tout me semble "logique"... Mais j'ai du louper quelque chose !
    Avez vous une piste à me conseiller sur "chaque" problème ? Je pensais pourtant comprendre ces histoires de pointeur, mais visiblement...

  6. #6
    Membre prolifique
    Avatar de Sve@r
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Février 2006
    Messages
    12 822
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Oise (Picardie)

    Informations professionnelles :
    Activité : Ingénieur développement logiciels
    Secteur : Aéronautique - Marine - Espace - Armement

    Informations forums :
    Inscription : Février 2006
    Messages : 12 822
    Billets dans le blog
    1
    Par défaut
    Citation Envoyé par azimut2000 Voir le message
    J'ai repris début le début en refaisant un nouveau programme simple pour comprendre, mais j'ai quelques erreur et le résultat final n'est... tout à fait cela !
    Seule le 1er champ du tableau est correctement retrouvé dans main(); les autres affichent n'importe quoi... et je suppose qu'il doit falloir indiquer une taille quelque part, mais là, j'avoue que je pêche et les erreurs du "return" de la fonction test(), je ne comprend pas pourquoi il y a des warnings ???

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    #include <stdio.h>
    #include <stdlib.h>
     
    int test(){
     
        char *variable[4];          // Déclaration du pointeur "variable" avec 4 champs possible
        variable[0] = "3";          // Définition des données "string" dans chaque ligne de la "variable"
        variable[1] = "Bonjour";
        variable[2] = "moi";
        variable[3] = "toi";
     
        return &variable;           // Je renvoie l'adresse du pointeur mais GCC renvoie "warning: function returns address of local variable [enabled by default]" et "warning: return makes integer from pointer without a cast [enabled by default]"
    }
     
    int main(){
     
        int *AdrTableau = test();   // on récpère l'adresse du pointeur renvoyé par la fonction test()
        int i;                       // Init de la variable temporaire et locale
        for(i=0; i<4; i++) {        // Boucle pour lister tous les champs
            // %d: affiche l'int "i"; %s: affiche le string char*
            printf("%d : %s\n", i, AdrTableau[i] );
        }
     
        return 0;
    }
    Le problème, c'est que je ne comprend pas pourquoi...
    Tout me semble "logique"... Mais j'ai du louper quelque chose !
    Avez vous une piste à me conseiller sur "chaque" problème ? Je pensais pourtant comprendre ces histoires de pointeur, mais visiblement...
    Bonjour

    Le souci de ton code est que ta fonction "test()" renvoie l'adresse d'une variable locale, donc variable détruite quand on quitte la fonction. Et donc dans ton main() tu reçois une adresse ne correspondant à plus rien de concret.

    Si une fonction doit renvoyer une adresse, elle doit renvoyer une adresse persistante. Or il n'y a que 2 façons de créer une adresse persistante: en utilisant un type "static" ou l'allouer via malloc/calloc/realloc
    Le type static peut sembler pratique à priori mais a l'inconvénient de rendre ta fonction non réentrante (si la fonction est appelée deux fois, le second appel écrase le premier). Le malloc est plus sympa mais tu es alors responsable du pointeur que tu récupères et c'est donc à toi de le libérer. Et si tu appelles ta fonction deux fois ben, tu dois alors gérer deux retours.

    Autre détail: bien qu'une adresse soit un nombre entier, ce n'est pas un "int". Si ta fonction doit renvoyer une adresse, elle doit alors être du type "pointeur".
    Ce n'est pas un souci d'incompatibilité, c'est un souci "d'information". Quand tu écris int var=123; int *pt=&var tu informes le compilo que *pt (ce qu'il y a à l'adresse "pt") est un entier. Si ensuite tu demandes printf("%d", *pt) le compilo connaissant la nature de *pt saura le manipuler (récupérer 4 octets à partir de l'adresse pt (pas d'étoile puisque je parle ici du pointeur "pt") et convertir ces 4 octets en nombre).
    Si en revanche tu écris int var=123; int pt=&var, ce n'est pas faux en soi (au plus bas niveau tout est du nombre) mais ensuite quand tu demandes printf("%d", *pt) le compilo ne connaissant pas la nature *pt ne sait pas quoi faire de cette adresse.

    Tout ça pour dire que ta fonction ne peut pas être de type "int".

    Maintenant, pour corriger ton exemple, il suffit de réfléchir. Tu veux créer une fonction qui va stocker 4 strings. Une string étant manipulable par son pointeur de début, on a l'habitude de dire "string=pointeur". Ce n'est pas rigoureusement exact mais c'est une analogie qui peut aider à comprendre.
    Donc tu vas devoir créer un tableau permettant de stocker 4 char *. Un tableau de "<truc>" s'allouant dans un <truc> *, tu alloueras ces 4 char * dans un char **.

    Ce qui donne:
    Code c : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    #include <stdio.h>
    #include <stdlib.h>
     
    char **test(){
    	char **variable;		// Déclaration d'un pointeur double
     
    	variable=malloc(4 * sizeof(char *));
    							// Allocation d'une zone permettant de stocker 4 pointeurs simples
     
    	// Remplissage des 4 pointeurs avec 4 adresses
    	variable[0] = "3";		
    	variable[1] = "Bonjour";
    	variable[2] = "moi";
    	variable[3] = "toi";
     
    	return variable;		// Renvoi du pointeur alloué
    }
     
    int main(){
     
    	char **AdrTableau = test();
    	int i;
    	for(i=0; i<4; i++) {
    		printf("%d : %s\n", i, AdrTableau[i] );
    	}
    	free(AdrTableau);
    	return 0;
    }

    Ne te méprends pas. Quand tu écris var="Bonjour" il ne s'agit pas d'une copie de chaine mais d'adresse. Tu ne fais que récupérer l'adresse de la chaine "Bonjour" (située en dur dans ton code) dans la variable var.

    Donc ce code récupère un tableau contenant 4 chaines statiques (non modifiables). Si tu veux changer le code pour récuperer des chaines "modifiable" il te faut alors les recopier depuis les chaines statiques en ayant pris soin auparavant de réserver l'espace pour ces chaines. Ce qui donnera

    Code c : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
     
    char **test(){
    	char **variable;		// Déclaration d'un pointeur double
     
    	variable=malloc(4 * sizeof(char *));
    							// Allocation d'une zone permettant de stocker 4 pointeurs simples
     
    	// Remplissage des 4 pointeurs avec 4 chaines
    	variable[0] = malloc(strlen("3") + 1);	// Ne pas oublier d'allouer l'espace pour le '\0' => La chaine "3" a une taille de "1" mais contient 2 caractères
    	strcpy(variable[0], "3");
    	variable[1] = malloc(strlen("Bonjour") + 1);
    	strcpy(variable[1], "Bonjour");
    	variable[2] = malloc(strlen("moi") + 1);
    	strcpy(variable[2], "moi");
    	variable[3] = malloc(strlen("toi") + 1);
    	strcpy(variable[3], "toi");
     
    	return variable;		// Renvoi du pointeur alloué
    }
     
    int main(){
     
    	char **AdrTableau = test();
    	int i;
    	for(i=0; i<4; i++) {
    		printf("%d : %s\n", i, AdrTableau[i] );
    	}
    	for(i=0; i<4; i++)
    		free(AdrTableau[i]);
    	free(AdrTableau);
     
    	return 0;
    }

    Et tu ne dois pas oublier de libérer la mémoire allouée dans l'ordre inverse de son allocation...

    Citation Envoyé par azimut2000 Voir le message
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
     
        tab = malloc(4*sizeof(char));
        tab = test();
    Sans avoir aucune connaissance ni en C, ni avec les pointeurs, ni même en programmation ; juste avec seulement un peu d'intuition on devrait tiquer sur ces deux lignes qui remplissent deux fois la même variable avec deux éléments différents. D'ailleurs de façon plus générale, on devrait tiquer chaque fois
    • qu'on remplit deux fois une même variable sans l'avoir traitée entre temps
    • qu'on traite une variable sans l'avoir remplie auparavant



    Citation Envoyé par azimut2000 Voir le message
    A noter que la première valeur est un int ("3" que j'ai inclus dans le tableau char... bof, bof...) puis les autres phrases sont bien des Strings.
    Parce tu penses qu'il y a une différence fondamentale entre "3" (une suite de "n" caractère ascii entre quotes) et "Bonjour" (une suite de "n" caractères ascii entre quotes) ? Ou que le C est assez intelligent pour "comprendre" que puisque l'ensemble de caractères représente un nombre il doit alors transformer cette suite en nombre ???
    Mon Tutoriel sur la programmation «Python»
    Mon Tutoriel sur la programmation «Shell»
    Sinon il y en a pleins d'autres. N'oubliez pas non plus les différentes faq disponibles sur ce site
    Et on poste ses codes entre balises [code] et [/code]

  7. #7
    Expert éminent
    Avatar de Médinoc
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Septembre 2005
    Messages
    27 394
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 41
    Localisation : France

    Informations professionnelles :
    Activité : Développeur informatique
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Septembre 2005
    Messages : 27 394
    Par défaut
    Le gros problème ici, c'est function returns address of local variable.
    Il ne faut pas oublier que les tableaux "statiques" sont passés et retournés par adresse et non pas par valeur (il en serait différemment si le tableau était dans une structure).
    Résultat, tu retournes l'adresse d'une variable qui n'existe plus une fois que la fonction retourne!

    Les seuls moyens que je connaisse pour retourner un nouveau tableau d'une fonction, sont l'allocation dynamique et le fait de mettre le tableau dans une structure.
    SVP, pas de questions techniques par MP. Surtout si je ne vous ai jamais parlé avant.

    "Aw, come on, who would be so stupid as to insert a cast to make an error go away without actually fixing the error?"
    Apparently everyone.
    -- Raymond Chen.
    Traduction obligatoire: "Oh, voyons, qui serait assez stupide pour mettre un cast pour faire disparaitre un message d'erreur sans vraiment corriger l'erreur?" - Apparemment, tout le monde. -- Raymond Chen.

Discussions similaires

  1. Réponses: 4
    Dernier message: 21/04/2007, 20h02
  2. [J2SE] tableau de string
    Par Jules82 dans le forum Collection et Stream
    Réponses: 3
    Dernier message: 17/03/2005, 14h35
  3. Ranger un tableau de String dans ma HashMap
    Par jeyce dans le forum Collection et Stream
    Réponses: 3
    Dernier message: 26/08/2004, 22h41
  4. [Collections] Conversion de Vector en tableau de String
    Par java_math dans le forum Collection et Stream
    Réponses: 5
    Dernier message: 06/06/2004, 12h55
  5. [Collections] Tableau de String
    Par gexti dans le forum Collection et Stream
    Réponses: 11
    Dernier message: 02/06/2004, 15h42

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo