如何动态分配结构体指针数组并逐次传入单个结构体参数?
看起来你已经搭好了初始化的架子,现在咱们把addit函数补全,顺便加上内存释放的逻辑,确保整个流程的内存管理是安全的。
首先先上完整的代码:
#include <stdio.h> #include <stdlib.h> #include <stdarg.h> #include <string.h> typedef struct _DROP_DOWN_MENU_LABELS { char *title; char **items; } MenuLabels; // 初始化结构体指针数组,初始分配2个指针位置(一个存数据,一个做NULL标记) MenuLabels ** ml_init(void) { MenuLabels **ml; // 用calloc初始化,自动置0,最后一个指针自然是NULL ml = calloc(2, sizeof(MenuLabels*)); return ml; } // 向数组中添加新的菜单标签项 void addit(MenuLabels **mlabels, char *title, ...) { if (!mlabels || !title) { fprintf(stderr, "Invalid input parameters\n"); return; } // 第一步:先找到当前数组中已使用的元素个数,确定扩容位置 int count = 0; while (mlabels[count] != NULL) { count++; } // 第二步:动态扩容数组——当前数组长度是count+1(含NULL标记),扩容后长度+1 MenuLabels **temp = realloc(mlabels, (count + 2) * sizeof(MenuLabels*)); if (!temp) { fprintf(stderr, "Realloc failed\n"); return; } mlabels = temp; mlabels[count + 1] = NULL; // 新的末尾标记为NULL // 第三步:分配新的MenuLabels结构体空间 mlabels[count] = malloc(sizeof(MenuLabels)); if (!mlabels[count]) { fprintf(stderr, "Malloc for MenuLabels failed\n"); return; } // 第四步:复制title字符串(避免直接指向原指针,防止原内存被释放出问题) mlabels[count]->title = malloc(strlen(title) + 1); if (!mlabels[count]->title) { free(mlabels[count]); fprintf(stderr, "Malloc for title failed\n"); return; } strcpy(mlabels[count]->title, title); // 第五步:处理可变参数的items,先统计item数量 va_list args; va_start(args, title); char *item; int item_count = 0; while ((item = va_arg(args, char*)) != NULL) { item_count++; } va_end(args); // 分配items数组空间(最后加一个NULL标记) mlabels[count]->items = calloc(item_count + 1, sizeof(char*)); if (!mlabels[count]->items) { free(mlabels[count]->title); free(mlabels[count]); fprintf(stderr, "Malloc for items failed\n"); return; } // 再次遍历可变参数,复制每个item字符串 va_start(args, title); int i = 0; while ((item = va_arg(args, char*)) != NULL) { mlabels[count]->items[i] = malloc(strlen(item) + 1); if (!mlabels[count]->items[i]) { // 内存分配失败时,要释放已分配的item和结构体资源 for (int j = 0; j < i; j++) { free(mlabels[count]->items[j]); } free(mlabels[count]->items); free(mlabels[count]->title); free(mlabels[count]); va_end(args); fprintf(stderr, "Malloc for item failed\n"); return; } strcpy(mlabels[count]->items[i], item); i++; } va_end(args); } // 释放整个结构体指针数组的内存 void ml_free(MenuLabels **mlabels) { if (!mlabels) return; int count = 0; while (mlabels[count] != NULL) { // 释放title free(mlabels[count]->title); // 释放每个item int i = 0; while (mlabels[count]->items[i] != NULL) { free(mlabels[count]->items[i]); i++; } // 释放items数组 free(mlabels[count]->items); // 释放结构体本身 free(mlabels[count]); count++; } // 释放结构体指针数组 free(mlabels); } // 测试用例 int main() { MenuLabels **menu = ml_init(); addit(menu, "File", "New", "Open", "Save", "Exit", NULL); addit(menu, "Edit", "Cut", "Copy", "Paste", NULL); // 打印测试 int i = 0; while (menu[i] != NULL) { printf("Title: %s\n", menu[i]->title); printf("Items:\n"); int j = 0; while (menu[i]->items[j] != NULL) { printf(" - %s\n", menu[i]->items[j]); j++; } printf("\n"); i++; } // 释放内存 ml_free(menu); return 0; }
关键细节说明
- 动态扩容逻辑:每次添加元素前,先遍历数组找到当前已有的元素个数,然后用
realloc扩容数组,始终保持最后一个指针为NULL,方便后续遍历和判断数组边界。 - 字符串内存管理:所有字符串(title和items)都单独分配内存,避免直接复用传入的指针,防止原内存被释放后出现野指针问题。
- 错误处理:每一步内存分配都做了失败判断,一旦分配失败,会释放已经分配的资源,避免内存泄漏。
- 内存释放:专门写了
ml_free函数,递归式地释放所有层级的内存(item字符串→items数组→MenuLabels结构体→结构体指针数组)。
你可以直接运行这个测试用例,看看效果是否符合你的预期。如果有其他需求,比如修改扩容策略(比如每次扩容固定大小而不是+1),可以调整realloc的参数。
内容的提问来源于stack exchange,提问作者klavdio
相关产品推荐
相关产品推荐

