2016-11-15 5 views
2

Je suis aux prises avec une struct C qui doit tenir un tableau dynamique de struct plus petits:Tableau C gratuit dynamique de struct - Pourquoi sont-ils pas Contigous

typedef struct issueStruct { 
    int data; 
} issue; 

typedef struct volumeStruct { 
    issue* collection; 
    size_t elements; 
} volume; 

Je peux créer dynamiquement autant question struct que je comme dans le tableau d'une structure de volume. Je peux également itérer à travers ce tableau:

int main(){ 
    volume* TimeMagazine = (volume*)malloc(sizeof(volume)); 
    TimeMagazine->collection = (issue*)malloc(4 * sizeof(issue)); 
    TimeMagazine->elements = 4; 

    issue* ptr = TimeMagazine->collection; 
    int i; 

    // Populate & iterate through array: 
    i = 0; 
    while(i < TimeMagazine->elements){ 
      ptr->data = 100*i; 
      printf("%d) %d\n", i, ptr->data); 
      i++; 
      ptr = ptr+i;  // Advance ptr 
    } 
    return 0; 
} 

OUTPUT: 
[Linux]$ gcc -Wall magazines.c 
[Linux]$ ./a.out 
0) 0 
1) 100 
2) 200 
3) 300 
[Linux]$ 

Jusqu'ici, tout va bien. Quand je passe en revue ce qui précède dans GDB, tout semble correct, même si je remarque que les structures de problèmes ne semblent pas avoir d'adresses mémoire contiguës. Voici les adresses de mémoire que j'ai vues:

issue 0) 0x602030 
issue 1) 0x602034 
issue 2) 0x60203c 
issue 3) 0x602048 

Cela m'a donné une pause; J'aurais supposé que tous les problèmes seraient séparés par 4 octets, comme sizeof(issue) = 4. Plus sérieusement, lorsque je modifie mon code "itérer" pour libérer les éléments du tableau, mon code segmente les fautes. Plus précisément, il fait défaut lorsqu'il tente de libérer le second numéro. Voici le code:

i = 0; 
    ptr = TimeMagazine->collection; 
    issue* ptr2 = ptr; 
    while(i< TimeMagazine->elements){ 
      printf("freeing %d...\n", i); 
      i++; 
      free(ptr2);   // free ptr2 
      ptr2 = ptr = ptr+i; // advance ptr & ptr2 
    } 

est ici l'erreur (GCC sous Linux):

*** Error in `./a.out': free(): invalid pointer: 0x000000000137c034 *** 

Je suis sûr que je manque quelque chose ici, mais pas sûr de ce que. Quelqu'un peut-il recommander un moyen efficace de libérer() les éléments du tableau?

Merci beaucoup!

-Pete

PS - Il y a beaucoup de « libérant ainsi struct dans le tableau » messages, mais aucun ne semblait correspondre exactement ce que je fais. Donc je poste ceci dans l'espoir que ma version de cette question est unique.

+1

Un 'malloc', un' free'. C'est la règle. Pour le moment, vous avez un 'malloc' pour tout le tableau' collection' et vous essayez d'avoir plusieurs appels 'free' avec un pour chaque élément du tableau (bien que l'arithmétique de votre pointeur soit incorrecte comme indiqué dans la réponse ci-dessous) . – kaylum

Répondre

7
while(i < TimeMagazine->elements){ 
     ptr->data = 100*i; 
     printf("%d) %d\n", i, ptr->data); 
     i++; 
     ptr = ptr+i;  // Advance ptr 
} 

Vous utilisez mal l'arithmétique des pointeurs dans ptr = ptr+i, devrait être ptr = ptr+1 ou vous accédez à l'extérieur des limites. Idem pour la section free.

Et comme l'a souligné @kaylum dans les commentaires: vous appelez free dans une boucle, c'est également faux, vous pouvez free(TimeMagazine->collection); à la fois puisque vous réservez un espace pour 4 éléments dans le même bloc.

+1

La même chose sur la partie sans mémoire. – Ari0nhh

+0

Utilisez également le format '"% p "' pour imprimer les pointeurs. Il n'y a aucune garantie que '"% d "' fera ce que vous voulez. – Gene

+0

@Gene, il semble que OP est l'impression de l'adresse dans gdb (pas dans le programme), 'ptr-> data' est un' int' –

2

Ceci est une remarque concernant la mémoire contiguë et les structures contenant des tableaux dynamiques. Pour la réponse réelle, référez-vous au the answer given by @KeineLust. Comme mentionné précédemment, un malloc == un free. Cependant, il n'est pas mentionné que la mémoire contiguë fonctionne souvent mieux en raison des considérations de mise en cache.

Cela signifie que votre struct volumeStruct fonctionnerait mieux si sa mémoire et la matrice dynamique étaient allouées en utilisant le même appel malloc.

Il y a deux façons courantes d'accomplir ceci.

un, en utilisant les mêmes structures que vous avez actuellement (je fixe votre boucle d'avoir ptr = ptr + 1, donc on ne va pas en dehors des limites):

int main(){ 
    volume* TimeMagazine = (volume*)malloc(sizeof(volume) + (4 * sizeof(issue))); 
    TimeMagazine->collection = TimeMagazine + 1; // pointer arithmetics 
    TimeMagazine->elements = 4; 

    issue* ptr = TimeMagazine->collection; 
    int i; 

    // Populate & iterate through array: 
    i = 0; 
    while(i < TimeMagazine->elements){ 
      ptr->data = 100*i; 
      printf("%d) %d\n", i, ptr->data); 
      i++; 
      ptr = ptr+1;  // Advance ptr 
    } 

    free(TimeMagazine); 

    return 0; 
} 

Une autre option (je pense que cela a été introduit dans C99), est d'ajouter un tableau de longueur variable à la fin de la structure. Cela vous évite les 8 (ou 4) octets requis pour le pointeur collection.

i.e. .:

typedef struct issueStruct { 
    int data; 
} issue; 

typedef struct volumeStruct { 
    size_t elements; 
    issue collection[]; 
} volume; 

int main(){ 
    volume* TimeMagazine = (volume*)malloc(sizeof(volume) + (4 * sizeof(issue))); 
    TimeMagazine->elements = 4; 
    // no need to assign a value for TimeMagazine->collection 

    issue* ptr = TimeMagazine->collection; 
    int i; 

    // Populate & iterate through array: 
    i = 0; 
    while(i < TimeMagazine->elements){ 
      ptr->data = 100*i; 
      printf("%d) %d\n", i, ptr->data); 
      i++; 
      ptr = ptr+1;  // Advance ptr 
    } 

    free(TimeMagazine); 

    return 0; 
} 

Le grand avantage est la mémoire cache du processeur et le code plus simple. Le fait que nous sauvegardions deux appels système par objet (un malloc et un free) n'est pas pertinent dans la plupart des cas.

+0

C'est absolument génial. Une chose qui me rend confus ... Vous n'avez pas d'appels malloc() pour les structures d'émission. Je comprends que le malloc() pour les problèmes est replié dans l'appel malloc() pour la structure de volume "TimeMagazine". Cela sculpte la mémoire pour les problèmes. Ensuite, le problème * ptr pointe vers la collection de TimeMagazine. Donc quand vous incrémentez ptr de +1, le compilateur sait que "+1" signifie "+ sizeof (issue)"? – Pete

+0

@Pete, oui, le compilateur sait que 'sizeof (* ptr) == sizeof (issue)' et effectue une arithmétique de pointeur correcte lorsque vous utilisez 'ptr + = 1'. Les pointeurs ne sont pas plus que des nombres représentant des adresses de mémoire (souvent virtuelles). Il n'y a pas de données de type en mémoire, donc les types sont toujours contrôlés par le compilateur qui ajuste l'arithmétique du pointeur en conséquence. – Myst

+0

Wow, merci. J'ai lu sur cet aspect de C dans beaucoup de documentation, mais c'est une chose étrange pour s'y habituer. Merci encore! – Pete