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 :

Variable globale locale erreur ?


Sujet :

C

  1. #1
    Membre expérimenté
    Inscrit en
    Mai 2002
    Messages
    251
    Détails du profil
    Informations forums :
    Inscription : Mai 2002
    Messages : 251
    Par défaut Variable globale locale erreur ?
    Bonjour,

    Pourriez-vous me dire pourquoi ce code focntionne :
    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
     
    int main()
     
    {
     
    char toto[50][50];
     
    void *tc = &toto;
    char **tutu = tc;
     
    strcpy((tutu[0]), "Test");
     
    return 0;
     
    }
    Alors que ce code plante :

    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
     
    char toto[50][50];
     
    int main()
     
    {
     
    void *tc = &toto;
    char **tutu = tc;
     
    strcpy((tutu[0]), "Test");
     
    return 0;
     
    }
    Je vous remercie.

  2. #2
    Membre Expert
    Avatar de kwariz
    Homme Profil pro
    Chef de projet en SSII
    Inscrit en
    Octobre 2011
    Messages
    898
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 52
    Localisation : France, Moselle (Lorraine)

    Informations professionnelles :
    Activité : Chef de projet en SSII
    Secteur : Conseil

    Informations forums :
    Inscription : Octobre 2011
    Messages : 898
    Par défaut
    Salut,

    donne plus de précision ... environnement, compilo, option de compilation ...

    Si ça peut te rassurer les deux codes segfaultent chez moi.

  3. #3
    Modérateur
    Avatar de Obsidian
    Homme Profil pro
    Développeur en systèmes embarqués
    Inscrit en
    Septembre 2007
    Messages
    7 432
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 48
    Localisation : France, Essonne (Île de France)

    Informations professionnelles :
    Activité : Développeur en systèmes embarqués
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Septembre 2007
    Messages : 7 432
    Par défaut
    Bonjour,

    Les deux sont incorrects, mais l'un des deux doit « tomber en marche », comme on dit.

    Un tableau à deux dimensions n'est pas un tableau de pointeurs, et un tableau unidimensionnel n'est pas doté non plus d'un pointeur additionnel en mémoire. Lorsque tu écris « char toto [50][50] », sachant qu'un char mesure un byte par définition et qu'un byte correspond pratiquement toujours à un octet sur les machines courantes, tu alloues exactement 2500 octets contigus en mémoire. Pas un de plus, pas un de moins.

    Ce sont les expressions qui se réfèrent aux différentes entrées du tableau qui sont résolues en l'adresse de l'entrée sélectionnée par les deux index, soit à l'exécution, soit directement par le compilateur.

    Seulement, si tu castes cela en « char ** », tu transtypes cette adresse en pointeur sur un pointeur sur un char. En faisant tutu[0], tu déréférences tutu et le compilateur s'attend à trouver derrière… un autre pointeur qui, lui, est censé contenir l'adresse de ton buffer. Et bien sûr, ce n'est pas le cas : tutu[0] pointe le début de ton tableau, dont le contenu est indéterminé.

    L'expression est donc valide mais le résultat que tu obtiens n'a aucun sens, et ton pointeur pointe n'importe où.

    Maintenant, pour pousser le diagnostic encore un peu plus loin, l'un de tes tableaux est déclaré dans la pile, l'autre dans le tas. Ceci suffit déjà à faire varier les effets de bords, mais le plus probable est que le code qui fonctionne est celui dont le tableau est déclaré en local à l'intérieur de ta fonction et qui disparaîtra donc avec elle : le compilateur doit faire des optimisations en se rendant compte que tu ne lis jamais ton tableau avant la fin de ta fonction et qu'il est inutile de le remplir. Donc, le bug n'aura pas lieu. En revanche, il ne peut faire la même hypothèse avec un tableau en variable globale, d'autant que celle-ci peut éventuellement être lue par un code extérieur à ton module. Il est donc obligé d'honorer le contenu de ta fonction et le plantage se produit.

  4. #4
    Membre émérite
    Avatar de bpy1401
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Mars 2003
    Messages
    511
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 64
    Localisation : France, Eure (Haute Normandie)

    Informations professionnelles :
    Activité : Ingénieur développement logiciels
    Secteur : Industrie

    Informations forums :
    Inscription : Mars 2003
    Messages : 511
    Par défaut
    Bonjour

    Obsidian à raison, ton code est faux. Dans le premier cas tu va planter car tu jardinne dans la pile. Dans le deuxième cas, tu jardinne dans la mémoire dite globale. Comme ta pile sert aussi à sauvegarder des valeurs de registre et adresse de retour, tu casse toutes ces informations, et donc plantage. En globale, tu risque uniquement de détruire tes données.
    Page sur Developpez : http://pbriand.developpez.com

  5. #5
    Expert confirmé
    Avatar de diogene
    Homme Profil pro
    Enseignant Chercheur
    Inscrit en
    Juin 2005
    Messages
    5 761
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Essonne (Île de France)

    Informations professionnelles :
    Activité : Enseignant Chercheur
    Secteur : Enseignement

    Informations forums :
    Inscription : Juin 2005
    Messages : 5 761
    Par défaut
    Pour poursuivre l'explication d'Obsidian, quelle est la différence que fait le C entre écrire
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    strcpy((tutu[0]), "Test");
    // et
    strcpy((toto[0]), "Test");
    avec pour mémoire
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    char toto[50][50];
    void *tc = &toto;
    char **tutu = tc;
    Même si les adresses correspondant à toto et tutu semblent les mêmes, leur type différent a des conséquences

    - tutu est un pointeur, alors tutu[0] est le contenu de l'adresse tutu. En pratique, cela correspondra au contenu des premiers bytes du tableau toto interprétés comme une adresse (et comme ce n'est pas une adresse légale, on écrira n'importe où).

    - Alors que toto[0] est un tableau. Alors toto[0] désigne ici l'adresse du début du tableau, et non pas le contenu du début du tableau.

  6. #6
    Membre expérimenté
    Inscrit en
    Mai 2002
    Messages
    251
    Détails du profil
    Informations forums :
    Inscription : Mai 2002
    Messages : 251
    Par défaut
    Bonjour,

    Merci à vous pour ces réponses.

    En réalité j'essayais d'implémenter une fonction liée à SQLITE.

    J'appelle cette fonction depuis mon main :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    sqlite3_exec(db, pSQL[0], callback_user2, pbla, &zErrMsg);
    La 4ème variable (pbla) sera récupérée dans la fonction callback_user2 dans le pointeur void* NotUsed.

    callback_user2 est une fonction :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    static int callback_user(void *NotUsed, int argc, char **argv, char **azColName)
    Je n'ai pas la main sur squilite3_exec pour surcharger callback_user2 car elle est définie dans une librairie.

    pbla est en fait un pointeur sur un tableau a deux dimensions.

    Je dois donc transtyper mon tableau
    char bla[50][50]
    en void*.
    Pour cela, j'ai pensé faire cette opération :
    void* pbla = &bla;
    Par contre, une fois dans la fonction,
    pbla
    est devenu
    void* NotUsed
    , je ne sais plus comment utiliser mon tableau .

    C'est pourquoi j'ai pensé réaliser l'opération que je vous est décrit ci-dessus.

    Savez-vous comment je peux implémenter ?

    Je vous remercie.

  7. #7
    Expert confirmé
    Avatar de diogene
    Homme Profil pro
    Enseignant Chercheur
    Inscrit en
    Juin 2005
    Messages
    5 761
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Essonne (Île de France)

    Informations professionnelles :
    Activité : Enseignant Chercheur
    Secteur : Enseignement

    Informations forums :
    Inscription : Juin 2005
    Messages : 5 761
    Par défaut
    Tu veux passer comme 4ieme argument à sqlite3_exec() le tableau bla là où est attendu un void *. Il suffit d'avoir :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    sqlite3_exec(db, pSQL[0], callback_user2, bla, &zErrMsg);
    La valeur passée est alors l'adresse du début du tableau bla, de type "adresse d'un tableau de 50 char" , char (*)[50], transtypée implicitement en void*, le type de l'argument attendu.

    Cette valeur est récupérée par callback_user2() dans le paramètre NotUsed de type void *. Pour utiliser ce paramètre, il faut commencer par rétablir son type exact :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    char (*tab)[50] = NotUsed;
    // on accède aux éléments du tableau comme d'habitude par tab[i][j]

  8. #8
    Membre expérimenté
    Inscrit en
    Mai 2002
    Messages
    251
    Détails du profil
    Informations forums :
    Inscription : Mai 2002
    Messages : 251
    Par défaut
    Citation Envoyé par diogene Voir le message
    Tu veux passer comme 4ieme argument à sqlite3_exec() le tableau bla là où est attendu un void *. Il suffit d'avoir :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    sqlite3_exec(db, pSQL[0], callback_user2, bla, &zErrMsg);
    La valeur passée est alors l'adresse du début du tableau bla, de type "adresse d'un tableau de 50 char" , char (*)[50], transtypée implicitement en void*, le type de l'argument attendu.

    Cette valeur est récupérée par callback_user2() dans le paramètre NotUsed de type void *. Pour utiliser ce paramètre, il faut commencer par rétablir son type exact :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    char (*tab)[50] = NotUsed;
    // on accède aux éléments du tableau comme d'habitude par tab[i][j]
    Le code ne semble pas fonctionner :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    main.cpp: In function `int callback_user2(void*, int, char**, char**)':
    main.cpp:61: error: invalid conversion from `void*' to `char (*)[50]'
    J'ai également essayé :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    char (*tab)[50][50] = NotUsed;
    merci.

  9. #9
    Expert confirmé
    Avatar de diogene
    Homme Profil pro
    Enseignant Chercheur
    Inscrit en
    Juin 2005
    Messages
    5 761
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Essonne (Île de France)

    Informations professionnelles :
    Activité : Enseignant Chercheur
    Secteur : Enseignement

    Informations forums :
    Inscription : Juin 2005
    Messages : 5 761
    Par défaut
    Le code ne semble pas fonctionner :
    Tu compiles en C++, pas en C.
    En C++, les conversions avec void* ne sont pas implicites et il faut mettre un opérateur de cast.

    J'ai également essayé :
    Il ne faut pas faire des choses au hasard jusqu'à ce qu'on croit que ça marche

  10. #10
    Membre expérimenté
    Inscrit en
    Mai 2002
    Messages
    251
    Détails du profil
    Informations forums :
    Inscription : Mai 2002
    Messages : 251
    Par défaut
    Citation Envoyé par diogene Voir le message
    Tu compiles en C++, pas en C.
    En C++, les conversions avec void* ne sont pas implicites et il faut mettre un opérateur de cast.

    Il ne faut pas faire des choses au hasard jusqu'à ce qu'on croit que ça marche

    Oui, je vois que je les cumules en fait. Mon devcpp a créé le projet SQLITE en cpp et non en c. Je n'ai pas fait attention.

    Sur le principe, ce développement me permets de mettre en application, des concepts lus dans des livres de c et c++. Les bouquins pour les nuls survolent, les autres sont trop compliqués sans explications oral ou pratique.
    J'essaye donc, pour voir le résultat, voir quel message j'obtiens.

    En tout cas, merci. Ces explications liées à un cas pratique et compréhensible pour moi (même si je vais mettre un petit moment pour comprendre l'explication d'Obsidian) est très instructif.

+ Répondre à la discussion
Cette discussion est résolue.

Discussions similaires

  1. variables globales/locales et pointeurs.
    Par fiofiotte dans le forum C
    Réponses: 11
    Dernier message: 19/11/2010, 22h58
  2. Variables Global/Local Serveur/Navigateur
    Par rednight dans le forum WebDev
    Réponses: 1
    Dernier message: 20/11/2009, 16h02
  3. Problème de variables globales / locales
    Par onigami dans le forum Général Java
    Réponses: 9
    Dernier message: 06/03/2008, 14h01
  4. variable globale ou locale pour CurrentDB
    Par jibouze dans le forum VBA Access
    Réponses: 7
    Dernier message: 30/05/2006, 17h15
  5. Erreur "For loop variable simple local varaible"
    Par Yepazix dans le forum Langage
    Réponses: 13
    Dernier message: 17/09/2005, 22h09

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