相同的代码片段,Windows和Linux上的不同行为

时间:2012-06-07 17:10:09

标签: c++ windows linux

我知道下面的代码不正确,因为我正在为任意地址赋值。

#include <iostream>

using namespace std;

int main()
{
int *i;
*i = 12;     // Not right.... i is not initialized.
cout << *i << endl;
return 0;
}

这段代码在Linux上产生了分段错误。但是,在Windows上它输出12 ...

为什么它可以在Windows上运行?我不是将12分配给某个任意位置我的程序没有特权吗?

3 个答案:

答案 0 :(得分:12)

它实际上调用了未定义的行为。所以这两种行为都是正确的,即它可能会给出段错误,也可能不会。

在下一次运行时,Windows版本可能会崩溃,Linux版本可能会向您发送威胁邮件!

换句话说,任何事情都可能发生。 C ++规范和(符合标准的)编译器都没有给出任何保证,这就是C ++规范将行为称为未定义行为的原因。

答案 1 :(得分:2)

因为恰好在<{1}}放在堆栈上的发生的随机数据发生以形成Linux下的不可写地址,但发生以包含Windows上的可写地址。

但请注意,您无法确定代码在Windows上有哪些其他影响。 可能可能如果你碰巧在13日星期五下午6:16启动程序,则地址发生成为“输出”系统呼叫号码的地址存储,它可能发生i是“删除文件”的调用,它可能发生这删除了最重要的文件你有。是的,这是极不可能的,但根本不可能(如果使用特定编译器设置使用特定版本的特定编译器编译它,可能只会发生这种情况)。

或者简而言之,没有人能够确定在运行此代码时会发生什么(请注意,即使在Linux下,如果更改编译器选项,编译器版本或恰好使用命令行参数调用它,行为可能会有所不同...)

答案 2 :(得分:1)

@Nawaz和@celtschk是对的。您的代码确实调用了未定义的行为,因此两种行为都是正确的。但是,您有理由看到这种特定的未定义行为。原因值得理解。

当操作系统在运行时加载程序时,会为其打开一个虚拟地址空间。在64位系统上,虚拟空间从地址0延伸到地址0xffffffffffffffff。 / p>

您的系统自然只占填充如此庞大地址空间所需的实际物理内存的一小部分。此外,您的系统必须共享它可能同时运行的多个不同进程之间的内存 - 每个进程都有自己的私有虚拟地址空间。

所以这就是系统的作用。它最初为你的新加载的程序分配一小块内存 - 可能是4 kiB - 并将其映射到地址空间的一小部分,如0xfffffffffffff000到0xffffffffffffffff。您的程序在此空间中构建存储数据对象的堆栈。如果堆栈超过4 kiB,系统将分配另外4 kiB,0xffffffffffffe000到0xffffffffffffefff,并且将以对正在运行的程序透明的方式执行。

只要程序以有序,规定的方式构建和使用其堆栈,它就会运行,好像它不知道对其堆栈大小的任何限制。但是,如果程序以无序方式访问内存,那么结果取决于几个因素,但如果访问系统从未将程序实际内存分配给的地址,则肯定会引发系统异常使用

现在,这个答案遗漏了很多细节。您可能知道第二种内存,动态内存池。还有其他因素,包括“磁盘交换”。但是,如果指针i碰巧指向内存,程序已经用于某种目的或其他目的,则不会引发系统异常。它是否指向这样的记忆?很难说。显然,在你的情况下,它似乎是在一个系统上这样做而不是在另一个系统上。

顺便说一下,我和@celtschk略有不同。现代CPU和操作系统是专门为防止系统灾难而构建的,就像他所说的那样。 C和C ++标准并不保证不会发生此类灾难,但操作系统会这样做,除非程序以超级用户权限运行(即使这样,也可能存在部分有效的安全措施)。我认为您不必担心无意中使用普通的用户模式程序擦除磁盘。不过,在嵌入式系统上,这是另一回事,@celtschk非常正确。