线程触发SIGTRAP错误 链表学生注册程序崩溃求助
学生课程注册C程序崩溃问题排查与解决
问题描述
我编写的学生课程注册C语言程序运行时出现崩溃,调试时收到错误提示:thread 1 received signal SIGTRAP, trace/breakpoint trap。程序采用链表实现,首次注册学生功能正常,第二次注册时程序终止,退出码为3221226356。
初始代码
#include <stdio.h> #include <stdlib.h> typedef struct s *student; struct s{ long long int code; char *name; short int age; float pass_mark; bool course; struct s *next; }; student enroll(student head,long long int code1, char *name1,short int age1,bool course1){ student p,q; if(head==NULL){ head=(student)malloc(sizeof(student)); head->code=code1; head->name=name1; head->age=age1; head->course=course1; head->next=NULL; } else{p=head; q=(student)malloc(sizeof(student)); q->code=code1; q->name=name1; q->age=age1; q->course=course1; q->next=p; head=q; } return(head); } int main(){ int x=0,b; student head=NULL,q=NULL; long long int code1; char name1[50]; short int age; bool course1; do{ printf("1.register new student. \n"); printf("0.finish program.\n"); printf("Enter the number of the task needed: "); scanf("%d",&x); if(x==1){ printf("you chose 1: \n"); printf("Enter the student code:"); scanf("%lli",&code1);//code of student getchar(); printf("Enter the student name"); gets(name1);//name of student printf("Enter student age:"); scanf("%hi",&age);//age of student printf("1-Math\n"); printf("2-computer science"); printf("\nEnter the number of the course: "); scanf("%d",&b);//number of the course while((b!=1)&&(b!=2)){ printf("\nchose 1 or 2: "); scanf("%d",&b); } if(b==1) course1=1; //math course if(b==2) course1=0; //computer science course head=enroll(head,code1,name1,age,course1); //fill student info printf("------------------------------------- \n"); } }while(x!=0); return 0; }
程序运行日志
1.register new student. 0.finish program. Enter the number of the task needed: 1 you chose 1: Enter the student code:231 Enter the student name Peter Enter student age:23 1-Math 2-computer science Enter the number of the course: 1 ------------------------------------- 1.register new student. 0.finish program. Enter the number of the task needed: 1 you chose 1: Enter the student code:232 Enter the student name Gary Enter student age:38 1-Math 2-computer science Enter the number of the course: 2 -------------------------------- Process exited after 39.01 seconds with return value 3221226356 Appuyez sur une touche pour continuer...
修改后的代码(仍存在问题)
#include <stdio.h> #include <stdlib.h> #include <string.h> typedef struct s student; struct s{ long long int code; char *name; short int age; float pass_mark; bool course; struct s *next; }; student* enroll(student *head,long long int code1, char name1[50],short int age1,bool course1){ student *p,*q; p=head; q=(student*)malloc(sizeof(student)); q->code=code1; strcpy(q->name,name1); q->age=age1; q->course=course1; q->next=p; head=q; return(head); } int main(){ int x=0,b; student *head=NULL,*q=NULL; long long int code1; char name1[50]; short int age; bool course1; do{ printf("1.register new student. \n"); printf("0.finish program.\n"); printf("Enter the number of the task needed: "); scanf("%d",&x); if(x==1){ printf("you chose 1: \n"); printf("Enter the student code:"); scanf("%lli",&code1);//code of student getchar(); printf("Enter the student name: "); fgets(name1,50,stdin);//name of student printf("%s",name1); printf("Enter student age:"); scanf("%hi",&age);//age of student printf("%d",age); printf("1-Math\n"); printf("2-computer science"); printf("\nEnter the number of the course: "); scanf("%d",&b);//number of the course while((b!=1)&&(b!=2)){ printf("\nchoose 1 or 2: "); scanf("%d",&b); } if(b==1) course1=1; //math course if(b==2) course1=0; //computer science course head=enroll(head,code1,name1,age,course1); //fill student info printf("%lli",head->code); printf("%s",head->name); printf("%d",head->age); printf("%d",head->course); printf("------------------------------------- \n"); } }while(x!=0); return 0; }
核心问题分析与修复
1. 链表节点内存分配错误(初始代码致命问题)
初始代码里typedef struct s *student;把student定义成了指针类型,导致malloc(sizeof(student))只分配了一个指针的大小(通常4或8字节),但实际需要的是整个struct s结构体的内存空间。这种内存不足会导致后续写入数据时溢出,第一次操作可能没触发崩溃,第二次就会踩坏内存,引发程序终止。
- 修复:将
typedef struct s *student;改为typedef struct s student;,让student代表结构体类型,指针用student*表示;分配内存时使用malloc(sizeof(student)),确保分配足够的结构体空间。
2. 字符串未分配内存直接拷贝(修改后仍存在的问题)
修改后的代码中,结构体的char *name只是一个空指针,没有为它分配内存就直接调用strcpy(q->name, name1),这属于野指针操作,会直接破坏内存,导致崩溃。
- 修复:在拷贝名字前,先为
q->name分配足够的内存:
另外,程序结束时要遍历链表,释放每个节点的// 分配内存,长度为输入字符串的长度+1(存储结束符'\0') q->name = malloc(strlen(name1) + 1); // 检查内存分配是否成功(新手可以先加这个判断,避免分配失败) if (q->name == NULL) { printf("内存分配失败\n"); exit(1); } strcpy(q->name, name1);name和节点本身,避免内存泄漏:// 在main函数结束前添加 student *temp; while (head != NULL) { temp = head; head = head->next; free(temp->name); // 先释放名字的内存 free(temp); // 再释放节点 }
3. gets函数的风险与fgets的正确使用
- gets的致命问题:
gets()函数完全不检查输入长度,如果用户输入的字符串超过name1的50字节限制,会直接溢出缓冲区,破坏其他内存数据,导致程序崩溃甚至被恶意利用。哪怕测试时输入正常,只要输入超长就会出问题,所以它已经被C标准彻底废弃。 - fgets的正确用法:
fgets(name1, 50, stdin)三个参数的含义:name1:存储输入字符串的数组50:最大读取的字符数(包括结束符\0,实际最多读49个有效字符)stdin:读取的输入流(这里是键盘输入)
注意fgets会把输入的换行符也读进数组里,如果想要去掉换行符,可以用下面的代码:
fgets(name1, 50, stdin); // 找到换行符的位置,替换成结束符 name1[strcspn(name1, "\n")] = '\0';strcspn函数会返回第一个换行符的索引,把这个位置改成\0就能去掉多余的换行。
最终修复后的完整代码示例
#include <stdio.h> #include <stdlib.h> #include <string.h> #include <stdbool.h> // 要包含这个头文件才能用bool类型 typedef struct s student; struct s{ long long int code; char *name; short int age; float pass_mark; bool course; struct s *next; }; student* enroll(student *head, long long int code1, const char *name1, short int age1, bool course1){ student *q = malloc(sizeof(student)); if (q == NULL) { printf("内存分配失败\n"); exit(1); } q->code = code1; // 为名字分配内存并拷贝 q->name = malloc(strlen(name1) + 1); if (q->name == NULL) { printf("内存分配失败\n"); free(q); // 先释放节点,避免内存泄漏 exit(1); } strcpy(q->name, name1); q->age = age1; q->course = course1; q->next = head; return q; } void free_students(student *head) { student *temp; while (head != NULL) { temp = head; head = head->next; free(temp->name); free(temp); } } int main(){ int x=0,b; student *head=NULL; long long int code1; char name1[50]; short int age; bool course1; do{ printf("1. 注册新学生\n"); printf("0. 退出程序\n"); printf("请输入操作编号: "); scanf("%d",&x); getchar(); // 吃掉scanf留下的换行符 if(x==1){ printf("你选择了注册新学生:\n"); printf("请输入学生学号: "); scanf("%lli",&code1); getchar(); // 吃掉换行符 printf("请输入学生姓名: "); fgets(name1, 50, stdin); // 去掉换行符 name1[strcspn(name1, "\n")] = '\0'; printf("请输入学生年龄: "); scanf("%hi",&age); getchar(); // 吃掉换行符 printf("1-数学\n"); printf("2-计算机科学\n"); printf("请选择课程编号: "); scanf("%d",&b); while((b!=1)&&(b!=2)){ printf("请选择1或2: "); scanf("%d",&b); } course1 = (b == 1); // 简化写法 head = enroll(head, code1, name1, age, course1); printf("注册成功!当前头节点信息:\n"); printf("学号: %lld\n姓名: %s\n年龄: %d\n课程: %s\n", head->code, head->name, head->age, head->course ? "数学" : "计算机科学"); printf("-------------------------------------\n"); } }while(x!=0); free_students(head); // 释放所有内存 return 0; }
内容的提问来源于stack exchange,提问作者Henri
相关产品推荐
相关产品推荐

