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 :

Masquage de pointeur pour un type abstrait de donnée


Sujet :

C

  1. #1
    Membre expert
    Avatar de Pragmateek
    Homme Profil pro
    Formateur expert .Net/C#
    Inscrit en
    Mars 2006
    Messages
    2 635
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 37
    Localisation : France, Val de Marne (Île de France)

    Informations professionnelles :
    Activité : Formateur expert .Net/C#
    Secteur : Conseil

    Informations forums :
    Inscription : Mars 2006
    Messages : 2 635
    Points : 3 958
    Points
    3 958
    Par défaut Masquage de pointeur pour un type abstrait de donnée
    Bonjour,

    parfois des types abstraits de donnée sont définis ainsi :
    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
    typedef struct
    {
            int n;
    }
    *Integer;
     
    Integer newInteger(int n)
    {
            Integer i = malloc(sizeof(*i));
            i->n = n;
            return i;
    }
     
    void deleteInteger(Integer i)
    {
            free(i);
    }
     
    int getValue(Integer i)
    {
            return i->n;
    }
    et est utilisé ainsi :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
     
    Integer i = newInteger(1);
    printf("%i\n", getValue(i));
    deleteInteger(i);
    Or il a été signalé à plusieures reprises sur ce forum que masquer un pointeur n'était pas une bonne pratique .

    Plusieures questions :
    1) Pourriez vous donner des exemples de problèmes générés par cette pratique dans le cas général, c'est à dire si l'utilisateur du type abstrait manipule directement celui ci sans passer par les fonctions qui lui sont associées.
    2) Dans le cas où l'utilisateur utilise les fonctions fournies et uniquement celles-ci pour :
    - construire le type abstrait
    - manipuler le type abstrait
    - effacer le type abstrait
    peut il y avoir encore des problèmes, si oui, lesquels ?

    Merci .
    Formateur expert .Net/C#/WPF/EF Certifié MCP disponible sur Paris, province et pays limitrophes (enseignement en français uniquement).
    Mon blog : pragmateek.com

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

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

    Informations forums :
    Inscription : Septembre 2005
    Messages : 27 369
    Points : 41 519
    Points
    41 519
    Par défaut
    Eh bien, coté "utilisateur", c'est surtout que pour un TAD simple comme Integer, on peut facilement oublier d'appeler deleteInteger().

    Mais surtout, les problèmes sont au niveau du code du TAD:
    Et aussi, c'est moins lisible dans le code même du TAD (pas trop dans son utilisation).

    Il m'arrive de faire des Typedefs pointeurs, mais je ne "cache" jamais le pointeur dedans: En lisant le type, on se doute facilement que c'est un pointeur.
    Code C : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    typedef struct
    {
    	int n;
    } Integer, *PInteger;
    typedef const Integer * PCInteger;
    Tu remarqueras aussi que j'ai deux types pointeurs différents: L'un const, l'autre pas.

    Sans parler des problèmes liés aux typedefs "arbre" et "liste" définis comme de bêtes pointeurs vers des structures qui seraient mieux nommées "noeud" et "chaînon"...
    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.

  3. #3
    Expert éminent
    Avatar de Melem
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Janvier 2006
    Messages
    3 656
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 38
    Localisation : France, Essonne (Île de France)

    Informations professionnelles :
    Activité : Ingénieur développement logiciels
    Secteur : High Tech - Électronique et micro-électronique

    Informations forums :
    Inscription : Janvier 2006
    Messages : 3 656
    Points : 8 389
    Points
    8 389
    Par défaut
    Et comment tu déclares les fonctions? D'abord ta structure elle est définie dans un .c ou dans un .h? En principe, on définit la structure et les fonctions associées dans un fichier .c et on met les déclarations dans un fichier .h.

    Dans le fichier .c on devrait donc avoir quelque chose ressemblant à ceci :

    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 <stdlib.h>
     
    struct integer
    {
            int n;
    };
     
    typedef struct integer * Integer;
     
    Integer newInteger(int n)
    {
            Integer i = malloc(sizeof(*i));
            i->n = n;
            return i;
    }
     
    void deleteInteger(Integer i)
    {
            free(i);
    }
     
    int getValue(Integer i)
    {
            return i->n;
    }
    Et dans le .h :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
     
    #ifndef H_INTEGER
     
    #définie H_INTEGER
     
    typedef struct integer * Integer;
     
    Integer newInteger(int n);
    void deleteInteger(Integer i);
    int getValue(Integer i);
     
    #endif
    Pourriez vous donner des exemples de problèmes générés par cette pratique dans le cas général, c'est à dire si l'utilisateur du type abstrait manipule directement celui ci sans passer par les fonctions qui lui sont associées.
    En principe il ne peut pas puisque la définition de la structure est cachée. Si dans un fichier a.c on a :
    et dans a.h :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
     
    ...
    struct A;
    ...
    On ne peut pas faire :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    #include "a.h"
    ...
    struct A a;
    ...
    Car les détails de la structure ne sont pas connus (donc sa taille non plus). Par contre on peut utiliser un pointeur :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    #include "a.h"
    ...
    struct A * p;
    ...
    mais on ne pourra jamais déréférencer le pointeur (c'est-à-dire faire *p) ...

    Ce ne serait cependant pas le cas si on avait défini la structure dans a.h.

    Dans le cas où l'utilisateur utilise les fonctions fournies et uniquement celles-ci pour :
    - construire le type abstrait
    - manipuler le type abstrait
    - effacer le type abstrait
    peut il y avoir encore des problèmes, si oui, lesquels ?
    Pas très clair. S'il y a des problèmes ben c'est tout simplement parce que t'as mal codé tes fonctions sinon je ne vois pas.

    Et concernant la question masquer ou pas masquer le pointeur je crois que c'est plutôt une question de goût ou de point de vue personnel.

    Citation Envoyé par Médinoc
    Eh bien, c'est surtout que pour un TAD simple comme Integer, on peut facilement oublier d'appeler deleteInteger().
    Après un malloc il faut quand même un free ...

  4. #4
    Expert éminent sénior
    Avatar de Emmanuel Delahaye
    Profil pro
    Retraité
    Inscrit en
    Décembre 2003
    Messages
    14 512
    Détails du profil
    Informations personnelles :
    Âge : 67
    Localisation : France, Paris (Île de France)

    Informations professionnelles :
    Activité : Retraité

    Informations forums :
    Inscription : Décembre 2003
    Messages : 14 512
    Points : 20 985
    Points
    20 985
    Par défaut
    Citation Envoyé par seriousme Voir le message
    Or il a été signalé à plusieures reprises sur ce forum que masquer un pointeur n'était pas une bonne pratique .

    Plusieures questions :
    1) Pourriez vous donner des exemples de problèmes générés par cette pratique dans le cas général, c'est à dire si l'utilisateur du type abstrait manipule directement celui ci sans passer par les fonctions qui lui sont associées.
    Impossible. C'est le principe du TAD Les données ne sont pas accessibles de l'extérieur. L'usage des 'accesseurs' est obligatoire.

    Nota : Ceci
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
     
    typedef struct
    {
            int n;
    }
    *Integer;
    N'est pas un TAD. Il faut séparer l'interface de l'implémentation :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
     
    /* interface (x.h) */
    typedef struct integer *Integer;
     
    /* prototypes des fonctions ... */
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
     
    /* implementation (x.c) */
    #include "x.h"
    struct integer
    {
       int n;
    };
     
    /* fonctions ... */
    2) Dans le cas où l'utilisateur utilise les fonctions fournies et uniquement celles-ci pour :
    - construire le type abstrait
    - manipuler le type abstrait
    - effacer le type abstrait
    peut il y avoir encore des problèmes, si oui, lesquels ?
    http://emmanuel-delahaye.developpez.com/tad.htm
    Pas de Wi-Fi à la maison : CPL

  5. #5
    Membre expert
    Avatar de Pragmateek
    Homme Profil pro
    Formateur expert .Net/C#
    Inscrit en
    Mars 2006
    Messages
    2 635
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 37
    Localisation : France, Val de Marne (Île de France)

    Informations professionnelles :
    Activité : Formateur expert .Net/C#
    Secteur : Conseil

    Informations forums :
    Inscription : Mars 2006
    Messages : 2 635
    Points : 3 958
    Points
    3 958
    Par défaut
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    #ifndef H_INTEGER
     
    typedef struct integer * Integer;
     
    Integer newInteger(int n);
    void deleteInteger(Integer i);
    int getValue(Integer i);
     
    #endif
    plutôt :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    #ifndef H_INTEGER
    #define H_INTEGER 
     
    typedef struct integer * Integer;
     
    Integer newInteger(int n);
    void deleteInteger(Integer i);
    int getValue(Integer i);
     
    #endif
    Impossible. C'est le principe du TAD Les données ne sont pas accessibles de l'extérieur. L'usage des 'accesseurs' est obligatoire.
    En effet donc la question ne se pose même pas .

    Merci à tous .
    Formateur expert .Net/C#/WPF/EF Certifié MCP disponible sur Paris, province et pays limitrophes (enseignement en français uniquement).
    Mon blog : pragmateek.com

  6. #6
    Membre expert
    Avatar de Pragmateek
    Homme Profil pro
    Formateur expert .Net/C#
    Inscrit en
    Mars 2006
    Messages
    2 635
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 37
    Localisation : France, Val de Marne (Île de France)

    Informations professionnelles :
    Activité : Formateur expert .Net/C#
    Secteur : Conseil

    Informations forums :
    Inscription : Mars 2006
    Messages : 2 635
    Points : 3 958
    Points
    3 958
    Par défaut
    Une autre question en lien avec les types abstraits de donnée .
    De la même façon qu'il est possible de définir des méthodes "private" avec le mot clé "static", est-il possible d'exposer publiquement certains attributs d'un type abstrait et pas d'autres afin de séparer attributs "private" et "public" ?

    Peut être :

    Integer.h :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    #ifndef H_INTEGER
    #define H_INTEGER
     
    typedef struct
    {
    	int n;
    }
    *Integer;
     
    Integer newInteger(int n);
    void deleteInteger(Integer i);
    int getValue(Integer i);
     
    #endif
    Integer.c :
    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
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    #include <stdlib.h>
    #include "Integer.h"
     
    typedef struct
    {
    	int n;
    	int private_n;
    }
    integer_private, *Integer_private;
     
    static Integer setPrivateValue(Integer i, int n)
    {
    	((Integer_private)i)->private_n = n;
    	return i;
    }
     
    Integer newInteger(int n)
    {
    	Integer_private i = malloc(sizeof(*i));
    	i->n = n;
    	setPrivateValue((Integer)i, n);
    	return (Integer)i;
    }
     
    Integer setValue(Integer i, int n)
    {
    	i->n = n;
    	return i;
    }
     
    void deleteInteger(Integer i)
    {
    	free(i);
    }
     
    int getValue(Integer i)
    {
    	return i->n;
    }
    main.c :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    #include <stdlib.h>
    #include <stdio.h>
    #include "Integer.h"
     
    int main(void)
    {
    	Integer i = newInteger(1);
    	printf("%i\n", i->n);
    	deleteInteger(i);
    	return EXIT_SUCCESS;
    }
    L'utilisateur du type ne peut ni accéder à la fonction "setPrivateValue" ni à "private_n".
    Ce n'est bien sûr qu'un brouillon .
    Qu'en est il de la portabilité des casts utilisés ?

    Merci .
    Formateur expert .Net/C#/WPF/EF Certifié MCP disponible sur Paris, province et pays limitrophes (enseignement en français uniquement).
    Mon blog : pragmateek.com

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

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

    Informations forums :
    Inscription : Septembre 2005
    Messages : 27 369
    Points : 41 519
    Points
    41 519
    Par défaut
    Houlà, c'est crade.

    À ta place, je supposerais plutôt que non. Si tu veux obtenir un accès direct à un champ, fais une fonction qui retourne un pointeur vers celui-ci...
    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.

  8. #8
    Expert éminent sénior
    Avatar de Emmanuel Delahaye
    Profil pro
    Retraité
    Inscrit en
    Décembre 2003
    Messages
    14 512
    Détails du profil
    Informations personnelles :
    Âge : 67
    Localisation : France, Paris (Île de France)

    Informations professionnelles :
    Activité : Retraité

    Informations forums :
    Inscription : Décembre 2003
    Messages : 14 512
    Points : 20 985
    Points
    20 985
    Par défaut
    Citation Envoyé par seriousme Voir le message
    De la même façon qu'il est possible de définir des méthodes "private" avec le mot clé "static", est-il possible d'exposer publiquement certains attributs d'un type abstrait et pas d'autres afin de séparer attributs "private" et "public" ?
    Les fonctions déclarées dans le .h sont publiques
    Les fonction définies dans le .c avec le mot clé 'static' sont privées.
    Il n'y a pas d'attributs 'publics', toutes les variables sont privées (c'est de bon sens...).
    Il n'y a pas d'équivalent à 'friend'.

    La solution que tu proposes ne fonctionne pas, car 2 structures différentes ne peuvent pas avoir le même nom. Par contre, il est possible de définir une structure publique 'normale' avec des éléments publics dont un ADT (pointeur, obligatoirement) qui cache les données privées.
    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
     
    /* x.h */ 
    #ifndef H_X
    #define H_X
     
    struct x_private;
     
    struct x_public
    {
        int a;
        double b;
    };
     
    struct x
    {
        struct x_public pub;
        struct x_private *priv; /* ADT */
    };
     
    /* prototypes */
     
    struct x *x_create(/* ... */);
    void x_delete(struct x * self);
     
    #endif
    Pas de Wi-Fi à la maison : CPL

  9. #9
    Membre expert
    Avatar de Pragmateek
    Homme Profil pro
    Formateur expert .Net/C#
    Inscrit en
    Mars 2006
    Messages
    2 635
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 37
    Localisation : France, Val de Marne (Île de France)

    Informations professionnelles :
    Activité : Formateur expert .Net/C#
    Secteur : Conseil

    Informations forums :
    Inscription : Mars 2006
    Messages : 2 635
    Points : 3 958
    Points
    3 958
    Par défaut
    La solution que tu proposes ne fonctionne pas, car 2 structures différentes ne peuvent pas avoir le même nom.
    Où ça ?

    En tout cas cela compile ainsi :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    gcc -ansi -pedantic -Wall -Wextra -c Integer.c
    gcc -ansi -pedantic -Wall -Wextra main.c Integer.o
    Formateur expert .Net/C#/WPF/EF Certifié MCP disponible sur Paris, province et pays limitrophes (enseignement en français uniquement).
    Mon blog : pragmateek.com

  10. #10
    Membre averti
    Profil pro
    Inscrit en
    Juillet 2007
    Messages
    308
    Détails du profil
    Informations personnelles :
    Âge : 38
    Localisation : France

    Informations forums :
    Inscription : Juillet 2007
    Messages : 308
    Points : 373
    Points
    373
    Par défaut
    Je tiens à contesté ça... La flexibilité du C grâce aux jeux des casts permet d'obtenir des types "partiels", des objets dont une partie de la structure est publique et l'autre pas...

    C'est peut-être pas très propre, je vais peut-être se faire dresser les cheveux sur la tête des puristes mais c'est possible. Je ne dis pas que c'est une pratique à utiliser, c'est même plutôt dangereux pour l'intégrité des objets, mais il est possible de faire ce genre de trucs, voilà une petite démo :

    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
    // MonType.h
     
    #ifndef MONTYPE_H_
    #define MONTYPE_H_
     
    typedef struct MonType {
    	int public;
    } *MonTypeRef;
     
    MonTypeRef CreateMonType(int value);
    int GetPublic(MonTypeRef o);
    int GetPrivate(MonTypeRef o);
    void FreeMonType(MonTypeRef o);
     
    #endif
    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
    30
    31
    32
    33
    // MonType.c
     
    #include "MonType.h"
    #include <stdlib.h>
     
    typedef struct {
    	int public;
    	int private;
    } MonType;
     
    MonTypeRef CreateMonType(int value)
    {
    	MonType *new = malloc(sizeof(MonType));
    	new->public = value;
    	new->private = value * 2;
     
    	return (MonTypeRef)new;
    }
     
    int GetPublic(MonTypeRef o)
    {
    	return o->public;
    }
     
    int GetPrivate(MonTypeRef o)
    {
    	return ((MonType*)o)->private;
    }
     
    void FreeMonType(MonTypeRef o)
    {
    	free(o);
    }
    Et le petit test tout simple :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    #include <stdio.h>
    #include "MonType.h"
     
    int main (int argc, const char * argv[])
    {
        MonTypeRef obj = CreateMonType(5);
        printf("obj = %d | %d, %d",
               obj->public,
               GetPublic(obj), GetPrivate(obj));
        FreeMonType(obj);
        return 0;
    }
    Ce qui fait qu'on a deux champs dans cet objet, un champ public et un champ privé, et les deux cohabites très bien. Alors c'est pas forcément très propre, mais c'est possible, et il y a plein d'autres systèmes de ce genre-là permettant de masquer les données à différents niveaux.

    Par exemple, masquer un compteur de références avant les données utiles :
    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
    // MonType.h
    #ifndef MONTYPE_H_
    #define MONTYPE_H_
     
    typedef struct MonType {
    	int public;
    } *MonTypeRef;
     
    MonTypeRef CreateMonType(int value);
    int GetPublic(MonTypeRef o);
    int GetPrivate(MonTypeRef o);
    int GetCompteur(MonTypeRef o);
    void FreeMonType(MonTypeRef o);
     
    #endif
    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
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    // MonType.c
    #include "MonType.h"
    #include <stdlib.h>
     
    typedef struct Compteur {
        int compteur;
    } Compteur;
     
    typedef struct {
    	int public;
    	int private;
    } MonType;
     
    MonTypeRef CreateMonType(int value)
    {
    	Compteur *new = malloc(sizeof(MonType) + sizeof(Compteur));
        new->compteur = 0;
        new = &new[1];
    	((MonType*)new)->public = value;
    	((MonType*)new)->private = value * 2;
     
    	return (MonTypeRef)new;
    }
     
    int GetPublic(MonTypeRef o)
    {
    	return o->public;
    }
     
    int GetPrivate(MonTypeRef o)
    {
    	return ((MonType*)o)->private;
    }
     
    int GetCompteur(MonTypeRef o)
    {
        return (&( (Compteur*)o )[-1])->compteur;
    }
     
    void FreeMonType(MonTypeRef o)
    {
    	free(&((Compteur*)o)[-1]);
    }
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    // main.c
    #include <stdio.h>
    #include "MonType.h"
     
    int main (int argc, const char * argv[])
    {
        MonTypeRef obj = CreateMonType(5);
        printf("obj = %d | %d, %d, %d",
               obj->public,
               GetPublic(obj), GetPrivate(obj), GetCompteur(obj));
        FreeMonType(obj);
        return 0;
    }
    Bien sûr ceci n'est qu'un exemple, il y a beaucoup de précautions à prendre dans ce genre de système, notamment pour ce qui est de l'alignement de la mémoire... Mais toujours est-il que ce genre de truc est tout à fait possible et qu'il est utile et utilisé dans certains cas.

  11. #11
    Expert éminent sénior
    Avatar de Emmanuel Delahaye
    Profil pro
    Retraité
    Inscrit en
    Décembre 2003
    Messages
    14 512
    Détails du profil
    Informations personnelles :
    Âge : 67
    Localisation : France, Paris (Île de France)

    Informations professionnelles :
    Activité : Retraité

    Informations forums :
    Inscription : Décembre 2003
    Messages : 14 512
    Points : 20 985
    Points
    20 985
    Par défaut
    Citation Envoyé par seriousme Voir le message
    Où ça ?
    OK. Il est techniquement possible de faire ce que tu as fait (ça compile) :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
     
    typedef struct
    {
    	int n;
    }
    *Integer;
    et
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
     
    typedef struct
    {
    	int n;
    	int private_n;
    }
    integer_private, *Integer_private;
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    ((Integer_private)i)->private_n = n;
    Horrible cast. Bon, c'est OK, j'avais mal lu. Mais comme dis Medinoc "c'est crade", mais c'est correct. Je prefère quand même ma méthode que je trouve plus 'clean'.

    NOTA important

    Ca fonctionne de façon portable uniquement parce qu'il n'y a qu'un seul élément commun et que c'est le premier.

    Ceci n'est pas portable :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
     
    /* .h */
    struct pub
    {
        int a;
        int b;
    };
     
    void f (struct pub *p);
    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
     
    /* .c */
    #include ".h"
     
    struct priv
    {
        int a;
        int b;
     
        int c;
        int d;
    };
     
    void f (struct pub *p)
    {
        struct priv *this = (struct priv *) p;
    seul l'accès à 'a' est garanti par le langage. L'accès à b n'est pas portable.

    La solution que j'avais proposée est 100% portable.
    Pas de Wi-Fi à la maison : CPL

  12. #12
    Membre expert
    Avatar de Pragmateek
    Homme Profil pro
    Formateur expert .Net/C#
    Inscrit en
    Mars 2006
    Messages
    2 635
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 37
    Localisation : France, Val de Marne (Île de France)

    Informations professionnelles :
    Activité : Formateur expert .Net/C#
    Secteur : Conseil

    Informations forums :
    Inscription : Mars 2006
    Messages : 2 635
    Points : 3 958
    Points
    3 958
    Par défaut
    mais c'est correct
    Correct au sens de :
    - pas de comportement indéfini,
    - pas de problème d'alignement mémoire,
    - portabilité garantie par le standard,
    - ... ?

    Donc utilisable sans risque technique ?
    Formateur expert .Net/C#/WPF/EF Certifié MCP disponible sur Paris, province et pays limitrophes (enseignement en français uniquement).
    Mon blog : pragmateek.com

  13. #13
    Expert éminent sénior
    Avatar de Emmanuel Delahaye
    Profil pro
    Retraité
    Inscrit en
    Décembre 2003
    Messages
    14 512
    Détails du profil
    Informations personnelles :
    Âge : 67
    Localisation : France, Paris (Île de France)

    Informations professionnelles :
    Activité : Retraité

    Informations forums :
    Inscription : Décembre 2003
    Messages : 14 512
    Points : 20 985
    Points
    20 985
    Par défaut
    Citation Envoyé par seriousme Voir le message
    Correct au sens de :
    - pas de comportement indéfini,
    - pas de problème d'alignement mémoire,
    - portabilité garantie par le standard,
    - ... ?

    Donc utilisable sans risque technique ?
    J'ai précisé ma réponse...
    Pas de Wi-Fi à la maison : CPL

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

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

    Informations forums :
    Inscription : Septembre 2005
    Messages : 27 369
    Points : 41 519
    Points
    41 519
    Par défaut
    Moi, j'ai pire:
    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
    /* Integer.h */
    struct st_integer;
    typedef struct st_integer *Integer;
     
    /*Ne pas définir la structure si INTEGER_PRIVATE est défini,
      Parce qu'on va la définir autrement dans Integer.c */
    #ifndef INTEGER_PRIVATE
     
    struct st_integer;
    {
    	int public_n;
    };
     
    #endif
     
    Integer IntegerCreate(int n);
    void IntegerDelete(Integer i);
    int IntegerGet(Integer i);
    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
    /* Integer.c */
    #define INTEGER_PRIVATE
    #include <Integer.h>
     
    struct st_integer;
    {
    	int public_n;
    	int private_n;
    };
     
    #endif
     
    static void IntegerDoSomethingPrivate(Integer i)
    {
    	...
    }
     
    Integer IntegerCreate(int n)
    {
    	...
    }
    void IntegerDelete(Integer i)
    {
    	...
    }
    int IntegerGet(Integer i)
    {
    	...
    }
    Code C : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    /* main.c */
    #include "integer.h"
    #include <stdio.h>
     
    int main(void)
    {
    	Integer i = IntegerCreate(42);
    	printf("i = %d = %d\n", IntegerGet(i), i->public_n);
    	IntegerDelete(i), i=0; /* Zero ? NULL ? INTEGER_NULL ? */
    	return 0;
    }
    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.

  15. #15
    Membre averti
    Profil pro
    Inscrit en
    Juillet 2007
    Messages
    308
    Détails du profil
    Informations personnelles :
    Âge : 38
    Localisation : France

    Informations forums :
    Inscription : Juillet 2007
    Messages : 308
    Points : 373
    Points
    373
    Par défaut
    Citation Envoyé par Emmanuel Delahaye Voir le message
    seul l'accès à 'a' est garanti par le langage. L'accès à b n'est pas portable.
    Comment cela se fait-il ?

    De plus, est-ce que t'as examiné mon test ? Surtout la deuxième partie sur le compteur de référence, j'aimerais avoir ton avis.

  16. #16
    Expert éminent sénior
    Avatar de Emmanuel Delahaye
    Profil pro
    Retraité
    Inscrit en
    Décembre 2003
    Messages
    14 512
    Détails du profil
    Informations personnelles :
    Âge : 67
    Localisation : France, Paris (Île de France)

    Informations professionnelles :
    Activité : Retraité

    Informations forums :
    Inscription : Décembre 2003
    Messages : 14 512
    Points : 20 985
    Points
    20 985
    Par défaut
    Citation Envoyé par PsychoH13 Voir le message
    Comment cela se fait-il ?
    Je te laisse lire la norme.
    Pas de Wi-Fi à la maison : CPL

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

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

    Informations forums :
    Inscription : Septembre 2005
    Messages : 27 369
    Points : 41 519
    Points
    41 519
    Par défaut
    Au passage, dans mon code "utilisateur" je me suis retrouvé face à une difficulté qu'entraîne le masquage du pointeurs: Comment sais-je que je peux remettre à NULL après l'appel à la fonction de suppression, si je ne sais pas que c'est un pointeur ?
    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.

  18. #18
    Membre averti
    Profil pro
    Inscrit en
    Juillet 2007
    Messages
    308
    Détails du profil
    Informations personnelles :
    Âge : 38
    Localisation : France

    Informations forums :
    Inscription : Juillet 2007
    Messages : 308
    Points : 373
    Points
    373
    Par défaut
    Citation Envoyé par Emmanuel Delahaye Voir le message
    Je te laisse lire la norme.
    Et pour la deuxième partie de ma question ?

  19. #19
    Expert éminent sénior
    Avatar de Emmanuel Delahaye
    Profil pro
    Retraité
    Inscrit en
    Décembre 2003
    Messages
    14 512
    Détails du profil
    Informations personnelles :
    Âge : 67
    Localisation : France, Paris (Île de France)

    Informations professionnelles :
    Activité : Retraité

    Informations forums :
    Inscription : Décembre 2003
    Messages : 14 512
    Points : 20 985
    Points
    20 985
    Par défaut
    Citation Envoyé par PsychoH13 Voir le message
    Et pour la deuxième partie de ma question ?
    Déjà répondu ailleurs. Le cast çaÿ mal et la superposition de structures ne fonctionne que sur le premier élément si c'est le même type.
    Pas de Wi-Fi à la maison : CPL

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

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

    Informations forums :
    Inscription : Septembre 2005
    Messages : 27 369
    Points : 41 519
    Points
    41 519
    Par défaut
    Ton compteur de références ne compte rien, pour l'instant.

    Je m'y connais en comptage de références (développement COM oblige) et je pense qu'il n'est pas du meilleur effet de le cacher comme ça avant le pointeur. Généralement, ça marche très bien intégré à l'objet...
    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. Les types abstraits de données (ADT)
    Par Emmanuel Delahaye dans le forum C
    Réponses: 5
    Dernier message: 05/06/2013, 14h59
  2. Classe : type abstrait de donnée
    Par problems99 dans le forum Langage
    Réponses: 6
    Dernier message: 11/01/2011, 13h59
  3. Réponses: 7
    Dernier message: 19/07/2010, 19h01
  4. Type abstrait de donnée
    Par mia123 dans le forum Pascal
    Réponses: 1
    Dernier message: 01/06/2007, 14h00

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