Struct realloc 中的动态字符串数组
Dynamic array of Strings inside a 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];
我从 10 个人的数组开始。最初所有人都有 0 个项目,因此他们的项目列表是一个 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*));
}
有时我想给这些人起名字,然后给他们添加一些这样的项目。
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]);
}
我遇到分段错误:11。我不确定我是否正确地重新分配。
您正在为项目列表分配,但没有为每个单独的项目字符串分配 space。你的
strcpy(ptr[0]->items[0], "pencil");
会失败。
您代码中的所有问题都围绕着使用未分配的内存。
考虑
ptr[0]->items = (char **)realloc(ptr[0]->items, ptr[0]->num_items * sizeof(char*));//line 1
strcpy(ptr[0]->items[0], "pencil");//line 2
在第 1 行中,您分配了内存来保存 ptr[0]->num_items
个指向 c 的指针 strings.But 您实际上没有分配内存来存储 c 字符串,即那些指针没有指向实际内存。
当您在第 2 行尝试访问 ptr[0]->items[0]
时,它只是 char*
没有分配给它的内存。
我在第 2 行之前添加了以下行,您的代码工作正常。
for(int i=0;i<ptr[0]->num_items;i++)
ptr[0]->items[i]=malloc(sizeof(char)*10);
你这里有几个问题
首先是你的ID_LEN
你没有检查是否通过复制到名称超过 ID_LEN
所以而不是
strcpy(ptr[0]->name, "John");
使用
strcpy_s(ptr[0]->name,sizeof(ptr[0]->name),"John or whatever"); // C11
你分配
ptr[0]->items = (char **)realloc(ptr[0]->items, ptr[0]->num_items * sizeof(char*))
但是
您不应投射 malloc/realloc
的 return 值(在网上搜索
解释,它被描述得令人作呕)
使用realloc
时,您应该先检查return值,它可能会失败。
char** tmp = realloc(ptr[0]->items, ptr[0]->num_items * sizeof(char*))
if (tmp != NULL)
{
ptr[0]->items = tmp;
}
else
{
abort();
}
内存重新分配 returns 部分未初始化(旧指针保留但
新的未初始化。在你的情况下,你以前没有任何
指针,以便一个 items[0]
未初始化。
所以当你这样做时
strcpy(ptr[0]->items[0], "pencil");
它将失败,因为 items[0]
指向某个任意内存位置。
在你 realloc
指针之后你需要初始化它们指向一个
足够大的内存来容纳你的字符串
例如
ptr[0]->items[0] = strdup("pencil"); // does malloc then copies string
每次需要添加一个新的时候使用realloc效率不高
项目,而不是分配一堆项目,但设置你不使用的项目
为 NULL 然后跟踪剩余的数量,一旦他们 运行 分配
又一堆
除了其他答案提供的内容之外,还有一些与您正在做的事情相关的其他注意事项。首先,不要 0
分配任何东西。那是过去的遗留物,没有必要。
接下来,在静态声明 num_persons
指向类型 struct Person
的指针的可变长度数组之后,您必须决定是一次性为所有指针分配存储空间,还是仅在添加人员时为结构分配存储空间。当您将代码拆分为函数时,这会产生影响。
由于您的数据结构的其余部分将动态分配,因此您必须在 copying/initializing 之前验证每个分配是否成功,无论如何。
分配空结构时,请仔细查看 calloc
而不是 malloc
。这是 calloc
提供的默认初始化可以提供帮助的一个实例。
您的任务的其余部分只是一个计算问题 -- 即计算哪些成员需要分配、每个分配的当前状态和大小,然后最后在不再分配的内存时释放您分配的内存需要。
以您可能需要的代码为例,将带有 item 的 person 添加到您的指针数组。要验证添加,您需要知道 ptr
是一个有效指针,并且在开始分配内存之前您是否已达到 num_persons
限制。接下来,您必须为 struct Person
分配存储空间,并将该块的地址分配给 ptx[X]
(对于您正在使用的任何索引 X
)。
分配后,您可以初始化默认值(或者如果使用了 calloc
,请知道所有值都已初始化为 0/NULL
)。之后你可以复制name
(限于ID_LEN
)并为你希望的每个item分配指针和存储保留你的指针到字符指针* items
(你将处理类似于 ptr
接受 items
的指针也被分配)注意没有必要realloc (items,...
因为这是第一次分配 items
。
如果你把所有这些放在一起,你可以想出一个函数来添加一个 person 和他们的第一个 item 相似以下,其中 max
是允许的最大 人数 ,nm
是姓名,itm
是项目,idx
是指向该人当前索引的指针:
/* 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];
}
(注意:必须在调用函数中将函数的return赋值给ptr[X]
。还要注意,C-style推荐所有小写的名字,也就是你上面看到的, 将 CamelCase 名称保留为 C++)
一旦您添加了一个人,您将希望能够将 项目 添加到与该人关联的项目列表中。这是 realloc
的用武之地,它允许您调整该人的项目指针数量。您可以执行类似于 add person 的操作,但您传递的索引不再需要是指针,因为它不会在函数内更新。例如
/* 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];
}
您现在可以将 persons 和 items 添加到列表中,但需要一种方法来遍历列表中的所有值列出您是否要使用它们。无论是搜索、打印还是释放内存,所有迭代函数都将具有相似的形式。要打印所有 persons 和 items,您可以执行类似于以下的操作:
/* 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');
}
}
在您编写的任何动态分配内存的代码中,您对分配的任何内存块负有 2 个责任:(1) 始终保留指向内存块起始地址的指针,因此,(2) 它可以是不再需要时释放。完成列表后,您需要 free
它。和打印类似,可以这样操作:
/* 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 */
}
这就是会计课的全部内容。如果您跟踪正在使用的数据结构的每个部分,那么管理内存就很简单了。将所有部分放在一个简短的示例中,您可以执行以下操作:
#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 */
}
例子Use/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
内存错误检查
您必须使用内存错误检查程序来确保您没有写入 beyond/outside 您分配的内存块,试图读取或基于未初始化的值进行跳转,并最终确认您已释放所有已分配的内存。
操作简单:
$ 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)
始终确认所有堆块都已释放——不可能存在泄漏,同样重要的是错误摘要:0 个上下文中的 0 个错误。
查看这个和其他答案。他们之间有很多好的建议。如果您还有其他问题,请告诉我们。
我在结构中有一个字符串数组,如下所示
#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];
我从 10 个人的数组开始。最初所有人都有 0 个项目,因此他们的项目列表是一个 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*));
}
有时我想给这些人起名字,然后给他们添加一些这样的项目。
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]);
}
我遇到分段错误:11。我不确定我是否正确地重新分配。
您正在为项目列表分配,但没有为每个单独的项目字符串分配 space。你的
strcpy(ptr[0]->items[0], "pencil");
会失败。
您代码中的所有问题都围绕着使用未分配的内存。 考虑
ptr[0]->items = (char **)realloc(ptr[0]->items, ptr[0]->num_items * sizeof(char*));//line 1
strcpy(ptr[0]->items[0], "pencil");//line 2
在第 1 行中,您分配了内存来保存 ptr[0]->num_items
个指向 c 的指针 strings.But 您实际上没有分配内存来存储 c 字符串,即那些指针没有指向实际内存。
当您在第 2 行尝试访问 ptr[0]->items[0]
时,它只是 char*
没有分配给它的内存。
我在第 2 行之前添加了以下行,您的代码工作正常。
for(int i=0;i<ptr[0]->num_items;i++)
ptr[0]->items[i]=malloc(sizeof(char)*10);
你这里有几个问题
首先是你的ID_LEN
你没有检查是否通过复制到名称超过 ID_LEN
所以而不是
strcpy(ptr[0]->name, "John");
使用
strcpy_s(ptr[0]->name,sizeof(ptr[0]->name),"John or whatever"); // C11
你分配
ptr[0]->items = (char **)realloc(ptr[0]->items, ptr[0]->num_items * sizeof(char*))
但是
您不应投射 malloc/realloc
的 return 值(在网上搜索
解释,它被描述得令人作呕)
使用realloc
时,您应该先检查return值,它可能会失败。
char** tmp = realloc(ptr[0]->items, ptr[0]->num_items * sizeof(char*))
if (tmp != NULL)
{
ptr[0]->items = tmp;
}
else
{
abort();
}
内存重新分配 returns 部分未初始化(旧指针保留但
新的未初始化。在你的情况下,你以前没有任何
指针,以便一个 items[0]
未初始化。
所以当你这样做时
strcpy(ptr[0]->items[0], "pencil");
它将失败,因为 items[0]
指向某个任意内存位置。
在你 realloc
指针之后你需要初始化它们指向一个
足够大的内存来容纳你的字符串
例如
ptr[0]->items[0] = strdup("pencil"); // does malloc then copies string
每次需要添加一个新的时候使用realloc效率不高 项目,而不是分配一堆项目,但设置你不使用的项目 为 NULL 然后跟踪剩余的数量,一旦他们 运行 分配 又一堆
除了其他答案提供的内容之外,还有一些与您正在做的事情相关的其他注意事项。首先,不要 0
分配任何东西。那是过去的遗留物,没有必要。
接下来,在静态声明 num_persons
指向类型 struct Person
的指针的可变长度数组之后,您必须决定是一次性为所有指针分配存储空间,还是仅在添加人员时为结构分配存储空间。当您将代码拆分为函数时,这会产生影响。
由于您的数据结构的其余部分将动态分配,因此您必须在 copying/initializing 之前验证每个分配是否成功,无论如何。
分配空结构时,请仔细查看 calloc
而不是 malloc
。这是 calloc
提供的默认初始化可以提供帮助的一个实例。
您的任务的其余部分只是一个计算问题 -- 即计算哪些成员需要分配、每个分配的当前状态和大小,然后最后在不再分配的内存时释放您分配的内存需要。
以您可能需要的代码为例,将带有 item 的 person 添加到您的指针数组。要验证添加,您需要知道 ptr
是一个有效指针,并且在开始分配内存之前您是否已达到 num_persons
限制。接下来,您必须为 struct Person
分配存储空间,并将该块的地址分配给 ptx[X]
(对于您正在使用的任何索引 X
)。
分配后,您可以初始化默认值(或者如果使用了 calloc
,请知道所有值都已初始化为 0/NULL
)。之后你可以复制name
(限于ID_LEN
)并为你希望的每个item分配指针和存储保留你的指针到字符指针* items
(你将处理类似于 ptr
接受 items
的指针也被分配)注意没有必要realloc (items,...
因为这是第一次分配 items
。
如果你把所有这些放在一起,你可以想出一个函数来添加一个 person 和他们的第一个 item 相似以下,其中 max
是允许的最大 人数 ,nm
是姓名,itm
是项目,idx
是指向该人当前索引的指针:
/* 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];
}
(注意:必须在调用函数中将函数的return赋值给ptr[X]
。还要注意,C-style推荐所有小写的名字,也就是你上面看到的, 将 CamelCase 名称保留为 C++)
一旦您添加了一个人,您将希望能够将 项目 添加到与该人关联的项目列表中。这是 realloc
的用武之地,它允许您调整该人的项目指针数量。您可以执行类似于 add person 的操作,但您传递的索引不再需要是指针,因为它不会在函数内更新。例如
/* 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];
}
您现在可以将 persons 和 items 添加到列表中,但需要一种方法来遍历列表中的所有值列出您是否要使用它们。无论是搜索、打印还是释放内存,所有迭代函数都将具有相似的形式。要打印所有 persons 和 items,您可以执行类似于以下的操作:
/* 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');
}
}
在您编写的任何动态分配内存的代码中,您对分配的任何内存块负有 2 个责任:(1) 始终保留指向内存块起始地址的指针,因此,(2) 它可以是不再需要时释放。完成列表后,您需要 free
它。和打印类似,可以这样操作:
/* 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 */
}
这就是会计课的全部内容。如果您跟踪正在使用的数据结构的每个部分,那么管理内存就很简单了。将所有部分放在一个简短的示例中,您可以执行以下操作:
#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 */
}
例子Use/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
内存错误检查
您必须使用内存错误检查程序来确保您没有写入 beyond/outside 您分配的内存块,试图读取或基于未初始化的值进行跳转,并最终确认您已释放所有已分配的内存。
操作简单:
$ 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)
始终确认所有堆块都已释放——不可能存在泄漏,同样重要的是错误摘要:0 个上下文中的 0 个错误。
查看这个和其他答案。他们之间有很多好的建议。如果您还有其他问题,请告诉我们。