2016-04-19 19 views
1

Ich habe ein Array von Strings in einer Struktur, dieDynamische Array von Strings innerhalb eines Struct realloc

#define ID_LEN 20 

struct Person{ 
char name[ID_LEN]; 
int num_items; 
char **items; 
}; 

int main(){ 
int num_persons = 10; 
struct Person *ptr[num_persons]; 

wie folgt aussieht ich mit einer Reihe von 10 Personen beginnen. Anfangs haben alle Personen 0 Einträge, also ist ihre Liste der Einträge ein malloc (0).

for(int i = 0; i < num_persons; i++){ 
     ptr[i] = (struct Person *) malloc(sizeof(struct Person) + 1); 
     ptr[i]->num_items = 0; 
     ptr[i]->items = malloc(0 * sizeof(char*)); 
} 

Irgendwann möchte ich diese Personen benennen und ihnen einige Elemente wie folgt hinzufügen.

strcpy(ptr[0]->name, "John"); 
ptr[0]->num_items = 1; 
ptr[0]->items = (char **)realloc(ptr[0]->items, ptr[0]->num_items * sizeof(char*)); 
strcpy(ptr[0]->items[0], "pencil"); 


printf("Name: %s\n", ptr[0]->name); 
printf("Number of items: %d\n", ptr[0]->num_items); 
for(int i = 0; i < ptr[0]->num_items; i++){ 
    printf("Item %d is %s\n", i, ptr[0]->items[i]); 
} 

ich Segmentation fault immer: 11. Ich bin mir nicht sicher, ob ich realloc richtig oder nicht getan haben.

+2

'strcpy (ptr [i] -> name," niemand ").' Dies ist UB, weil Sie keinen Speicher für Strukturen innerhalb 'ptr'-Arrays zugewiesen haben. –

+0

Ich muss innerhalb der _for_ 'ptr [i] = (struct Person *) malloc (sizeof (struct Person) + 1);' – Danick

+0

'int num_persons = 10; struct Person * ptr [num_persons]; 'sicher kompiliert für mich nicht, fehlt mir etwas? – yano

Antwort

1

Sie haben mehrere Probleme hier

zuerst Ihre ID_LEN

nirgends überprüfen Sie, ob durch Kopieren Sie die ID_LEN

so statt

strcpy(ptr[0]->name, "John"); 

übertreffen zu nennen Verwenden Sie

strcpy_s(ptr[0]->name,sizeof(ptr[0]->name),"John or whatever"); // C11 

Sie ordnen

ptr[0]->items = (char **)realloc(ptr[0]->items, ptr[0]->num_items * sizeof(char*)) 

aber

Sie sollten nicht Rückgabewert von malloc/realloc (Suche im Web für Erklärung wurde beschrieben Überdruss wurde) gegossen

wenn realloc mit , sollten Sie zuerst den Rückgabewert überprüfen, es kann fehlschlagen.

char** tmp = realloc(ptr[0]->items, ptr[0]->num_items * sizeof(char*)) 
if (tmp != NULL) 
{ 
    ptr[0]->items = tmp; 
} 
else 
{ 
    abort(); 
} 

Die Speicher realloc kehrt ist zum Teil nicht initialisierte (alte Zeiger bleiben aber neue sind nicht initialisiert. In Ihrem Fall Sie hatte keine vorherige Zeiger, so dass ein items[0] nicht initialisiert ist.

so, wenn Sie tun

strcpy(ptr[0]->items[0], "pencil"); 

es wird fehlschlagen, da items[0] an einer beliebigen Speicherstelle zeigt.

nachdem Sie realloc Zeiger müssen Sie initialisieren sie groß genug, um einen Speicher-zu-Punkt Zeichenfolge

Z. B. aufzunehmen

ptr[0]->items[0] = strdup("pencil"); // does malloc then copies string 

Es ist nicht so effizient ist realloc jedes Mal wenn Sie ein neues Element hinzufügen verwenden müssen, sondern ein Bündel von Elementen zuweisen, sondern setzen die, die Sie nicht verwenden dann halten auf NULL Spur, wie viele bleiben , sobald sie auslaufen zuweisen ein weiterer Haufen

0

Sie ordnen für die Liste der Elemente zu, Sie reservieren jedoch nicht für jede einzelne Elementzeichenfolge Speicherplatz. Ihr

strcpy(ptr[0]->items[0], "pencil"); 

wird fehlschlagen.

+0

verwenden Der Code wird viel vor dem Erreichen dieser Zeile ausfallen. –

+0

Vielleicht, nicht sicher, ob der Code Schnipsel ist oder nicht. Aber es wird auch an dieser Linie scheitern. – ChiralMichael

1

Das ganze Problem in Ihrem Code dreht sich um die Verwendung von Speicher, der nicht zugeordnet ist. Betrachten

ptr[0]->items = (char **)realloc(ptr[0]->items, ptr[0]->num_items * sizeof(char*));//line 1 
strcpy(ptr[0]->items[0], "pencil");//line 2 

In Zeile 1 Sie Speicher zugewiesen haben ptr[0]->num_items Anzahl von Zeigern auf c strings.But Sie halten nicht tatsächlich zugewiesenen Speicher speichern c Strings heißt diese Zeiger sind nicht auf die tatsächlichen Speicher zeigt. Wenn Sie in Zeile 2 versuchen, auf ptr[0]->items[0] zuzugreifen, ist nur char* zugewiesen, es ist kein Speicher zugewiesen. Ich habe die folgende Zeile vor Zeile 2 hinzugefügt und Ihr Code hat gut funktioniert.

for(int i=0;i<ptr[0]->num_items;i++) 
     ptr[0]->items[i]=malloc(sizeof(char)*10); 
0

Zusätzlich zu dem, was die anderen Antworten bieten, gibt es mehrere andere Überlegungen relevant für was Sie tun. Die erste, nicht 0 alles zuordnen. Das ist ein Überbleibsel aus vergangenen Zeiten und nicht notwendig.

Als nächstes folgt Ihre statische Deklaration einer Variablen Länge Anordnung von num_persons Zeiger struct Person zu geben, müssen Sie entscheiden, ob Sie Speicherplatz für alle Ihre Zeiger auf einmal vergeben wird, oder ob Sie Speicher für die Struktur zuteilen wird, nur beim Hinzufügen eine Person. Das wird Auswirkungen haben, wenn Sie Ihren Code in Funktionen aufteilen.

Da der Rest Ihrer Datenstruktur dynamisch zugewiesen wird, müssen Sie jede Zuordnung vor dem Kopieren/Initialisieren überprüfen.

Wenn Sie Ihre leere Struktur zuweisen, sehen Sie sich calloc statt malloc an. Dies ist eine Instanz, bei der die Standardinitialisierung von calloc hilfreich sein kann.

Der Rest Ihrer Aufgabe ist nur ein Buchführungsproblem - das heißt, für welche Mitglieder die Zuordnung, der aktuelle Status und die Größe jeder Zuweisung und schließlich den Speicher, den Sie zugewiesen haben, freigeben, wenn er nicht mehr benötigt wird .

Nehmen Sie zum Beispiel den Code Sie für das Hinzufügen einer Person mit einem Artikel auf Ihre Array von Zeigern wollen könnte. Um den Zusatz zu validieren, müssen Sie wissen, dass ptr ein gültiger Zeiger ist und ob Sie Ihr Limit num_persons erreicht haben, bevor Sie mit der Speicherzuweisung beginnen. Als nächstes müssen Sie Speicher für eine struct Person zuweisen und die Adresse für diesen Block ptx[X] zuweisen (für welchen Index X Sie arbeiten).

Nach der Zuweisung können Sie die Standardwerte initialisieren (oder wenn calloc verwendet wurde, wissen Sie, dass alle Werte auf 0/NULL initialisiert wurden). Im Anschluss an, dass Sie name (begrenzt auf ID_LEN) kopieren und verteilen Zeiger und Speicher für jeden Artikel Sie in Ihrem Zeiger-zu-Zeiger-to-char halten möchten * items (die Sie ähnlich behandelt zu ptr akzeptieren, dass die Zeiger für items werden ebenfalls zugewiesen) Hinweis: realloc (items,... ist an dieser Stelle nicht erforderlich, da dies das erste Mal ist items zugeordnet ist.

Wenn Sie all das zusammen, können Sie mit einer Funktion kommen eine Person hinzufügen zusammen mit ihrem ersten Artikel ähnlich der folgenden, wo max die maximale Anzahl von Personen ist erlaubt, nm der Name ist, ist itm das Element, und idx ist ein Zeiger auf den aktuellen Index für diese Person:

/* add person with item */ 
struct person *addpwitem (struct person **ptr, int max, char *nm, 
          char *itm, int *idx) 
{ 
    if (!ptr || *idx + 1 == max) return NULL; 
    int i = *idx; 

    /* allocate storage for struct */ 
    if (!(ptr[i] = calloc (1, sizeof **ptr))) { 
     fprintf (stderr, "error: ptr[%d], virtual memory exhausted.\n", i); 
     return NULL; 
    } 
    strncpy (ptr[i]->name, nm, ID_LEN); /* copy name */ 

    /* allocate/validate pointer to char* ptr[i]->items */ 
    if (!(ptr[i]->items = 
     malloc (ptr[i]->num_items + 1 * sizeof *(ptr[i]->items)))) { 
     fprintf (stderr, "error: items*, virtual memory exhausted.\n"); 
     return NULL; 
    } 
    /* allocate/validate memory for ptr[num_items]->items[num_items] */ 
    if (!(ptr[i]->items[ptr[i]->num_items] = strdup (itm))) { 
     fprintf (stderr, "error: items, virtual memory exhausted.\n"); 
     return NULL; 
    } 
    ptr[i]->num_items++; 
    (*idx)++; 

    return ptr[i]; 
} 

(Anmerkung: Sie müssen die Rückkehr der Funktion ptr[X] in dem Aufruf functi zuweisen auf. Beachten Sie auch, C-Stil alle Klein Namen empfiehlt, das ist, was Sie oben sehen, lassen Camelcase Namen C++)

Sobald Sie eine Person hinzugefügt haben, sollten Sie die Möglichkeit, Artikel hinzufügen die Liste der mit dieser Person verbundenen Elemente. Hier wird realloc angezeigt, damit Sie die Anzahl der Zeiger auf Elemente für diese Person ändern können. Sie können etwas ähnliches tun, um eine Person hinzuzufügen, aber der übergebene Index muss nicht länger ein Zeiger sein, da er nicht innerhalb der Funktion aktualisiert wird. z.B.

/* add item to person at index 'i' */ 
struct person *additem (struct person **ptr, int i, char *itm) 
{ 
    if (!ptr) return NULL; 
    void *tmp; 

    /* allocate/realloc/validate pointer to char* ptr[i]->items */ 
    if (!(tmp = realloc (ptr[i]->items, 
         (ptr[i]->num_items + 1) * sizeof *(ptr[i]->items)))) { 
     fprintf (stderr, "error: items*, virtual memory exhausted.\n"); 
     return NULL; 
    } 
    ptr[i]->items = tmp; /* assign tmp on successful realloc */ 

    /* allocate/validate memory for ptr[num_items]->items[num_items] */ 
    if (!(ptr[i]->items[ptr[i]->num_items] = strdup (itm))) { 
     fprintf (stderr, "error: items, virtual memory exhausted.\n"); 
     return NULL; 
    } 
    ptr[i]->num_items++; 

    return ptr[i]; 
} 

Sie sind nun in der Lage, beide Personen und Artikel zur Liste hinzuzufügen, sondern brauchen eine Möglichkeit, alle Werte in der Liste iterieren, wenn Sie davon Gebrauch machen werden. Beim Suchen, Drucken oder Freigeben von Speicher haben alle Iterationsfunktionen eine ähnliche Form. So drucken Sie alle Personen und Artikel Sie etwas Ähnliches wie die folgende tun könnte:

/* print the list of persons and items */ 
void prndetail (struct person **ptr, int idx) 
{ 
    if (!ptr || !*ptr) return; 
    int i, p; 
    for (p = 0; p < idx; p++) { 
     printf (" %-20s %2d", ptr[p]->name, ptr[p]->num_items); 
     for (i = 0; i < ptr[p]->num_items; i++) 
      printf ("%s%s", i ? ", " : " ", ptr[p]->items[i]); 
     putchar ('\n'); 
    } 
} 

In jedem Code Ihre Schreib, die dynamisch Speicher reserviert, haben Sie 2 responsibilites in Bezug auf jeden Speicherblock zugeordnet: (1) behält immer einen Zeiger auf die Startadresse für den Speicherblock, so dass (2) es freigegeben werden kann, wenn es nicht mehr benötigt wird. Wenn Sie mit Ihrer Liste fertig sind, müssen Sie free es. Ähnlich wie beim Drucken, kann es wie folgt gemacht werden:

/* free all allocated memory */ 
void deletelist (struct person **ptr, int idx) 
{ 
    if (!ptr || !*ptr) return; 
    int i, p; 
    for (p = 0; p < idx; p++) { 
     for (i = 0; i < ptr[p]->num_items; i++) 
      free (ptr[p]->items[i]); 
     free (ptr[p]->items); 
     free (ptr[p]); 
    } 
    // free (ptr); /* if array of pointers allocated, not static */ 
} 

Das ist wirklich alles, was es in der Buchhaltung Lektion gibt. Wenn Sie jeden Teil der von Ihnen verwendeten Datenstruktur im Auge behalten, ist die Verwaltung des Speichers einfach. Putting alle Teile zusammen in einem kurzen Beispiel, Sie so etwas wie tun könnte:

#include <stdio.h> 
#include <stdlib.h> 
#include <string.h> 

enum { NPER = 10, ID_LEN = 20 }; 

struct person { 
    char name[ID_LEN]; 
    int num_items; 
    char **items; 
}; 

/* add person + item - to add both person and item at once */ 
struct person *addpwitem (struct person **ptr, int max, char *nm, 
          char *itm, int *idx); 
/* add item to existing person */ 
struct person *additem (struct person **ptr, int i, char *itm); 
void prndetail (struct person **ptr, int idx); 
void deletelist (struct person **ptr, int idx); 

int main (void) { 

    int idx = 0; 
    struct person *ptr[NPER]; 

    /* allocate storage for struct, add person + item */ 
    if (!(ptr[idx] = addpwitem (ptr, NPER, "John", "pencils", &idx))) { 
     fprintf (stderr, "error: adding ptr[%d] failed.\n", idx); 
     return 1; 
    } 

    printf ("\nadded John:\n"); 
    prndetail (ptr, idx);  /* print contents of persons & items */ 

    additem (ptr, idx - 1, "pens"); /* add next item */ 
    printf ("\nadded item 'pens' for John:\n"); 
    prndetail (ptr, idx);  /* print contents of persons & items */ 

    /* add next person + item */ 
    if (!(ptr[idx] = addpwitem (ptr, NPER, "Martha", "paper", &idx))) { 
     fprintf (stderr, "error: adding ptr[%d] failed.\n", idx); 
     return 1; 
    } 

    printf ("\nadded Martha:\n"); 
    prndetail (ptr, idx);  /* print contents of persons & items */ 

    deletelist (ptr, idx);  /* free all allocated memory */ 

    return 0; 
} 

/* add person with item */ 
struct person *addpwitem (struct person **ptr, int max, char *nm, 
          char *itm, int *idx) 
{ 
    if (!ptr || *idx + 1 == max) return NULL; 
    int i = *idx; 

    /* allocate storage for struct */ 
    if (!(ptr[i] = calloc (1, sizeof **ptr))) { 
     fprintf (stderr, "error: ptr[%d], virtual memory exhausted.\n", i); 
     return NULL; 
    } 
    strncpy (ptr[i]->name, nm, ID_LEN); /* copy name */ 

    /* allocate/validate pointer to char* ptr[i]->items */ 
    if (!(ptr[i]->items = 
     malloc (ptr[i]->num_items + 1 * sizeof *(ptr[i]->items)))) { 
     fprintf (stderr, "error: items*, virtual memory exhausted.\n"); 
     return NULL; 
    } 
    /* allocate/validate memory for ptr[num_items]->items[num_items] */ 
    if (!(ptr[i]->items[ptr[i]->num_items] = strdup (itm))) { 
     fprintf (stderr, "error: items, virtual memory exhausted.\n"); 
     return NULL; 
    } 
    ptr[i]->num_items++; 
    (*idx)++; 

    return ptr[i]; 
} 

/* add item to person at index 'i' */ 
struct person *additem (struct person **ptr, int i, char *itm) 
{ 
    if (!ptr) return NULL; 
    void *tmp; 

    /* allocate/realloc/validate pointer to char* ptr[i]->items */ 
    if (!(tmp = realloc (ptr[i]->items, 
         (ptr[i]->num_items + 1) * sizeof *(ptr[i]->items)))) { 
     fprintf (stderr, "error: items*, virtual memory exhausted.\n"); 
     return NULL; 
    } 
    ptr[i]->items = tmp; /* assign tmp on successful realloc */ 

    /* allocate/validate memory for ptr[num_items]->items[num_items] */ 
    if (!(ptr[i]->items[ptr[i]->num_items] = strdup (itm))) { 
     fprintf (stderr, "error: items, virtual memory exhausted.\n"); 
     return NULL; 
    } 
    ptr[i]->num_items++; 

    return ptr[i]; 
} 

/* print the list of persons and items */ 
void prndetail (struct person **ptr, int idx) 
{ 
    if (!ptr || !*ptr) return; 
    int i, p; 
    for (p = 0; p < idx; p++) { 
     printf (" %-20s %2d", ptr[p]->name, ptr[p]->num_items); 
     for (i = 0; i < ptr[p]->num_items; i++) 
      printf ("%s%s", i ? ", " : " ", ptr[p]->items[i]); 
     putchar ('\n'); 
    } 
} 

/* free all allocated memory */ 
void deletelist (struct person **ptr, int idx) 
{ 
    if (!ptr || !*ptr) return; 
    int i, p; 
    for (p = 0; p < idx; p++) { 
     for (i = 0; i < ptr[p]->num_items; i++) 
      free (ptr[p]->items[i]); 
     free (ptr[p]->items); 
     free (ptr[p]); 
    } 
    // free (ptr); /* if array of pointers allocated */ 
} 

Beispiel Verwendung/Output

$ ./bin/struct_p2p2c 

added John: 
John     1 pencils 

added item 'pens' for John: 
John     2 pencils, pens 

added Martha: 
John     2 pencils, pens 
Martha     1 paper 

Speicherfehler prüfen

ist es zwingend notwendig, dass Sie Verwenden Sie ein Programm zur Überprüfung des Speicherfehlers, um sicherzustellen, dass Sie nicht über/außerhalb des zugewiesenen Speicherblocks geschrieben, versucht haben, einen Sprung auf einen uninitialisierten Wert und fi zu lesen oder zu basieren um zu bestätigen, dass Sie den gesamten von Ihnen zugewiesenen Speicher freigegeben haben.

Es ist einfach zu tun:

$ valgrind ./bin/struct_p2p2c 
==7618== Memcheck, a memory error detector 
==7618== Copyright (C) 2002-2013, and GNU GPL'd, by Julian Seward et al. 
==7618== Using Valgrind-3.10.1 and LibVEX; rerun with -h for copyright info 
==7618== Command: ./bin/struct_p2p2c 
==7618== 

added John: 
John     1 pencils 

added item 'pens' for John: 
John     2 pencils, pens 

added Martha: 
John     2 pencils, pens 
Martha     1 paper 
==7618== 
==7618== HEAP SUMMARY: 
==7618==  in use at exit: 0 bytes in 0 blocks 
==7618== total heap usage: 8 allocs, 8 frees, 115 bytes allocated 
==7618== 
==7618== All heap blocks were freed -- no leaks are possible 
==7618== 
==7618== For counts of detected and suppressed errors, rerun with: -v 
==7618== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 1 from 1) 

Immer Alle Haufen Blöcke wurden befreit bestätigen - keine Lecks sind möglich und ebenso wichtig ERROR Zusammenfassung: 0 Fehler von 0 Kontexten.

Schauen Sie sich das und den Rest der Antworten an. Es gibt viele gute Vorschläge zwischen ihnen. Lassen Sie uns wissen, wenn Sie weitere Fragen haben.