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.
'strcpy (ptr [i] -> name," niemand ").' Dies ist UB, weil Sie keinen Speicher für Strukturen innerhalb 'ptr'-Arrays zugewiesen haben. –
Ich muss innerhalb der _for_ 'ptr [i] = (struct Person *) malloc (sizeof (struct Person) + 1);' – Danick
'int num_persons = 10; struct Person * ptr [num_persons]; 'sicher kompiliert für mich nicht, fehlt mir etwas? – yano