C语言如何引用结构体内部嵌套的另一结构体指针
问题修复说明
直接崩溃原因
- 调用
scanf读取年龄字段时未传入地址:p[i].pet_age->years是int类型的值,scanf接收int参数需要传入对应变量的指针,正确写法是&(p[i].pet_age->years),月份字段同理,这是程序输入月份后崩溃的直接原因。
其他隐藏逻辑错误
- 函数参数传递错误:C语言是值传递,你在
addPet函数内修改形参p的指向(malloc/realloc操作)不会影响main函数里的petarray变量,导致主函数永远拿不到正确的数组地址。 - 首次调用判断写错:
if(i=1)是赋值操作,永远返回真,每次调用addPet都会重新申请内存,原有数据全部丢失还会造成内存泄漏,应该改成if(i==1)的相等判断。 - 字符串内存分配不足:
sex字段只分配了1字节的内存,字符串结尾需要额外1字节存\0,至少要分配2字节,否则scanf写入会越界。 - 内存分配大小写错:首次
malloc(k*sizeof(p))里p是指针类型,sizeof(p)得到的是指针的大小,不是pet结构体的大小,应该改成sizeof(pet)或者sizeof(*p)。 pet_age分配方式不规范:malloc(sizeof(int)*2)虽然刚好匹配age结构体的大小,但是可读性差,后续如果结构体修改很容易出问题,建议改成malloc(sizeof(age))。- 数组下标浪费:全局计数器
i从1开始,数组下标0的位置永远被浪费,建议从0开始计数。
提示:建议后续功能实现完成后,注意补充边界校验逻辑,避免非法输入导致程序崩溃,下述修复代码已在退出流程中加入了基础的内存释放逻辑,避免内存泄漏
修复后完整可运行代码
#include <stdio.h> #include <stdlib.h> typedef struct age { int years, months; } age; typedef struct pet { int id; char* sex; char* breed; age* pet_age; } pet; int i = 0; // 计数器从0开始,不浪费数组空间 int k = 10; // 默认初始容量 // 改成传二级指针,才能修改main里的petarray的指向 void addPet(pet **p){ // 首次调用初始化数组 if(i == 0){ *p = malloc(k * sizeof(pet)); if(!*p){ printf("\n内存分配失败..."); exit(0); } } // 给当前pet的各个成员分配内存 (*p)[i].sex = malloc(sizeof(char)*2); // 存单个字符加结束符\0 (*p)[i].breed = malloc(sizeof(char)*20); (*p)[i].pet_age = malloc(sizeof(age)); (*p)[i].id = i+1; // 自增ID从1开始,符合用户习惯 printf("\n宠物ID:%d\n", (*p)[i].id); printf("输入宠物品种:"); scanf("%s", (*p)[i].breed); printf("输入宠物性别(M/F):"); scanf("%s", (*p)[i].sex); printf("输入宠物年龄(年):"); scanf("%d", &((*p)[i].pet_age->years)); // 传入地址 printf("已输入年份:%d\n", (*p)[i].pet_age->years); printf("输入宠物年龄(月):"); scanf("%d", &((*p)[i].pet_age->months)); // 传入地址 printf("已输入月份:%d\n", (*p)[i].pet_age->months); i++; // 容量不足扩容 if(i == k){ k += 10; *p = realloc(*p, k * sizeof(pet)); if(!*p){ printf("\n内存扩容失败..."); exit(0); } } } int main(int argc, char *argv[]) { int choice; pet *petarray = NULL; // 初始化野指针为NULL while(1){ printf("\n\n 菜单:"); printf("\n========="); printf("\n1. 新增宠物信息"); printf("\n2. 根据ID删除宠物记录"); printf("\n3. 根据品种查询宠物记录"); printf("\n4. 修改品种名称"); printf("\n5. 退出\n\n"); scanf("%d", &choice); switch(choice) { case 1: addPet(&petarray); // 传入指针的地址 break; case 2: // 后续实现删除逻辑 break; case 3: // 后续实现查询逻辑 break; case 4: // 后续实现修改逻辑 break; case 5: // 退出前释放所有申请的内存,避免内存泄漏 for(int j=0;j<i;j++){ free(petarray[j].sex); free(petarray[j].breed); free(petarray[j].pet_age); } free(petarray); printf("程序退出..."); exit(0); } } return 0; }
内容的提问来源于stack exchange,提问作者JustDream
相关产品推荐
相关产品推荐

