没有越界的错误

我有这个代码在C中,需要在一堆char

 #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; } 

输入是:testtesttest

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

我的问题是:

  1. 为什么我不明显超出数组的容量,出现越界(分段错误)异常?

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

我在C ++中尝试了这一点,并得到了相同的行为。 任何人都可以请解释这是什么原因?

  1. C不检查数组边界。 如果尝试将指针取消引用到内存中,则程序没有权限访问,只会发生分段错误。 简单地通过一个数组的结束是不可能导致这种行为。 未定义的行为就是这样 – 未定义。 它可能似乎工作得很好,但你不应该依靠它的安全。
  2. 您的程序通过访问超出数组末尾的内存导致未定义的行为。 在这种情况下,它看起来像你的str[i] = c写入覆盖i的值。
  3. C ++在这种情况下具有与C相同的规则。

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

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

你读的时候得到的价值就是在这个特定的地方会发生什么。 他们是完全不确定的

如果你使用C ++(并且很幸运可以使用C ++ 11),那么标准定义了std::array<T, N>类型,它是一个知道它的边界的数组。 如果你试图读取结尾, at方法会抛出。

C不检查数组边界。

事实上,分段错误并不是特定的超出数组边界而产生的运行时错误。 而是由操作系统提供的内存保护的结果。 当您的进程尝试访问不属于它的内存,或者尝试访问不存在的内存地址时,就会发生这种情况。

因为C / C ++不检查边界。

数组是指向内存中的一个位置的内部指针。 当你调用arr[index] ,它所做的是:

 type value = *(arr + index); 

结果是大数字(不一定),因为它们是垃圾值。 就像一个未初始化的变量。

在数组之外写入(实际上甚至只是执行指针算术/数组下标,即使你不使用结果来读或写任何东西)也会导致未定义的行为 。 未定义的行为不是报告或可报告的错误; 这意味着你的程序可以做任何事情。 这是非常危险的,你完全有责任避免它。 C不是Java / Python /等。

内存分配比看起来更复杂。 在这种情况下,变量“str”在堆栈中,在其他变量旁边,所以它之后没有未分配的内存。 内存通常也是字对齐的(一个“字”是四到八个字节)。你可能会搞乱另一个变量的值,或者用一些“填充”(为了维护字对齐而添加空的空间)或其他东西。

像R ..说,这是不确定的行为。 越界条件可能会导致段错误…或者他们可能导致无声的内存损坏。 如果您正在修改已分配的内存,则操作系统不会捕获这些内存。 这就是为什么超越界错误在C中是如此阴险

你必须这样编译:

 gcc -fsanitize=address -ggdb -o test test.c 

这里有更多的信息。