为啥在超出数组末尾写入时不会出现分段错误?

Posted

技术标签:

【中文标题】为啥在超出数组末尾写入时不会出现分段错误?【英文标题】:Why don't I get a segmentation fault when I write beyond the end of an array?为什么在超出数组末尾写入时不会出现分段错误? 【发布时间】:2022-01-23 09:25:45 【问题描述】:

为什么编译时没有报错?

#include <iostream>
using namespace std;

int main()

    int *a = new int[2];
    // int a[2]; // even this is not giving error
    a[0] = 0;
    a[1] = 1;
    a[2] = 2;
    a[3] = 3;
    a[100] = 4;
    int b;

    return 0;

谁能解释为什么会这样。 提前致谢。)

【问题讨论】:

不幸的是,已定义的行为有时是未定义行为的子集:( @LuchianGrigore 有时 @Mahesh no...根据定义...预期行为是未定义行为的子集。但是 UB 和定义的行为是不相交的。 尝试添加内存释放 [operator delete],我想,你会承担你的 segfault。操作系统通常在完成进程后释放内存而没有错误。并且不要忘记删除“a”的第二个堆栈声明。 切换编译器或启用更多警告。 a 的双重定义太可怕了 【参考方案1】:

因为未定义的行为 == 任何事情都可能发生。你很不幸它没有崩溃,这种行为可能会隐藏错误。

至于 a 被定义两次 - 这是编译器中的一个错误。

【讨论】:

对不起。 “a”没有被宣布两次。我只是想说这两个声明的行为相似。 @singingsingh ok :) 然后忽略第二段。【参考方案2】:

声明两个名为a 的变量肯定是错误的;如果你的编译器接受了,那么它就坏了。我假设您的意思是,如果将一个声明替换为另一个声明,您仍然不会收到错误。

数组访问没有经过范围检查。在编译时,数组的大小通常是未知的,即使知道,语言也不需要检查。在运行时,检查会降低性能,这违背了不为不需要的东西付费的 C++ 理念。因此,超出数组末尾的访问会产生未定义的行为,程序员需要确保它不会发生。

有时,无效访问会导致分段错误,但这不能保证。通常,内存保护仅适用于整个内存页面,典型的页面大小为几千字节。有效内存页面内的任何访问都不会被捕获。您访问的内存很有可能包含一些其他程序变量或调用堆栈的一部分,因此在那里写入可能会以您可以想象的任何方式影响程序的行为。

如果您想安全起见,可以使用std::vector,并且只能使用at() 函数访问其元素。这将检查索引,如果超出范围则抛出异常。它还将为您管理内存分配,修复示例中的内存泄漏。

【讨论】:

对不起。 “a”没有被宣布两次。我只是想说这两个声明的行为相似。【参考方案3】:

我猜你来自 Java 或类似 Java 的语言,一旦你走出数组的边界,就会得到“数组索引越界”异常。

嗯,C 对你有更多的期望;它节省了您要求的空间,但它不会检查您是否超出了所节省空间的边界。一旦你按照上面提到的那样做,程序就会出现可怕的未定义行为。

请记住,如果您的程序中存在错误并且您似乎无法找到它,并且当您检查代码/调试它时,一切似乎都正常,您很有可能是“越界”并访问未分配的地方。

【讨论】:

【参考方案4】:

具有良好代码分析的编译器肯定会警告该代码引用超出您的数组分配。忘记多个 a 声明,如果您运行它,它可能会或可能不会出错(正如其他人所说的未定义行为)。例如,如果你有一个 4KB 的堆页面(在处理器地址空间中),如果你不在那个页面之外写,你就不会从处理器那里得到错误。删除数组后,如果您已经删除了,并且根据堆实现,堆可能会检测到它已损坏。

【讨论】:

以上是关于为啥在超出数组末尾写入时不会出现分段错误?的主要内容,如果未能解决你的问题,请参考以下文章

为啥大型静态数组会产生段错误而动态却不会? (C++)

为啥这段代码在 leetcode 运行良好,但在 geeksforgeeks 出现分段错误?

为啥 C++ 标准向量在分配或调整大小时会出现段错误? [关闭]

动态二维数组。为啥是分段错误?

为啥在某些机器上堆栈溢出,但在另一台机器上出现分段错误?

为啥这个程序在调用函数时会出现分段错误?