我注意到C ++中的分段错误的常见原因列表没有问题,所以我想我会添加它。
当然它是社区Wiki,因为没有一个正确答案。
我认为这对于学习C ++的新程序员可能有用,如果你不同意,可以随意关闭它。
答案 0 :(得分:37)
仅当您的操作系统具有MMU(Memory Management Unit)时,才会因访问内存不良而导致分段错误。否则,你不会得到它,只会有奇怪的行为。
虚拟内存(您可以访问的整个内存= 2^(sizeof(pointer_type)*8)
(即:2^num_bits_in_pointer_type
))以命名页面或段为单位映射到物理内存(分页取代分段但仍然使用它们) 。
每个页面都有一些保护权限,如果您尝试从没有读取权限的页面读取,您将获得段错误。如果您尝试写入只读位置,您将获得SIGSEGV。
如果你有一个单位指针并使用它,它可能会发生它将指向另一个好的位置,所以你不会得到段错误。如果在绑定之后有一个小数组读取可能会破坏其他内存区域,如果它没有超过页面边界。
此外,由于页面很多,并非所有页面都被映射。如果您触摸非映射页面,则会出现段错误。实际上,对非映射页面的任何访问都必须考虑写入时的复制,交换页面,延迟加载,内存映射文件和其他内容。请参阅this article on page fault handling,特别是那里的第二张图,也在下面发布(但请阅读文章以获得更多解释)
您主要对用户空间和导致SIGSEGV的所有路径中发生的事情感兴趣。但内核空间也很有趣。
答案 1 :(得分:5)
取消引用NULL指针。
#include <cstddef> //For NULL.
int* p1 = NULL; //p1 points to no memory address
*p1 = 3; //Segfault.
答案 2 :(得分:4)
访问数组越界(可能):
int ia[10];
ia[10] = 4; // Someone forgot that arrays are 0-indexed! Possible Segfault.
答案 3 :(得分:4)
“段落错误”C ++的许多方法不一定保证发生,事实上,这里发布的大多数示例就是这种情况。如果你能在没有发生段错误的情况下执行这些操作,那只是好运(或运气不好,取决于你如何看待它!)。
这实际上是C ++中将其与其他语言分开的事情之一;未定义的行为。而在Java或C#中,您可能会遇到'InvalidOperationException'或类似情况,这可以保证在执行这些操作时发生;在C ++中,标准只是说“未定义的行为”,这基本上是抽奖的运气,而你永远不希望这种情况发生。
答案 4 :(得分:3)
我的最爱:
#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()
有效。
答案 5 :(得分:1)
显而易见的答案是“未定义的行为”,但这是乞求
一个没有经验的程序员和一些类型的程序员的问题
未定义的行为不太可能导致分段错误
(或其他类型的崩溃)比其他人。最常见的原因
分段错误通常与指针相关:解除引用
未初始化的指针,空指针或以前释放的指针;
超越终点(或在开头前,但更少
频繁的)对象(数组或其他);使用非法的结果
当对象没有时,指针强制转换(static_cast
到派生类型
实际上有这种类型,或大多数reinterpret_cast
);等
然而,在这里要记住的最重要的一点是
通常,这些不能保证导致分段错误,并且
通常,它们引起的分段错误只会发生
一段时间后,在一个完全不相关的操作中。因此,写作
超出本地数组的末尾通常会“工作”,
但会修改堆栈上数组后面发生的事情:一些
其他局部变量(修改堆栈上对象的vptr
尝试调用虚拟时可能会导致分段错误
对象上的函数),调用函数的帧指针
(之后可能会导致该功能出现分段错误
你已经退货了)或者寄回地址(这可能会引起各种各样的问题)
奇怪的行为 - 分段错误或非法指令
陷阱可能是最好的事情)。写作超越了结束
释放内存,或通过已释放的指针,可以破坏自由
太空竞技场,导致分段错误(有时很多,
以后分配或免费;它还可以完全修改其他一些
不相关的对象,破坏其中的vptr
或其他指针
对象,或只是一些随机数据 - 再次,分段错误
可能是最好的结果(很可能继续下去
损坏的数据)。
答案 6 :(得分:0)
忘记初始化指针,留下随机内存地址。注意:这可能不会总是段错误,但可能会。
int* p1; //No initialization.
*p1 = 3; //Possible segfault.
答案 7 :(得分:0)
取消引用已释放的内存可能会导致段错误。
SomeClass* someObject = new SomeClass();
delete someObject;
someObject->someMethod(); //Could cause a segfault.
答案 8 :(得分:0)
尝试修改字符串文字:
char* mystr = "test";
mystr[2] = 'w';
CAN 导致分段错误。