C语言字符串行为与atoi函数:if条件不生效及缓冲区溢出疑问
明明数值相等却触发不了if判断?缓冲区溢出的坑你踩中了!
兄弟,你遇到的这个诡异问题百分百是缓冲区溢出搞的鬼!咱们结合你的代码一点点拆解:
首先把你没贴全的代码补全(猜也猜得到你肯定有读取输入到instring的操作):
#include <stdio.h> #include <stdlib.h> #include <time.h> int main(void) { srand(time(NULL)); char instring[2]; // 增大该数组长度时程序行为恢复正常 int inint; int guess; guess = rand() % 127; // 这里应该是你读取输入的代码,比如: scanf("%s", instring); inint = atoi(instring); printf("guess: %i\n", guess); printf("inint: %i\n", inint); // 明明printf显示相等,却进不了这个if? if (inint == guess) { printf("猜对啦!"); } else { printf("怎么不对?明明数值一样啊!"); } return 0; }
问题根源:instring[2]的缓冲区溢出
你定义的instring只有2个字节的空间,但C语言里字符串是以**\0(空字符)**结尾的——这意味着这个数组最多只能存1个可见字符加结束符。如果你输入的字符串长度超过1(比如输入"12"、"abc"之类的),多余的字符就会“溢出”到数组之外的内存区域。
而在C语言的栈内存布局里,局部变量是挨着存放的(具体顺序取决于编译器,但肯定是相邻的)。instring溢出的字节会直接覆盖相邻的变量内存——比如你的inint或者guess,甚至是栈帧里的函数返回地址、ebp寄存器的值!
为什么printf显示相等却进不了if?
这就是未定义行为的诡异之处,几种可能性供你参考:
- 溢出刚好把
guess或者inint的内存改得让printf读取时显示相等,但实际在if判断时,栈上的变量又被后续操作(比如printf的栈操作)篡改了; - 溢出破坏了栈帧中的关键数据(比如函数的返回地址、ebp指针),导致程序的执行流程被打乱——看起来if判断的代码还在,但实际跳转逻辑已经被破坏了;
- 编译器优化把变量放到了寄存器里,溢出修改的是内存中的值,但printf用的是寄存器里的旧值,而if判断时读取的是被篡改后的内存值(这种情况概率低,但未定义行为什么都可能发生)。
缓冲区溢出的危害远不止这个!
你问缓冲区溢出会不会影响其他代码?那可太会了:
- 轻一点的是破坏变量值、导致程序逻辑混乱(就像你遇到的情况);
- 严重的会直接导致程序崩溃(比如覆盖了返回地址,让程序跳转到非法内存);
- 更危险的是,恶意攻击者可以利用缓冲区溢出注入恶意代码,获取程序的控制权(这是很多漏洞的根源)。
怎么解决?
最简单的就是你已经发现的:增大instring的长度,确保能容纳你输入的字符串加\0。但更规范的做法是:
- 使用带长度限制的输入函数,比如
scanf("%1s", instring)(限制最多读1个字符,留位置给\0); - 或者用
fgets代替scanf,明确指定读取的最大长度:fgets(instring, sizeof(instring), stdin)。
内容的提问来源于stack exchange,提问作者user6398437
相关产品推荐
相关产品推荐

