什么会导致 C++ 中的分段错误? [关闭]

Posted

技术标签:

【中文标题】什么会导致 C++ 中的分段错误? [关闭]【英文标题】:What can cause segmentation faults in C++? [closed] 【发布时间】:2011-10-18 21:50:35 【问题描述】:

我注意到 C++ 中的分段错误的常见原因列表没有问题,所以我想我会添加它。

自然是社区 Wiki,因为没有一个正确答案。

我认为这可能对学习 C++ 的新程序员有用,如果您不同意,请随时关闭它。

【问题讨论】:

嗯...看来我无法将其移至社区 wiki。希望有更多权限的人出现。 怎么样:“什么可以导致 C++ 中的分段错误?”。没有 MMU 的嵌入式系统根本不会给您任何段错误——即使“写入”到 ROM 时也是如此。 你说得对,我改一下。 我想我应该补充一点,你可以从缺少的返回中得到一个段错误(当一个函数应该返回一个值,但由于某种原因你忘了写返回)。花了大约 1:30 调试... 【参考方案1】:

忘记初始化指针,给它们留下随机内存地址。注意:这可能不是总是段错误,但它可能。

int* p1; //No initialization.
*p1 = 3; //Possible segfault.

【讨论】:

不保证会出现段错误。也许会,也许不会。 再次正确。我正在寻找可能导致段错误的事情的例子,所以我不够具体。已编辑。【参考方案2】:

越界访问数组(可能):

int ia[10];
ia[10] = 4; // Someone forgot that arrays are 0-indexed! Possible Segfault.

【讨论】:

不保证会出现段错误。 @sharptooth - 嗯,可能 段错误。已编辑。 是Undefined Behavior。【参考方案3】:

取消引用 NULL 指针。

#include <cstddef> //For NULL.
int* p1 = NULL; //p1 points to no memory address
*p1 = 3; //Segfault.

【讨论】:

你对NULL的定义在哪里? &lt;stddef.h&gt; 和其他几个标题中。在实践中,它可能间接包含在大多数 C++ 头文件中,但最好确定,无论如何都要包含 &lt;stddef.h&gt;&lt;cstddef&gt; 谢谢。我认为我的编译器默认包含它,我不知道其他人没有。【参考方案4】:

取消引用释放的内存可能会导致段错误。

SomeClass* someObject = new SomeClass();
delete someObject;
someObject->someMethod();  //Could cause a segfault.

【讨论】:

【参考方案5】:

许多“段错误”C++ 的方法不一定保证会发生,事实上,这里发布的大多数示例都是这种情况。如果您可以在不发生段错误的情况下执行这些操作,那只是运气好(或运气不好,取决于您如何看待它!)。

这实际上是 C++ 与其他语言的区别之一;未定义的行为。而在 Java 或 C# 中,您可能会收到“InvalidOperationException”或类似的情况,这保证在执行这些操作时会发生;在 C++ 中,标准只是说“未定义的行为”,这基本上是运气,你永远不希望发生这种情况。

【讨论】:

C++ 和未定义的行为非常正确。【参考方案6】:

仅当您的操作系统具有 MMU (Memory Management Unit) 时,对内存的错误访问才会导致分段错误。否则,你不会得到它,只会出现奇怪的行为。

虚拟内存(您可以访问的整个内存 = 2^(sizeof(pointer_type)*8)(即:2^num_bits_in_pointer_type))以称为页或段的单位映射到物理内存(分页取代了分段,但它们仍在使用)。

每个页面都有一些保护权限,如果您尝试从具有非读取访问权限的页面读取,则会出现段错误。如果您尝试写入只读位置,您将获得 SIGSEGV。

如果你有一个未初始化的指针并使用它,它可能会指向另一个好的位置,这样你就不会遇到段错误。如果您在绑定后读取一个小数组,如果它没有超过页面边界,则可能会损坏其他内存区域。

此外,由于页面很多,并非所有页面都真正映射。如果你触摸一个非映射页面,你会得到一个段错误。实际上,对非映射页面的任何访问都必须考虑写入时复制、交换页面、延迟加载、内存映射文件和其他事情。请参阅this article on page fault handling,尤其是那里的第二张图,也发布在下面(但请阅读文章以获得更多解释)

(来源:champ at vistech.net)

您主要对用户空间中发生的事情以及通向 SIGSEGV 的所有路径感兴趣。但内核空间也很有趣。

【讨论】:

您提供的链接很有用,但合法吗?我看不懂我认为是中国人的东西,所以你能澄清一下法律地位吗?如果一切正常,请随时删除此评论。 更新了与同一主题的另一个资源的链接。【参考方案7】:

显而易见的答案是“未定义的行为”,但这要求 一个没有经验的程序员的问题,以及某些类型的 未定义的行为不太可能导致分段错误 (或其他类型的崩溃)比其他人。最常见的原因 分段错误通常与指针相关:取消引用 未初始化的指针、空指针或先前释放的指针; 访问结束(或在开始之前,但那更少 频繁)对象(数组或其他);使用非法的结果 指针转换(static_cast 到派生类型,当对象没有 实际上有那种类型,或者大多数reinterpret_cast);等等

然而,在这里要记住的最重要的一点可能是 一般来说,这些不能保证会导致分段错误,并且 通常,它们导致的分段错误只会发生 一段时间后,在一个完全不相关的操作中。因此,写 超出本地数组的末尾通常会“工作”, 但会修改堆栈上数组后面发生的任何事情:一些 其他局部变量(修改栈上对象的vptr 当您尝试调用虚拟时可能会导致分段错误 对象上的函数),调用函数的帧指针 (这可能会导致该函数出现分段错误,之后 你已经退回),或退货地址(这可能会导致各种 奇怪的行为——分段错误或非法指令 陷阱可能是可能发生的最好的情况)。写超出结尾 释放的内存,或者通过已经释放的指针,可以破坏空闲的 空间领域,在很多(有时很多, 很多)以后分配或免费;它还可以完全修改其他一些 不相关的对象,破坏其vptr 或其他一些指针 对象,或者只是一些随机数据——同样,分段错误是 可能是最好的结果(远比继续 损坏的数据)。

【讨论】:

确实如此。您提出了一个很好的观点,即段错误比损坏的数据更可取。【参考方案8】:

尝试修改字符串文字:

char* mystr = "test";
mystr[2] = 'w';

这个CAN会导致分段错误。

【讨论】:

【参考方案9】:

我的最爱:

#include <iostream>
struct A 
    virtual void f() 
        std::cout << "A::f();\n";
    
    int i;
;

struct B : A 
    virtual void f() 
        std::cout << "B::f();\n";
    
    int j;
;

void seti(A* arr, size_t size) 
    for (size_t i = 0; i < size; ++i)
        arr[i].i = 0;


int main() 
    B b[10];
    seti(b, 10);
    b[3].f();

与大多数可能导致段错误的事情一样,这也可能失败。例如,在 ideone 上,b[3].f() 失败,但 b[2].f() 有效。

【讨论】:

等一下。为什么会出现这个段错误? @fluffels - 简单的回答:它是 UB,所以它可能。从技术上讲,这是因为void seti(A*, size_t) 覆盖了某些元素b 的vtable,这会导致对虚函数的任何调用导致段错误。或者不会导致段错误,如果它们被奇迹般地覆盖以指向一个函数。 UB 就是 UB。 天哪。这是否适用于类? @fluffels - 不。见***.com/questions/92859/…

以上是关于什么会导致 C++ 中的分段错误? [关闭]的主要内容,如果未能解决你的问题,请参考以下文章

在 C++ 中使用向量时出现分段错误

为啥重新声明 std::cout 会导致分段错误?

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

为啥这个非常简单的构造函数会导致段错误?

为啥 MPI_Barrier 在 C++ 中会导致分段错误

gcc,c++:静态字符串成员变量导致堆损坏/分段错误