什么会导致 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
的定义在哪里?
在<stddef.h>
和其他几个标题中。在实践中,它可能间接包含在大多数 C++ 头文件中,但最好确定,无论如何都要包含 <stddef.h>
或 <cstddef>
。
谢谢。我认为我的编译器默认包含它,我不知道其他人没有。【参考方案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++ 中的分段错误? [关闭]的主要内容,如果未能解决你的问题,请参考以下文章