C语言函数输入处理引发栈溢出(stack smashing)问题求助
问题排查:输入长字符串后触发栈溢出错误
我编写了一段处理命令输入的C语言代码,测试异常处理逻辑时发现:当先输入单个无效字符(如"p")再输入有效字符"b",程序能正常输出the capital is: B;但先输入长度大于1的字符串(如"awd"),再输入有效字符"b"时,程序会触发**栈溢出(stack smashing)**错误,无法定位问题原因,请求帮忙排查解决。
原代码如下:
#include <stdio.h> #include <stdlib.h> #include <math.h> #include <string.h> #include <ctype.h> char main_choice(); int main() { //case_b(); // check_if_num1(); char mainchoice [300]; printf("the capital is: %c\n", main_choice(mainchoice)); return 0; } char main_choice() { int flag1=0; int flag2=0; char choice; printf("Select one of the following commands: \n"); printf("B) - Binary Mathematical Operations, such as addition and subtraction.\n"); printf("U) - Unary Mathematical operations, such as square root, and log.\n"); printf("A) - Advances Mathematical Operations, using variables, arrays.\n"); printf("V) - Define variables and assign them values.\n"); printf("E) - Exit\n"); scanf(" %[^\n]%*c",&choice); while (1) { if (strlen(&choice) == 1) { do { if (choice == 'b') { choice = 'B'; return choice; flag1 = 1; //flag2 = 1; break; } else if (choice == 'u') { choice = 'U'; return choice; flag1 = 1; //flag2 = 1; break; } else if (choice == 'a') { choice = 'A'; return choice; flag1 = 1; //flag2 = 1; break; } else if (choice == 'v') { choice = 'V'; return choice; flag1 = 1; //flag2 = 1; break; } else if (choice == 'e') { choice = 'E'; return choice; flag1 = 1; //flag2 = 1; break; } // if (strlen(&choice) == 1 && choice != 'b' && choice != 'u' && choice != 'a' && choice != 'v' && choice != 'e') else { //flag2 =0; printf("Invalid entry\n"); printf("----------------------------------------------------------\n\n"); printf("Select one of the following commands: \n"); printf("B) - Binary Mathematical Operations, such as addition and subtraction.\n"); printf("U) - Unary Mathematical operations, such as square root, and log.\n"); printf("A) - Advances Mathematical Operations, using variables, arrays.\n"); printf("V) - Define variables and assign them values.\n"); printf("E) - Exit\n"); scanf(" %[^\n]%*c",&choice); } }while(flag1); //break; } else { printf("Invalid entry\n"); printf("----------------------------------------------------------\n\n"); printf("Select one of the following commands: \n"); printf("B) - Binary Mathematical Operations, such as addition and subtraction.\n"); printf("U) - Unary Mathematical operations, such as square root, and log.\n"); printf("A) - Advances Mathematical Operations, using variables, arrays.\n"); printf("V) - Define variables and assign them values.\n"); printf("E) - Exit\n"); scanf(" %[^\n]%*c",&choice); } //break; } //return choice; }
核心问题原因
- 缓冲区溢出直接破坏栈结构:
main_choice函数中定义的char choice是单个字符变量,但使用scanf(" %[^\n]%*c", &choice)读取输入。%[^\n]会读取所有非换行符的字符,当输入长度大于1的字符串时,超出单个字符的内容会直接写入choice内存地址之外的栈空间,破坏栈帧数据,触发栈溢出。strlen(&choice)用法错误:strlen要求传入以\0结尾的字符串首地址,但choice是单个字符,没有终止符,函数会越界读取内存直到找到\0,结果不可预测,进一步加剧内存访问异常。
- 函数声明与定义不匹配:
- 声明的
char main_choice()无参数,但main中调用时传入了mainchoice,函数定义却未接收参数,属于未定义行为,干扰栈的正常布局。
- 声明的
修复后的代码
#include <stdio.h> #include <stdlib.h> #include <math.h> #include <string.h> #include <ctype.h> // 修正函数声明,明确接收缓冲区参数 char main_choice(char *input_buf); int main() { char mainchoice[300]; printf("the capital is: %c\n", main_choice(mainchoice)); return 0; } // 使用缓冲区存储输入,避免单个字符溢出问题 char main_choice(char *input_buf) { while (1) { // 打印命令提示 printf("Select one of the following commands: \n"); printf("B) - Binary Mathematical Operations, such as addition and subtraction.\n"); printf("U) - Unary Mathematical operations, such as square root, and log.\n"); printf("A) - Advances Mathematical Operations, using variables, arrays.\n"); printf("V) - Define variables and assign them values.\n"); printf("E) - Exit\n"); // 限制读取长度为299,避免缓冲区溢出;%*c吃掉换行符 if (scanf(" %299[^\n]%*c", input_buf) != 1) { // 处理输入错误,清空输入缓冲区 while (getchar() != '\n'); printf("Invalid entry\n"); printf("----------------------------------------------------------\n\n"); continue; } // 检查输入长度是否为1 if (strlen(input_buf) != 1) { printf("Invalid entry\n"); printf("----------------------------------------------------------\n\n"); continue; } // 统一转为大写,简化判断逻辑 char choice = toupper(input_buf[0]); if (choice == 'B' || choice == 'U' || choice == 'A' || choice == 'V' || choice == 'E') { return choice; } else { printf("Invalid entry\n"); printf("----------------------------------------------------------\n\n"); } } }
关键修复点
- 使用缓冲区存储输入:利用
main中定义的mainchoice[300]作为输入缓冲区,scanf通过%299[^\n]限制读取长度,彻底避免溢出。 - 修正内存访问逻辑:传入合法的以
\0结尾的字符串给strlen,确保长度判断准确。 - 简化命令判断:用
toupper统一转换输入大小写,减少冗余代码。 - 修复函数匹配问题:让函数接收缓冲区参数,消除未定义行为。
- 增加错误处理:当
scanf失败时清空输入缓冲区,避免死循环。
内容的提问来源于stack exchange,提问作者Ghostrocket 017
相关产品推荐
相关产品推荐

