1

我想从键盘读取一个字符串并存储在buf. 我设置了一个char buf[6]数组,这个数组最多可以存储5个字符和\0

然后我键入123 456 789它包含 11 个字符和一个\0,程序仍然可以运行,但如果我键入更长的字符串123 456 789 123 456 789,它会在运行时崩溃。这两个输入也超出范围buf,但是一个可以运行,另一个崩溃?

这是我的代码:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

void read_str();

int main(){
    read_str();
    system("pause");
    return 0;
}
void read_str(){

    char buf[6] = {};
    scanf("%[^\n]",buf);
    printf("%d\n",strlen(buf));
    printf("%s\n",buf);
}
4

2 回答 2

3

这只是在分配的内存范围之外写入的未定义行为。它现在可以工作,但不能依赖它来工作。附件未定义行为中的C99 草案标准说:J.2

数组下标超出范围,即使一个对象显然可以使用给定的下标访问(如在给定声明 int a[4][5] 的左值表达式 a[1][7] 中)(6.5.6)。

请注意,定义第2段中的术语的3.4.3 未定义行为部分说(强调我的):

可能的未定义行为范围从完全忽略具有不可预测结果的情况,到在翻译或程序执行期间以环境特征的记录方式表现(有或没有发出诊断消息),到终止翻译或执行(有发出的诊断消息)。

于 2013-09-28T02:40:55.347 回答
3

真正的原因很可能是因为您在函数调用中只是覆盖了堆栈的内容,并且在您尝试将字符写入底部之前,您实际上并没有获得您不拥有的内存其中。即使它没有崩溃,但这几乎总是很糟糕,因为您正在覆盖程序出于某种原因放在那里的值。毕竟,如果每次覆盖缓冲区时总是崩溃,那么缓冲区溢出错误就永远不会发生,我们知道它们会发生。

例如,您的堆栈可能会向下增长。当您进行函数调用时,您可能会获得寄存器值、返回地址、参数值和其他放入堆栈的内容。然后,只有这样,你的 6 个字节buf才会被分配。如果所有其他内容占用了 12 个字节,那么您可以写入 18 个字符,buf并且仍然只触及不应该更改但您的进程拥有的内存。由于您的进程拥有它,因此您不会获得非法内存访问,也不会崩溃。一旦超过 18 个字节,您很可能会进入您的进程不拥有的内存,并且您将得到一个段错误并且游戏将启动。

C 的原因是您只是有未定义的行为,并且发生了您甚至不应该尝试理解的奇怪事情。

于 2013-09-28T02:42:17.233 回答