没有出界错误

Cem*_*mre 12 c indexoutofboundsexception

我在C中有这个代码,它接受了一堆chars

#include<stdio.h> 
# define NEWLINE '\n'
int main()
{

char c;
char str[6];
int i = 0;
while( ((c = getchar()) != NEWLINE))
{
        str[i] = c;
        ++i;
        printf("%d\n", i);
}

return 0;
}
Run Code Online (Sandbox Code Playgroud)

输入是:testtesttest

输出:1 2 3 4 5 6 7 8 117 118 119 120

我的问题是:

  1. 虽然我明显超出了阵列的容量,为什么我不会出现超出界限(分段错误)的异常?

  2. 为什么输出中的数字会突然跳到很大的数字?

我在C++中试过这个并且得到了相同的行为.请问有谁可以解释一下这是什么原因?

Car*_*rum 26

  1. C不检查数组边界.仅当您尝试取消引用程序无权访问的内存指针时,才会发生分段错误.简单地越过数组的末尾不太可能导致这种行为.未定义的行为只是 - 未定义.它似乎工作得很好,但你不应该依赖它的安全.
  2. 您的程序通过访问数组末尾的内存导致未定义的行为.在这种情况下,看起来您的一个str[i] = c写入会覆盖该值i.
  3. 在这种情况下,C++与C具有相同的规则.

  • 当然,如果传递`-fcatch-undefined-behavior`,clang会在某些情况下执行此操作.但这并不意味着它是语言的一部分. (3认同)

zne*_*eak 6

访问数组索引时,C和C++不进行绑定检查.只有当您尝试读取或写入未分配的页面(或尝试在不允许的页面上执行某些操作,例如尝试写入只读页面)时,才会发生分段错误,但由于页面通常是非常大(几千字节的倍数;在Mac OS上,4 KB的倍数),它经常会留下很多空间来溢出.

如果你的阵列在堆栈上(就像你的一样),它可能会更糟,因为堆栈通常非常大(高达几兆字节).这也是安全问题的原因:写入堆栈上的数组边界可能会覆盖函数的返回地址并导致任意代码执行(着名的"缓冲区溢出"安全漏洞).

您阅读时获得的值正是在这个特定的地方发生的事情.它们完全没有定义.

如果你使用C++(并且很幸运能够使用C++ 11),标准定义了std::array<T, N>类型,它是一个知道其边界的数组.at如果您尝试阅读它的结尾,该方法将抛出.

  • 当程序出现段错误时,首先发现问题的始终是硬件,而不是操作系统。硬件调用操作系统来处理段错误,然后硬件可能会从磁盘加载一些数据,或者提供零页,或者向有问题的进程传递信号。无论操作系统做什么,它都受到硬件页面大小粒度的限制。X86 上的硬件页面大小恰好为 4kiB。 (2认同)