Forum Programmation.c Malloc et pointeur sur structure anonyme

Posté par (page perso) .
Tags : aucun
0
8
jan.
2012

Bonjour,

J'aimerais que l'on m'explique pourquoi la fonction si dessous fonctionne. Elle renvoie un tableau à deux dimension de Case, un pointeur vers une structure anonyme.

Ce qui m'intrique c'est que je fais map[i][j] = malloc (sizeof(Case)) ce qui devrait m'allouer un pointeur car c'est ce qu'est Case, et pourtant ça m'alloue une structure. Du coup je me dis que malloc à peut-être un comportement spéciale pour les pointeurs vers des structures anonymes.

Voici le code :

typedef struct
{
    Casetype type;
    Quantite qte;
    int pheroNid;
} *Case;


Case **newMap (int taille)
{
    Case **map = malloc (sizeof(Case*)*taille);

    int i, j;
    for (i = 0; i < taille; i++)
    {
        map[i] = malloc (sizeof(Case)*taille);

        for (j = 0; j < taille; j++)
        {
            map[i][j] = malloc (sizeof(Case));
            map[i][j]->type = VIDE;

            /* initialisation de la quantité de phéromone de sucre à 0 */
            map[i][j]->qte.pheroSucre = 0;
        }
    }

    return map;
}

  • # Mouais

    Posté par (page perso) . Évalué à 0.

    Tu vois ca comment?

    Et sinon il sert a quoi ce malloc?
    map[i] est un tableau de Case, donc map[i][j] c'est deja un Case
    Pourquoi tu ne fais pas juste map[i][j].type = VIDE ?

    • [^] # Re: Mouais

      Posté par (page perso) . Évalué à 3.

      Et sinon il sert a quoi ce malloc?

      Sans j'ai un segfault.

      Pourquoi tu ne fais pas juste map[i][j].type = VIDE ?

      Parce que Case est un pointeur (vers une structure anonyme).

      Il existe deux catégories de gens : ceux qui divisent les gens en deux catégories et les autres.

  • # Meh.

    Posté par . Évalué à 10.

    ce qui devrait m'allouer un pointeur car c'est ce qu'est Case, et pourtant ça m'alloue une structure

    malloc() en à rien à faire de ce que tu alloue, la seule chose qui l'intéresse, c'est sa taille. Si il se trouve que la taille de ta structure est inférieure à la taille de ton pointeur (t'est en 64bits ? les pointeurs sont gros en 64bits ;), tu n'aura aucun problème. Si ce n'est pas le cas, mais que tu n'utilise que les membres qui se trouvent être parmi les premiers sizeof(Case) octets¹ de ta structure, ton code va tourner parfaitement quand même. Et même si tu dépasse, ce n'est pas garanti que ton programme plante (mais ne compte pas la dessus, ça peut planter bizarrement ailleurs, voire pire, fonctionner parfaitement mais avec une faille de sécurité), par contre, c'est presque garanti que valgrind va gueuler.

    De manière générale, ce n'est pas parce qu'un code marche chez toi qu'il est correct, portable et qu'il va marcher chez tout le monde.

    ¹ oui je sais c'est pas des octets mais des "tailles de char", tapez pas.

  • # hum...

    Posté par (page perso) . Évalué à 8.

    Le problème ne vient pas de malloc mais plutôt de sizeof.
    En effet, sizeof sait mesurer la taille des données brutes qu'on lui donne en paramètre, toutefois il ne peut pas présupposer de la taille d'un déréférencement de pointeur. Pour lui un pointeur pointe au moins sur un bloc de mémoire de la taille du mot de base du compilateur (ex. 8 pour un processeur 64 bits).

    L'exemple simple ci-dessous pourra éventuellement t'éclairer.

    #include <stdio.h>
    
    int main() {
      char* c = NULL;
    
      printf("sizeof(c) = %lu\n", sizeof(c));
      printf("sizeof(char) = %lu\n", sizeof(char));
    
      return 0;
    }
    
    

    Ainsi, l'exemple que tu donnes par de toute façon dans la mauvaise direction quant à ton utilisation de sizeof.
    Je te conseille plutôt un écriture du style pour t'en sortir.
    struct case_t
    {
        Casetype type;
        Quantite qte;
        int pheroNid;
    };
    typedef struct case_t *Case;
    
    

    Afin de corriger ta ligne
    map[i][j] = malloc (sizeof(Case));
    
    

    en
    map[i][j] = malloc (sizeof(struct case_t));
    
    

    car manifestement, celle-ci est fausse et ci cela fonctionne sans segfault chez toi, c'est plutôt étrange...

    Un conseil pour finir, évite les typedef de pointeur, c'est un truc à s'embrouiller le cerveau pour pas grand chose.

  • # ça marche, un peu...

    Posté par . Évalué à 2.

    map[i][j] = malloc (sizeof(Case));
    map[i][j]->type = VIDE;
    
    

    ça marche parce que "type" est le premier membre de ta structure est que "VIDE" à une taille inférieure à sizeof(void*)

    Tu assignes dans map[i][j] un pointeur vers une zone de mémoire de taille sizeof(Case) qui vaut sizeof(void), à savoir, 4 octets sur une architecture x86 et 8 sur une architecture amd64, et tu mets dedans, avec l'instruction "map[i][j]->type = VIDE", type étant le premier membre de ta structure, donc celui qui commence "au début", la valeur de VIDE, que je suppose être une macro du préprocesseur définie par 0, Si sizeof(Casetype) <= sizeof(void), ça ne pose pas de soucis d'un point de vue corruption de ta mémoire.

    Dans ton cas, pour faire les choses correctement, donnes 2 noms à ta structure, dont un qui ne correspond pas à un pointer et utilise celui là pour le malloc de plus bas niveau.

  • # Merci

    Posté par (page perso) . Évalué à 2.

    Merci pour vos explications. Donc finalement ça marche grâce au hasard. Ce qui est étonnant c'est que j'ai testé sur 3 machines différentes (dont au moins une en 32 bits et une en 64 bits) et ça marche sur les 3.

    J'ai corrigé en donnant un nom à ma structure et en faisant un malloc (sizeof(struct maStruct)).

    Merci !

    Il existe deux catégories de gens : ceux qui divisent les gens en deux catégories et les autres.

    • [^] # Re: Merci

      Posté par . Évalué à 3.

      Merci pour vos explications. Donc finalement ça marche grâce au hasard.

      En C, c'est assez fréquent malheureusement (non ce n'est pas un troll, juste mon expérience).

      Deux conseils:
      - Ada, Ocaml ou Python sont des très bon langages, beaucoup plus simple a utiliser que le C (mais beaucoup moins répandu ce qui peut être handicapant) donc si tu peux les utiliser ne t'en prives pas.
      - si tu veux continuer a utiliser le C, utilise des outils qui permette d'éviter des problèmes (lint + valgrind par exemple), même John Carmack le fait:
      http://blog.regehr.org/archives/659

      • [^] # Re: Merci

        Posté par . Évalué à 2.

        Je ne peux pas laisser dire que parfois, "ça marche grâce au hasard" et encore moins que ça soit plus fréquent en C que sur n'importe quel langage.

        Il peut arriver qu'un morceau de code utilisant mal la mémoire fonctionne systématiquement, mais c'est pas du hasard. Genre des cas de débordements de buffer qui se passent bien parce qu'on déborde sur une zone de mémoire allouée, qui a été utilisée précédemment dans l'algo et dont on ne se sert plus après. Ou des cas ou la réorganisation du code par le compilo masque l'expression d'un bug. Dans ces cas là, on peut dire qu'on a eu de la chance, mais c'est pas du hasard.

        le paragraphe qui vient ne concerne personne en particulier de ce thread ... enfin j'espère, mais maintenant que je suis lancé, ça serait dommage:
        La pente est très glissante quand on commence à arrêter de chercher des explications techniques à un comportement incorrecte en se contentant d'une explication approximative. En dehors du fait que c'est un peu de la paresse intellectuelle, on finit par en arriver à l'informatique vaudoue où les incantations pour faire fonctionner le bousin, transmises de bouches de développeurs à oreilles de développeurs (en se batardisant de plus en plus au passage), remplacent l'analyse et la compréhension. L'effet de bord malencontreux, c'est que ça empêche la structuration d'une approche efficace de débogage (et oui, on ne peut pas corriger la malchance). Une partie non négligeable de mon temps consiste à essayer de récupérer le n'importe quoi écrit par des développeurs qui font de l'informatique ésotériques [1]. C'est pas la partie la plus amusante (encore qu'au vu de certains bouts de code, y a des bonnes tranches de rigolade :)

        j'ai vu des trucs "rigolos" en C, en C++, en Java et en Python (et en SQL, soyons fous).

        bon, soyons honnêtes, la plupart du temps, c'est plutôt du code très naïf/inefficient/redondant/pas robuste/pas maintenable/pas lisible/... que du code complètement loufoque, mais bon, ça arrive aussi.

        non ce n'est pas un troll, juste mon expérience).

        c'est absolument pas incompatible :)

        [1] mais ça fait des chouettes bouquins: Le Bureau des atrocités - Charles STROSS

        • [^] # Re: Merci

          Posté par . Évalué à 2.

          Je parlai de code incorrect (dépassement de tableau, variable non initialisée, utilisant une opération non définie) qui fonctionne par chance, en C ou C++ c'est très fréquent: http://blog.regehr.org/archives/213

          En Java? A moins d'utiliser les thread, ça me parait plus difficile..

          Après du code naïf/inefficient/redondant/pas robuste/pas maintenable/pas lisible, ça existe dans n'importe quel langage, mais c'est autre chose.

          • [^] # Re: Merci

            Posté par . Évalué à 2.

            attention, "par chance" != "par hasard" !

            c'est ce qui m'a poussé à commenter.

            En Java? A moins d'utiliser les thread, ça me parait plus difficile..

            je crains que tu ne sous estimes la capacité de la nature à produire des meilleurs idiots :-)
            (en référence à "Every day, man is making bigger and better fool-proof things, and every day, nature is making bigger and better fools. So far, I think nature is winning." - Albert Einstein)

            j'ai vu il y a pas longtemps du code qui fonctionne "par chance" en Python en monothreadé.
            Un dév qui a mal manifestement mal compris la distinction entre classe et instance.
            Alors oui,

            class uneClasse:
                ...
            
            a = uneClasse # au lieu de a = uneClasse()
            a.truc = 'bla'
            a.bidule = 'bli'
            
            

            ça marche ... tant qu'on a n'a pas besoin de deux instances de nom_class en même temps

            • [^] # Re: Merci

              Posté par . Évalué à 2.

              Un dév qui a mal manifestement mal compris la distinction entre classe et instance.

              Ou qui a zappé une paire de parenthèses ce qui peut arriver a tout le monde.

              Un exemple de plus comme quoi le typage dynamique implique de tester beaucoup..

              • [^] # Re: Merci

                Posté par . Évalué à 3.

                Ou qui a zappé une paire de parenthèses ce qui peut arriver a tout le monde.

                en théorie oui, mais là, j'ai des arguments :-)

                Dans ce cas, la classe en question est importée d'un module généré à partir d'un IDL Corba, et toutes les classes définies dans ce module sont mal utilisées.

                Un exemple de plus comme quoi le typage dynamique implique de tester beaucoup..

                agreed

Suivre le flux des commentaires

Note : les commentaires appartiennent à ceux qui les ont postés. Nous n'en sommes pas responsables.