C语言如何通过函数访问嵌套结构体指针并修复段错误
段错误问题根因与修复方案
核心问题点
代码触发段错误本质是多层野指针未分配合法内存导致的非法内存访问,具体问题有3处:
main函数中声明的Train *t是未初始化的野指针,没有为Train结构体申请对应的内存空间,直接传入getTrain写入成员本身就是非法操作。- 即使
Train实例内存合法,其内部的Date *d成员同样是未初始化的野指针,没有为Date结构体申请内存,传入getDate后访问d->jour、d->dateDepart等成员时,会直接访问随机内存地址触发段错误。 getDate函数中对d->dateDepart调用strcat前没有初始化字符串,strcat会从目标内存中第一个\0的位置开始拼接,未初始化的字符数组内容是随机值,找不到终止符时会越界写内存,同样会触发异常。
另外还有一处不规范写法:scanf读取字符串时,字符数组名本身就是首地址,不需要额外加&取地址,虽然多数编译器不会报错,但属于语义错误。
修复后完整代码
结构体定义(保持原有逻辑不变)
#define MAX_VAL 64 #include <stdio.h> #include <stdlib.h> #include <string.h> typedef struct date{ char dateDepart[MAX_VAL]; int mois; int jour; int annee; }Date; typedef struct train{ int numero; char villeDepart[MAX_VAL]; char villeArrivee[MAX_VAL]; Date *d; }Train;
修复getDate:初始化目标字符串
void getDate(Date *d){ printf("Jour: "); scanf("%d",&d->jour); printf("Mois: "); scanf("%d",&d->mois); printf("Annee: "); scanf("%d",&d->annee); // 先将目标字符串置空,保证strcat从起始位置开始拼接 d->dateDepart[0] = '\0'; char tmp [MAX_VAL]; itoa(d->jour,tmp,10); strcat(d->dateDepart,tmp); strcat(d->dateDepart,"/"); itoa(d->mois,tmp,10); strcat(d->dateDepart,tmp); strcat(d->dateDepart,"/"); itoa(d->annee,tmp,10); strcat(d->dateDepart,tmp); }
修复getTrain:去掉scanf多余的取地址符
void getTrain(Train *t){ printf("Saisie Train: "); scanf("%d",&t->numero); printf("Ville Depart: "); scanf("%s",t->villeDepart); printf("Ville Arrivee: "); scanf("%s",t->villeArrivee); printf("=========== Date Depart ============\n"); getDate(t->d); printf("====================================\n"); } // 补全putTrain示例实现,用于打印验证 void putTrain(Train *t) { printf("\n--- Train Info ---\n"); printf("Numero: %d\n", t->numero); printf("Ville Depart: %s\n", t->villeDepart); printf("Ville Arrivee: %s\n", t->villeArrivee); printf("Date Depart: %s\n", t->d->dateDepart); }
修复main:为两层指针分配合法内存,用完释放
int main(){ // 为Train结构体申请堆内存 Train *t = (Train*)malloc(sizeof(Train)); if (t == NULL) { perror("alloc Train failed"); return EXIT_FAILURE; } // 为Train内部的Date指针成员申请堆内存 t->d = (Date*)malloc(sizeof(Date)); if (t->d == NULL) { perror("alloc Date failed"); free(t); return EXIT_FAILURE; } getTrain(t); putTrain(t); // 用完释放内存,避免泄漏:先释放内部嵌套的指针,再释放外层 free(t->d); free(t); return EXIT_SUCCESS; }
关键编写规范
- 所有指针在解引用(用
->访问成员、用*取值)前,必须确保它指向了合法可访问的内存:要么指向栈上已定义的同类型变量,要么通过malloc/calloc申请了足够的堆内存,未初始化的指针就是野指针,访问必触发内存错误。 - 用
strcat做字符串拼接前,必须保证目标字符串是已初始化、以\0结尾的合法C字符串,否则会出现越界写入。 - 堆内存申请和释放必须成对出现,嵌套结构体要先释放内部成员的堆内存,再释放外层结构体,避免内存泄漏。
内容的提问来源于stack exchange,提问作者j1kk4
相关产品推荐
相关产品推荐

