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 :

exportation via pointeur de données locales


Sujet :

C

Vue hybride

Message précédent Message précédent   Message suivant Message suivant
  1. #1
    Membre confirmé
    Profil pro
    amateur
    Inscrit en
    Avril 2012
    Messages
    145
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations professionnelles :
    Activité : amateur
    Secteur : Arts - Culture

    Informations forums :
    Inscription : Avril 2012
    Messages : 145
    Par défaut exportation via pointeur de données locales
    Bonjour,

    Voilà : je ne pige pas du tout comment ça fonctionne, ou pas. Ca ressemble un peu à la discussion à propos de l'exportation de chaînes constantes, mais là le cas est plus clair.

    (Je me demandais au départ si c'était vivable en pratique, comme on m'a recommandé, de ne pas définir "par référence" un type viariable ; donc un type dont les instances seront passées explicitement par références à nombre des routines qui s'occuppent de lui. Ce qui veut dire qu'on a des '*' et des '&' à gauche et à droite, et qu'il faut bien faire gaffe quand une variable dénote directement, ou au contraire un pointeur sur, une entité de ce type. Je voulais faire un essai exemplaire, qui commence avec la déf de routines pour créer des instances de ce type. Et ça commence ainsi : )
    Voilà trois petites routines pour créer des spécimens de type S et un test qui affiche leur contenu:

    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
    27
    28
    29
    typedef struct { uint i ; } S ;
     
    S s_make (uint i ) {
       return (S) {i} ;
    }
     
    S * s_new1 (uint i ) {
       S s = {i} ;
       return & s ;
    }
     
    S * s_new2 (uint i ) {
       return & ((S) {i}) ;
    }
     
    void test_make () {
       puts ("=== test ========================================================") ;
     
       S s = s_make (0) ;
       printf ("s : (i:%u)\n", s.i) ;
     
       S * ps1 = s_new1 (1) ;
       printf ("ps1 --> (i:%u)\n", ps1->i) ;
     
       S * ps2 = s_new2 (2) ;
       printf ("ps2 --> (i:%u)\n", ps2->i) ;
     
       end_test () ;
    }
    (Il y a aussi main() qui appelle test() qui appelle test_make().)

    Résultat: C a l'air d'accord ! Voilà :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    === test ========================================================
    s : (i:0)
    ps1 --> (i:1)
    ps2 --> (i:2)
    =================================================================
    Alors, questions:

    1. gcc m'affiche un warning à propos de s_new1 parce que j'échappe une donnée locale:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
       a.c:13:4: warning: function returns address of local variable [enabled by default]
    et il a raison ! mais il ne dit rien à propos de s_new_2 qui fait la même chose. Comment interprétez vous cela ? [La commande de compilation inclut "-Wall -Wextra".]

    2. Selon moi, ni s_new_1, ni s_new2, ne devraient marcher : elle retournent toutes les deux un pointeur sur une donnée locale. Ca ne veut rien dire, je devrais avoir un segfault ou qq chose comme ça. Et pourtant, ça marche !
    Qu'est-ce que je ne pige pas ?

    Valgrind (précisément l'outil 'memcheck') lui me dit bien que je transgresse une règle, là. Voilà son commentaire:

    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
     
    === test ========================================================
    s : (i:1)
    ==9275== Invalid read of size 4
    ==9275==    at 0x804922E: test_make (a.c:33)
    ==9275==    by 0x804927C: test (a.c:44)
    ==9275==    by 0x8049295: main (a.c:49)
    ==9275==  Address 0xbe922254 is just below the stack ptr.  To suppress, use: --workaround-gcc296-bugs=yes
    ==9275== 
    ps --> (i:1)
    ==9275== Invalid read of size 4
    ==9275==    at 0x8049253: test_make (a.c:36)
    ==9275==    by 0x804927C: test (a.c:44)
    ==9275==    by 0x8049295: main (a.c:49)
    ==9275==  Address 0xbe922254 is just below the stack ptr.  To suppress, use: --workaround-gcc296-bugs=yes
    ==9275== 
    ps --> (i:2)
    =================================================================
    (a.c:33) et (a.c:36) sont les seconde et troisième lignes de printf. C'est exactement ça, en fait, je crois : je lis des données qui viennent de disparaître de la pile. Elles n'ont pas encore été écrasées, peut-être, mais elles sont en-dehors de la zone dans maquelle mon prog devrait être autorisé à lire, non ?
    Je ne comprends plus grand chose...

    Merci,
    Denis

  2. #2
    Membre émérite Avatar de |PaRa-BoL
    Profil pro
    Inscrit en
    Novembre 2003
    Messages
    738
    Détails du profil
    Informations personnelles :
    Âge : 38
    Localisation : France

    Informations forums :
    Inscription : Novembre 2003
    Messages : 738
    Par défaut
    Pas de crash car tu as tout a fait le droit de lire a cette adresse (qui fait partie de la pile).
    La valeur a cette adresse n'a pas encore été écrasée. En revanche c'est un comportement indéfini. Ce qui fonctionne maintenant ne fonctionnera pas plus tard

  3. #3
    Membre prolifique
    Avatar de Sve@r
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Février 2006
    Messages
    12 841
    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 841
    Billets dans le blog
    1
    Par défaut
    Citation Envoyé par denispir Voir le message
    Bonjour,

    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
    27
    28
    29
    typedef struct { uint i ; } S ;
     
    S s_make (uint i ) {
       return (S) {i} ;
    }
     
    S * s_new1 (uint i ) {
       S s = {i} ;
       return & s ;
    }
     
    S * s_new2 (uint i ) {
       return & ((S) {i}) ;
    }
     
    void test_make () {
       puts ("=== test ========================================================") ;
     
       S s = s_make (0) ;
       printf ("s : (i:%u)\n", s.i) ;
     
       S * ps1 = s_new1 (1) ;
       printf ("ps1 --> (i:%u)\n", ps1->i) ;
     
       S * ps2 = s_new2 (2) ;
       printf ("ps2 --> (i:%u)\n", ps2->i) ;
     
       end_test () ;
    }
    Alors, questions:

    1. gcc m'affiche un warning à propos de s_new1 parce que j'échappe une donnée locale:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
       a.c:13:4: warning: function returns address of local variable [enabled by default]
    et il a raison ! mais il ne dit rien à propos de s_new_2 qui fait la même chose. Comment interprétez vous cela ? [La commande de compilation inclut "-Wall -Wextra".]
    Salut
    s_new1 renvoie l'adresse d'une variable définie dans le bloc (donc locale). Et donc au moment où tu récupères cette adresse dans la fonction appelante, le contenu de la case qui s'y trouve n'est pas garanti.
    En revanche, s_new2 renvoie l'adresse du paramètre qui lui a été passé mais qui n'est pas dans le bloc. Donc c'est là aussi une variable locale (enfin il me semble) mais probablement que le compilo n'a pas été prévu pour détecter et avertir ce cas de figure...

    Citation Envoyé par denispir Voir le message
    2. Selon moi, ni s_new_1, ni s_new2, ne devraient marcher : elle retournent toutes les deux un pointeur sur une donnée locale. Ca ne veut rien dire, je devrais avoir un segfault ou qq chose comme ça. Et pourtant, ça marche !
    Qu'est-ce que je ne pige pas ?
    C'est le principe du "comportement indéterminé". Le terme "indéterminé" signifie que tu ne peux pas présumer du fonctionnement qui peut avoir toute la gamme de résultats de "ça marche bien (ou ça semble marcher bien)" jusqu'à "ça plante avec un segfault". Généralement on est heureux d'avoir un segfault car au-moins on sait qu'il y a un pb. Parce que quand ça marche, ça peut marcher pendant 6 mois jusqu'à ce qu'on rajoute un printf() dans une fonction située à dache et là ça plante. Mais là, on galère comme un malade à essayer de comprendre pourquoi printf() fait planter...

    Citation Envoyé par denispir Voir le message
    Valgrind (précisément l'outil 'memcheck') lui me dit bien que je transgresse une règle, là. Voilà son commentaire:

    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
     
    === test ========================================================
    s : (i:1)
    ==9275== Invalid read of size 4
    ==9275==    at 0x804922E: test_make (a.c:33)
    ==9275==    by 0x804927C: test (a.c:44)
    ==9275==    by 0x8049295: main (a.c:49)
    ==9275==  Address 0xbe922254 is just below the stack ptr.  To suppress, use: --workaround-gcc296-bugs=yes
    ==9275== 
    ps --> (i:1)
    ==9275== Invalid read of size 4
    ==9275==    at 0x8049253: test_make (a.c:36)
    ==9275==    by 0x804927C: test (a.c:44)
    ==9275==    by 0x8049295: main (a.c:49)
    ==9275==  Address 0xbe922254 is just below the stack ptr.  To suppress, use: --workaround-gcc296-bugs=yes
    ==9275== 
    ps --> (i:2)
    =================================================================
    (a.c:33) et (a.c:36) sont les seconde et troisième lignes de printf. C'est exactement ça, en fait, je crois : je lis des données qui viennent de disparaître de la pile. Elles n'ont pas encore été écrasées, peut-être, mais elles sont en-dehors de la zone dans maquelle mon prog devrait être autorisé à lire, non ?
    Je ne comprends plus grand chose...
    La seule chose à comprendre, c'est que "indéterminé" signifie "tu ne peux pas prévoir le résultat"...

    Citation Envoyé par |PaRa-BoL Voir le message
    Pas de crash car tu as tout a fait le droit de lire a cette adresse (qui fait partie de la pile).
    La valeur a cette adresse n'a pas encore été écrasée. En revanche c'est un comportement indéfini. Ce qui fonctionne maintenant ne fonctionnera pas plus tard
    Hum, je ne suis pas certain qu'on ait le droit de lire à cet endroit. Une fois à mes débuts j'avais fait une fonction qui, par flemme, renvoyait l'adresse d'une locale. Je n'utilisais cette adresse qu'au retourde la fonction et pas ailleurs donc je me disais "ok, elle n'a pas encore été écrasée". Ben ça a marché tant que j'étais sur ma machine de l'époque (une sun). Puis je suis passé à Linux et là ben mon programme a planté...
    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]

  4. #4
    Membre confirmé
    Profil pro
    amateur
    Inscrit en
    Avril 2012
    Messages
    145
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations professionnelles :
    Activité : amateur
    Secteur : Arts - Culture

    Informations forums :
    Inscription : Avril 2012
    Messages : 145
    Par défaut
    Citation Envoyé par Sve@r Voir le message
    Salut
    s_new1 renvoie l'adresse d'une variable définie dans le bloc (donc locale). Et donc au moment où tu récupères cette adresse dans la fonction appelante, le contenu de la case qui s'y trouve n'est pas garanti.
    En revanche, s_new2 renvoie l'adresse du paramètre qui lui a été passé mais qui n'est pas dans le bloc. Donc c'est là aussi une variable locale (enfin il me semble) mais probablement que le compilo n'a pas été prévu pour détecter et avertir ce cas de figure...
    Oui, à propos de s_new2, même si je renvoyais directement l'adresse du param (i), ce serait de toute façon une donnée locale, vu que passée par valeur et donc copiée. En fait, s_new_2 crée une nouvelle donnée, de type S, exactement comme s_new1, simplement il ne la nomme pas. Seule la syntaxe est différente de mon point de vue, et d'ailleurs le compilo réécrit sans doute les deux de la même façon (la plus efficiente, avec ou sans var locale correspondant à 's').

    C'est le principe du "comportement indéterminé". Le terme "indéterminé" signifie que tu ne peux pas présumer du fonctionnement qui peut avoir toute la gamme de résultats de "ça marche bien (ou ça semble marcher bien)" jusqu'à "ça plante avec un segfault". Généralement on est heureux d'avoir un segfault car au-moins on sait qu'il y a un pb. Parce que quand ça marche, ça peut marcher pendant 6 mois jusqu'à ce qu'on rajoute un printf() dans une fonction située à dache et là ça plante. Mais là, on galère comme un malade à essayer de comprendre pourquoi printf() fait planter...

    La seule chose à comprendre, c'est que "indéterminé" signifie "tu ne peux pas prévoir le résultat"...

    [...]

    Hum, je ne suis pas certain qu'on ait le droit de lire à cet endroit. Une fois à mes débuts j'avais fait une fonction qui, par flemme, renvoyait l'adresse d'une locale. Je n'utilisais cette adresse qu'au retour de la fonction et pas ailleurs donc je me disais "ok, elle n'a pas encore été écrasée". Ben ça a marché tant que j'étais sur ma machine de l'époque (une sun). Puis je suis passé à Linux et là ben mon programme a planté...
    T'as eu de la chance qu'il plante

    Je voudrais être certain de la chose suivante : du point de vue de la sémantique du langage (C en général), mon code est faux (dans les 2 s_new*), point ; et ça ne fonctionne que du fait que l'implantation particulière par gcc le permet. Et encore peut-être seulement parce que l'utilisation des valeurs de type S est très proche dans le flux du programme de leur création en tant que variables locales dans les fonctions applées.
    Donc : résumé : on fait pas ça, point.
    Mais j'aimerais bien que gcc me balance un warning, au moins, dans tous les cas de figure correspondant à cette erreur-là. Valgrind le dit, cool, mais c'est un outil plus lourd et donc que je n'utilise qu'en fin de cycle de développement, lorsque tout marche, y compris les tests (et d'ailleurs presque toutes les erreurs qu'il détecte sont de soublis de libération mémoire dans les tests, dûs en partie au fait que ceux-ci, en tant que tests, n'utilisent pas toujours le code qu'ils contrôlent d'une façon standard).

    Merci,
    denis

  5. #5
    Membre prolifique
    Avatar de Sve@r
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Février 2006
    Messages
    12 841
    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 841
    Billets dans le blog
    1
    Par défaut
    Citation Envoyé par denispir Voir le message
    T'as eu de la chance qu'il plante
    Il aurait pu aussi fonctionner. Mais de toute façon je savais qu'il était bancal donc quand il a planté, j'ai su de suite où chercher...

    Citation Envoyé par denispir Voir le message
    ...Mais j'aimerais bien que gcc me balance un warning, au moins, dans tous les cas de figure correspondant à cette erreur-là.
    Moi j'aimerais bien gagner à l'euromillion. Donc chacun ses souhaits quoi...

    Citation Envoyé par denispir Voir le message
    Valgrind le dit, cool, mais c'est un outil plus lourd...
    T'as compris. Plus l'outil fait de choses, plus il est lourd. Quoi qu'il en soit, aucun outil ne remplacera ton cerveau. Tu l'as d'ailleurs bien compris car tu le dis toi-même: quand tu codes ton appli tu codes naturellement assez rigoureusement pour n'avoir pas de remontées de valgrind. Donc voilà quoi. Laisse le compilo faire le travail pour lequel on le paye (analyser la syntaxe et créer le module objet) et utilise ta tête pour ne pas avoir de bug dans ton code
    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]

  6. #6
    Expert éminent
    Avatar de Médinoc
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Septembre 2005
    Messages
    27 397
    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 397
    Par défaut
    Je suis surpris que s_new2 compile. Le résultat d'un cast n'est-il pas supposé être une rvalue?
    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.

  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
    Citation Envoyé par Médinoc Voir le message
    Je suis surpris que s_new2 compile. Le résultat d'un cast n'est-il pas supposé être une rvalue?
    Mais, il ne s'agit pas ici d'un cast (dont le résultat n'est effectivement pas une lvalue) mais d'un objet non nommé du C99
    Cf n1256 : 6.5.2.5 Compound literals

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

Discussions similaires

  1. exporter une base de données via script php
    Par DimitriLille dans le forum Langage
    Réponses: 3
    Dernier message: 04/06/2014, 09h31
  2. Base de données locale
    Par Salam59 dans le forum Décisions SGBD
    Réponses: 5
    Dernier message: 07/06/2005, 15h29
  3. aide pour exporter une base de donnée
    Par matt55 dans le forum PostgreSQL
    Réponses: 8
    Dernier message: 06/04/2004, 14h28
  4. Pbs Base de donnée locale!
    Par danuz dans le forum InterBase
    Réponses: 2
    Dernier message: 11/08/2003, 14h36
  5. Synchronisation de base de données locale/distante Internet
    Par StefC30 dans le forum Développement
    Réponses: 3
    Dernier message: 25/07/2003, 14h47

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