没有越界错误

Posted

技术标签:

【中文标题】没有越界错误【英文标题】:No out of bounds error 【发布时间】:2012-02-26 13:45:09 【问题描述】:

我在 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;

输入是:testtesttest

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

我的问题是:

    虽然我明显超出了数组的容量,但为什么没有出现越界(分段错误)异常?

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

我在 C++ 中尝试过这个并得到了相同的行为。谁能解释一下这是什么原因?

【问题讨论】:

Accessing an array out of bounds gives no error, why?、How dangerous is it to access an array out of bounds?等可能重复 【参考方案1】:

你必须像这样编译:

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

There is more information here.

【讨论】:

-fsanitize=undefined 实际上是一个更强大的诊断。 address 只会在运行时代码段错误时触发。相反,undefined 会为所有未定义的行为触发运行时错误,无论运行时是否存在段错误。还存在其他 fsanitize 参数,记录在 man gcc 中。【参考方案2】:

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

数组是指向内存中某个位置的内部指针。当您致电arr[index] 时,它的作用是:

type value = *(arr + index);

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

【讨论】:

@R.. 是的,有:“我在 C++ 中尝试过这个并得到了相同的行为。谁能解释一下这是什么原因?”。 一个挑剔的点:数组不是指针。它们只是正常值。 它们更像是“参考”类型。但在底层它们是指针,就像引用实际上是内部指针一样。因此他们可以被尊重。并且还投到type* 数组绝对不是指针。完全没有。数组变量在某些上下文中可以衰减为指针类型这一事实与此无关。 @CarlNorum 再次学习如何阅读。在内部。也就是说,如果您不相信我,请阅读汇编代码。【参考方案3】:

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

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

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

如果您使用 C++(并且有幸使用 C++11),标准定义了 std::array&lt;T, N&gt; 类型,这是一个知道其边界的数组。 at 方法如果你试图读到它的末尾就会抛出。

【讨论】:

当程序出现段错误时,首先识别出问题的总是硬件,而不是操作系统。硬件调用操作系统来处理段错误,然后可能会从磁盘加载一些数据,或提供零页,或将信号传递给有问题的进程。无论操作系统做什么,它都受限于硬件页面大小的粒度。 X86 上的硬件页面大小恰好是 4kiB。【参考方案4】:

内存分配比看起来更复杂。在这种情况下,变量“str”位于堆栈上,紧挨着其他变量,因此它后面没有未分配的内存。内存通常也是字对齐的(一个“字”是四到八个字节。)您可能弄乱了另一个变量的值,或者一些“填充”(添加空白以保持字对齐)或其他完全.

就像 R.. 所说,这是未定义的行为。越界条件可能会导致段错误......或者它们可能导致静默内存损坏。如果您正在修改已分配的内存,则操作系统不会捕获到。这就是为什么越界错误在 C 中如此隐蔽的原因。

【讨论】:

【参考方案5】:

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

【讨论】:

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

【讨论】:

我认为某些 C 编译器允许您以较慢的运行时间为代价选择数组边界检查,还是 C++?查看数组en.wikipedia.org/wiki/C_%28programming_language%29 @octopusgrabbus 当然,它可能是某些编译器的一项功能,但边界检查不是标准本身的一部分。 你是正确的马龙。但是,如果它在编译器中可用,那么它可能是一个有用的工具。 当然,如果你通过-fcatch-undefined-behavior,clang 会在某些情况下这样做。但这并不意味着它是语言的一部分。 gcc 等价于 -fsanitize=undefined-ggdb 标志也有帮助;它会导致使用额外的调试信息进行编译,这可能是在 fsanitizer 输出中的代码中有/没有指向错误的行号之间的区别。【参考方案7】:

C 不检查数组边界。

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

【讨论】:

以上是关于没有越界错误的主要内容,如果未能解决你的问题,请参考以下文章

阅读 ASP.NET 中的 Outlook 消息捕获越界错误

php问题求助,Notice: Undefined offset: 1 ,我的数组下标没有越界啊?为啥会有这样的提示呢?

linux 段错误常见处理方法

从一道简单模拟题看数组越界以及其他

关于Segmentation fault错误

stm32数组越界一定会进硬件错误中断吗