C语言结构体嵌套读文件报错:类型不兼容且数据仅存最后一行
问题解决:读取products.txt到recipe结构体的错误修复
问题背景
正在开发程序从stockroom.txt和products.txt读取数据并存储到指定结构体,结构体定义如下:
// 来自stockroom.txt的数据 struct ingredient { char* name; int number; }; struct dose { int index; int amount; }; // 来自products.txt的数据 struct recipe { char* name; int price; int number_ing; int current_price; int best_price; struct dose* ingredients; };
实现read_recipes函数读取products.txt时出现两个问题:
- GCC报错:
incompatible types when assigning to type 'struct dose' from type 'struct dose **' - 调试发现仅保存最后一行配料数据
原read_recipes函数代码:
void read_recipes(int nr, int ni, struct recipe* recipes[], struct ingredient* ingredients[], FILE* products){ int i, j, k; for (i = 0; i < nr; i++){ char tmp_s[31]; int tmp_n, n; fscanf(products, "%s %d %d", tmp_s, &tmp_n, &n); recipes[i] = (struct recipe *)malloc(sizeof(struct recipe)); recipes[i] -> name = strdup(tmp_s); recipes[i] -> price = tmp_n; recipes[i] -> number_ing = n; recipes[i] -> current_price = 0; recipes[i] -> best_price = 0; recipes[i] -> ingredients = NULL; for(j = 0; j < n; j ++){ char tmp_name[21]; int tmp_amount; struct dose* doses[n]; doses[j] = (struct dose*)malloc(sizeof(struct dose)); fscanf(products, "%s %d", tmp_name, &tmp_amount); for(k = 0; k < ni; k++){ if(strcmp(ingredients[k] -> name, tmp_name) == 0){ doses[j] -> index = k; doses[j] -> amount = tmp_amount; } } recipes[i] -> ingredients = doses; // 错误位置 } } }
对应文件内容:
- stockroom.txt:
6 flour 35 sugar 40 eggs 20 milk 25 cocoa 48 jam 10
- products.txt:
3 Easter_eggs 6 2 cocoa 8 milk 3 Sacher_cake 22 3 flour 4 cocoa 14 jam 2 Mimosa_cake 13 5 eggs 3 flour 4 sugar 6 milk 4 jam 2
错误原因分析
- 类型不匹配:
recipes[i]->ingredients的类型是struct dose*(用作连续结构体数组的指针),而doses是struct dose*[](指针数组,本质为struct dose**),直接赋值导致类型兼容报错。 - 内存管理错误:
struct dose* doses[n];是j循环内的栈数组,每次循环都会重建,出循环后内存自动释放;且每次循环都覆盖赋值ingredients,最终仅保留最后一次循环的无效内存地址。 - 冗余内存分配:单个
dose无需单独malloc,应为整个配料数组分配连续内存,直接操作数组元素即可。
修复后的代码
#include <stdio.h> #include <stdlib.h> #include <string.h> void read_recipes(int nr, int ni, struct recipe* recipes[], struct ingredient* ingredients[], FILE* products){ int i, j, k; for (i = 0; i < nr; i++){ char tmp_s[31]; int tmp_n, n; fscanf(products, "%s %d %d", tmp_s, &tmp_n, &n); recipes[i] = malloc(sizeof(struct recipe)); recipes[i]->name = strdup(tmp_s); recipes[i]->price = tmp_n; recipes[i]->number_ing = n; recipes[i]->current_price = 0; recipes[i]->best_price = 0; // 提前为当前食谱的配料数组分配连续内存 recipes[i]->ingredients = malloc(n * sizeof(struct dose)); if (recipes[i]->ingredients == NULL) { fprintf(stderr, "内存分配失败:无法为食谱配料分配空间\n"); exit(EXIT_FAILURE); } for(j = 0; j < n; j++){ char tmp_name[21]; int tmp_amount; fscanf(products, "%s %d", tmp_name, &tmp_amount); // 查找对应配料的索引,找到后立即退出循环 for(k = 0; k < ni; k++){ if(strcmp(ingredients[k]->name, tmp_name) == 0){ recipes[i]->ingredients[j].index = k; recipes[i]->ingredients[j].amount = tmp_amount; break; } } } } }
关键修改点
- 提前分配配料数组内存:在进入配料循环前,根据
n(配料数量)为recipes[i]->ingredients分配连续的struct dose数组内存,解决类型不匹配和内存失效问题。 - 直接操作数组元素:移除栈上的
doses数组,直接通过recipes[i]->ingredients[j]访问并赋值配料的index和amount,无需单独分配单个dose的内存。 - 添加内存分配检查:增加
malloc后的空指针校验,避免内存分配失败引发后续错误。 - 优化查找逻辑:找到匹配配料后立即
break,减少不必要的循环遍历。
验证说明
修复后,程序可正确读取products.txt中的所有配料数据,每个食谱的ingredients数组会完整保存对应数量的配料信息,不再出现类型错误和数据丢失问题。
内容的提问来源于stack exchange,提问作者Azizbek Sattorov
相关产品推荐
相关产品推荐

