为什么不会发生分割故障?
我希望在执行下面的代码后会发生SEG错误,但事实并非如此。有人可以告诉我为什么吗?
int main(){
float *arr;
cout << arr[0] << "\n" --> This prints out a ZERO. I am expecting a seg-fault.
cout << arr[1000] << "\n" --> This gives me a seg-fault
return 0;
}
我想知道这是否是由于减轻撞车事故的编译器的“智能”设计。但是我不确定。
I am expecting a seg-fault to occurr after the execution of the code below, but it doesn't. Could someone tell me why?
int main(){
float *arr;
cout << arr[0] << "\n" --> This prints out a ZERO. I am expecting a seg-fault.
cout << arr[1000] << "\n" --> This gives me a seg-fault
return 0;
}
I am wondering if this due to the "smart" design of the compiler that alleviates the crash. But I can't be sure.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
由于指针 arr 未初始化,因此它的值可能是内存地址先前使用时的值。
在您的情况下,以前使用该内存地址的代码可能使用该内存地址来存储指针,即存储指向有效对象的另一个内存地址。即使该对象的生命周期同时已过期,操作系统可能无法检测到这一点,因为内存页面可能不是返回到运行状态 系统。因此,就操作系统而言,该内存页仍然可以被程序读取(并且可能也可写入)。这可能解释了为什么取消引用 arr 的未初始化值不会产生分段错误。
表达式 arr[1000] 将尝试取消引用与 arr 的未初始化值相距 4000 字节的地址(假设 sizeof(float)==4< /代码>)。内存页的典型大小为 4096 字节。因此,假设 arr 的未初始化值是指向 4096 字节内存页开头附近的内存地址,那么向该地址添加 4000 不会充分改变内存地址以使得该地址指向不同的内存页。但是,如果
arr
的未初始化值是指向内存页中间某处的内存地址,那么向该地址添加4000将使其指向不同的内存页(假设内存页大小为 4096 字节)。这可能解释了为什么操作系统以不同的方式处理这两个地址,以便一个内存访问会导致分段错误,而另一个内存访问不会失败。然而,这都是猜测(我经常使用“可能”这个词就可以清楚地表明这一点)。您的代码不会导致分段错误可能还有另一个原因。无论如何,当您的程序调用未定义的行为(它通过取消引用未初始化的指针来实现)时,您不能依赖任何特定的行为。在某些平台上,它可能会导致分段错误,而在其他平台上,程序可能会正常运行。即使更改编译器设置(例如优化级别)也可能足以改变程序的行为。
在这种情况下,“聪明”的做法是报告某种错误(即崩溃),而不是尝试缓解崩溃。这是因为崩溃使错误更容易被发现。
您的程序没有立即崩溃的原因是您的编译器和操作系统都没有检测到错误。
如果您希望更可靠地检测此类错误,那么您可能需要考虑使用某些编译器提供的功能来尝试检测此类错误。例如,gcc 和 clang 都支持 AddressSanitizer。在这两个编译器上,您所要做的就是使用 -fsanitize=address 命令行选项进行编译。但是,这将导致编译器添加额外的检查,从而显着降低性能(大约两倍)并增加内存使用量。因此,这只能用于调试目的。
Since the pointer
arr
is not initialized, it probably has the value of whatever value that memory address had when it was previously used.In your case, the code that used that memory address previously probably used that memory address for storing a pointer, i.e. for storing another memory address that points to a valid object. Even if the lifetime of that object has expired in the mean time, the operating system will probably not be able to detect this, because the memory page was probably not returned to the operating system. Therefore, as far as the operating system is concerned, that memory page is still readable (and possibly also writable) by the program. This probably explains why dereferencing the uninitialized value of
arr
does not produce a segmentation fault.The expression
arr[1000]
will attempt to dereference an address that is 4000 bytes apart from the uninitialized value ofarr
(assumingsizeof(float)==4
). A typical size of a memory page is 4096 bytes. Therefore, assuming that the uninitialized value ofarr
is a memory address that points near the start of a memory page of 4096 bytes, then adding 4000 to that address will not change the memory address sufficiently to make the address point to a different memory page. However, if the uninitialized value ofarr
is a memory address that points somewhere in the middle of a memory page, then adding 4000 to that address will make it point to a different memory page (assuming a memory page size of 4096 bytes). This probably explains why your operating system treats both addresses differently, so that one memory access causes a segmentation fault and the other memory access does not fail.However, this is all speculation (which is made clear by my frequent use of the word "probably"). There could be another reason why your code does not cause a segmentation fault. In any case, when your program invokes undefined behavior (which it does by dereferencing an uninitialized pointer), you cannot rely on any specific behavior. On some platforms, it may cause a segmentation fault, while on other platforms, the program may work perfectly. Even changing the compiler settings (such as the optimization level) may be enough to change the behavior of the program.
The "smart" thing to do in such a case would be to report some kind of error (i.e. to crash), and not to attempt to alleviate the crash. This is because crashing makes the bug easier to find.
The reason why your program is not crashing immediately is that neither your compiler nor your operating system are detecting the error.
If you want such errors to be detected more reliably, then you may want to consider using a feature offered by some compilers that tries to detect such bugs. For example, both gcc and clang support AddressSanitizer. On those two compilers, all you have to do is compile with the
-fsanitize=address
command-line option. However, this will cause the compiler to add additional checks, which will significantly decrease performance (by a factor of about two) and increase memory usage. Therefore, this should only be done for debugging purposes.您的程序具有未定义的行为,因为指针
arr
是不统治的,并且您正在提取它隐式。因此,您看到的(也许看到)的输出是不确定行为的结果。正如我所说,不依赖具有UB程序的程序的输出。该程序可能只是崩溃。
例如,在这里该程序不会崩溃,但是在这里它崩溃了。
因此,使程序正确的第一步是删除UB。 然后,只有这样,您才能开始对程序的输出进行推理。
1 有关未定义行为的更精确的定义,请参见 提到的地方:对程序的行为没有任何限制。
Your program has undefined behavior since the pointer
arr
is uninitialized and you're dereferencing it implicitly.So the output that you're seeing(maybe seeing) is a result of undefined behavior. And as i said don't rely on the output of a program that has UB. The program may just crash.
For example, here the program doesn't crash but here it crashes.
So the first step to make the program correct would be to remove UB. Then and only then you can start reasoning about the output of the program.
1For a more technically accurate definition of undefined behavior see this where it is mentioned that: there are no restrictions on the behavior of the program.