C++11 如果Mac OS和Windows都使用x86指令集,为什么我们必须为每个平台重新编译?

C++11 如果Mac OS和Windows都使用x86指令集,为什么我们必须为每个平台重新编译?,c++11,C++11,如果在英特尔处理器上运行的Mac OS和Windows都使用x86指令集,为什么只使用C++11编写的程序(没有特定于操作系统的库、框架或API)不能在这两个平台上运行,而不必为该平台重新编译 最终程序被编译成机器代码,所以如果指令集是相同的,有什么区别?到底发生了什么事 编辑:我真的只是在谈论一个用gcc之类的东西编译的简单Hello world程序。不是应用程序 编辑:例如: #include<iostream> using namespace std;

如果在英特尔处理器上运行的Mac OS和Windows都使用x86指令集,为什么只使用C++11编写的程序(没有特定于操作系统的库、框架或API)不能在这两个平台上运行,而不必为该平台重新编译

最终程序被编译成机器代码,所以如果指令集是相同的,有什么区别?到底发生了什么事

编辑:我真的只是在谈论一个用gcc之类的东西编译的简单Hello world程序。不是应用程序

编辑:例如:

    #include<iostream>
    using namespace std;

    int main()
    {
        cout << "Hello World!";
        return 0;
    }

因为现在的程序不仅仅由二进制代码组成。他们的文件格式不兼容PE/COFF、ELF和Mach-O。想想看,这有点傻,是的,但这是事实。如果你能重新开始历史,就不必这样

编辑:
您可能还想看到我的较长答案和其他答案。

因为现在的程序不仅仅由一团二进制代码组成。他们的文件格式不兼容PE/COFF、ELF和Mach-O。想想看,这有点傻,是的,但这是事实。如果你能重新开始历史,就不必这样

编辑:
您可能还想看到我的较长答案和其他答案。

即使是“你好,世界需要生成输出”。这将是操作系统调用,BIOS调用的级别稍低,或者,由于性能原因,在DOS时代很常见,通过I/O调用或内存映射视频直接输出到视频。这些方法中的任何一种都将高度特定于操作系统和其他设计问题。在您列出的示例中,iostream隐藏了这些详细信息,对于每个目标系统都会有所不同。

即使是Hello,world也需要生成输出。这将是操作系统调用,BIOS调用的级别稍低,或者,由于性能原因,在DOS时代很常见,通过I/O调用或内存映射视频直接输出到视频。这些方法中的任何一种都将高度特定于操作系统和其他设计问题。在您列出的示例中,iostream隐藏了这些细节,并且对于每个目标系统都会有所不同。

一个原因是@Mehrdad在他们的回答中提供的:即使汇编代码在所有平台上都是相同的,但封装到可执行文件中的方式也可能不同。在那一天,MS-DOS中出现了。您可以将该文件加载到内存中,然后从头开始执行它

最终,我们得到了只读内存页、不可执行的读写内存页(出于安全原因不可执行)、嵌入式资源(如Windows上的图标)以及操作系统在运行代码之前应该了解的其他内容,以便为新创建的进程正确配置隔离环境。当然,还有一些共享库必须由操作系统加载,任何做任何有意义的事情的程序都必须通过操作系统调用输出一些结果,例如,它必须知道如何执行系统调用

因此,在多进程现代操作系统中,除了代码外,可执行文件还应该包含大量元信息。这就是为什么我们有文件格式。它们在不同平台上的差异主要是由于历史原因。想象一下PNG和JPEG——两者都是经过压缩的光栅化图像格式,但它们不兼容,使用不同的压缩算法和不同的存储格式

没有特定于操作系统的库、框架或API

那不是真的。由于我们生活在多进程操作系统中,任何进程都无法直接访问硬件——无论是网卡还是显示器。一般来说,它只能以非常有限的方式访问CPU和内存

例如,当您在终端中运行程序时,其输出应到达终端仿真器,以便可以在窗口中显示,您可以在屏幕上拖动该窗口,对您的Hello World透明。所以,无论如何,操作系统都会参与进来

即使您的hello world应用程序也必须:

加载动态C++运行时,在初始化之前初始化CIN对象。当主进程结束时,还有谁将初始化cin对象并调用析构函数? 当您尝试打印某件东西时,您的C++运行时最终将不得不对OS进行调用。现在,它通常在C标准库LIBC中被抽象,我们必须在C++运行时之前动态加载。 该C标准库调用一些x86指令,这些指令进行系统调用,在屏幕上打印字符串。请注意,即使在x86系列中,不同的操作系统和不同的CPU在系统调用方面也有不同的机制和约定。有些使用中断,有些使用英特尔和AMD专门设计的sysenter/syscall指令,有些在已知内存位置传递参数,有些通过寄存器传递参数。同样,这也是为什么该代码被操作系统的标准库抽象出来的原因——它通常提供一些简单的C语言 使必要的装配级别变魔术的面。 总之,回答您的问题:因为您的程序必须与操作系统交互,而不同的操作系统使用完全不同的机制


如果您的程序没有像第二个示例那样的副作用,那么它仍然以常规格式保存。而且,由于平台之间的通用格式不同,我们应该重新编译。为没有副作用的简单程序发明一种通用的兼容格式是不值得的,因为它们是无用的。

一个原因是@Mehrdad在他们的回答中提供的:即使汇编代码在所有平台上都是相同的,但封装到可执行文件中的方式可能不同。在那一天,MS-DOS中出现了。您可以将该文件加载到内存中,然后从头开始执行它

最终,我们得到了只读内存页、不可执行的读写内存页(出于安全原因不可执行)、嵌入式资源(如Windows上的图标)以及操作系统在运行代码之前应该了解的其他内容,以便为新创建的进程正确配置隔离环境。当然,还有一些共享库必须由操作系统加载,任何做任何有意义的事情的程序都必须通过操作系统调用输出一些结果,例如,它必须知道如何执行系统调用

因此,在多进程现代操作系统中,除了代码外,可执行文件还应该包含大量元信息。这就是为什么我们有文件格式。它们在不同平台上的差异主要是由于历史原因。想象一下PNG和JPEG——两者都是经过压缩的光栅化图像格式,但它们不兼容,使用不同的压缩算法和不同的存储格式

没有特定于操作系统的库、框架或API

那不是真的。由于我们生活在多进程操作系统中,任何进程都无法直接访问硬件——无论是网卡还是显示器。一般来说,它只能以非常有限的方式访问CPU和内存

例如,当您在终端中运行程序时,其输出应到达终端仿真器,以便可以在窗口中显示,您可以在屏幕上拖动该窗口,对您的Hello World透明。所以,无论如何,操作系统都会参与进来

即使您的hello world应用程序也必须:

加载动态C++运行时,在初始化之前初始化CIN对象。当主进程结束时,还有谁将初始化cin对象并调用析构函数? 当您尝试打印某件东西时,您的C++运行时最终将不得不对OS进行调用。现在,它通常在C标准库LIBC中被抽象,我们必须在C++运行时之前动态加载。 该C标准库调用一些x86指令,这些指令进行系统调用,在屏幕上打印字符串。请注意,即使在x86系列中,不同的操作系统和不同的CPU在系统调用方面也有不同的机制和约定。有些使用中断,有些使用英特尔和AMD专门设计的sysenter/syscall指令,有些在已知内存位置传递参数,有些通过寄存器传递参数。同样,这也是为什么该代码被操作系统的标准库抽象掉的原因——它通常提供一些简单的C接口,使必要的汇编级变得神奇。 总之,回答您的问题:因为您的程序必须与操作系统交互,而不同的操作系统使用完全不同的机制


如果您的程序没有像第二个示例那样的副作用,那么它仍然以常规格式保存。而且,由于平台之间的通用格式不同,我们应该重新编译。为没有副作用的简单程序发明一种通用的兼容格式是不值得的,因为它们是无用的。

你能解释一下吗?我只是在说一个简单的hello world程序,用gcc编译…@John:是的,我想说的是,即使你的程序像int main{}一样简单,它也会产生一个至少有一个千字节左右的文件,因为这个文件不仅仅是运行的代码。最起码,在代码本身之前,文件还包含头信息,解释它与什么操作系统兼容、代码实际开始的位置、文件在内存中应该映射到什么地址、校验和、最初需要的堆栈和堆的大小、调试信息的位置,位置信息所在的位置等。。。这些都是空间的。在这方面的一个补充说明是C++是建立在一组C库上的,这些库通常包含一些特定于OS的垃圾。你能解释一下吗?我只是在说一个简单的hello world程序,用gcc编译…@John:是的,我想说的是,即使你的程序像int main{}一样简单,它也会产生一个至少有一个千字节左右的文件,因为这个文件不仅仅是运行的代码。至少在代码本身之前,该文件还包含头信息expla
它与什么操作系统兼容,代码实际开始的位置,文件在内存中应该映射到什么地址,校验和,最初需要的堆栈和堆的大小,调试信息所在的位置,位置信息所在的位置,等等。。。这些都是空间的。在这方面的一个补充是C++是建立在一组C语言库上的,这些库通常包含一些特定于OS的垃圾桶。为什么要关闭?这是一个合理的问题。如果指令集相同,则应运行相同的指令集!为什么接近?这是一个合理的问题。如果指令集相同,则应运行相同的指令集!我觉得这就是我的问题所在——如果iostream在每个操作系统中都不同,那么重新编译是有意义的,因为它实际上是两个不同的程序。但是如果连一个头文件都没有,那么程序是相同的……你正在改变这里的情况。第一个例子是一个真实的Hello world,其标题将使事情变得不同。第二个示例没有可见的输出或任何其他有用的副作用,因此,虽然它可能假设两个不同的操作系统碰巧处理相同的可执行文件格式运行,但它将是无用的-没有可见的,没有文件更改,等等。我觉得这就是我的问题所在-如果每个操作系统中的iostream不同,然后重新编译是有意义的,因为它实际上是两个不同的程序。但是如果连一个头文件都没有,那么程序是相同的……你正在改变这里的情况。第一个例子是一个真实的Hello world,其标题将使事情变得不同。第二个示例没有可见的输出或任何其他有用的副作用,因此,虽然它可能假设两个不同的操作系统碰巧处理相同的可执行文件格式运行,但它将是无用的-没有可见的,没有文件更改,等等@John如果操作系统、处理器和所需的共享库是相同的,那么程序将只运行。我是不是无意中说了别的?实际上PNG和JPEG是一个糟糕的比较,因为PNG是无损的,而JPEG不是,所以显示时的结果输出在某种程度上几乎总是从同一源图像开始不同。PNG和GIF是一个更好的类比——相同的无损输出,但不同的格式、压缩等@manassehkatz我考虑过这一点,但GIF支持动画,所以它也不理想。SVN和EMF听起来太花哨了。@John如果操作系统、处理器和所需的共享库是相同的,那么程序就可以运行了。我是不是无意中说了别的?实际上PNG和JPEG是一个糟糕的比较,因为PNG是无损的,而JPEG不是,所以显示时的结果输出在某种程度上几乎总是从同一源图像开始不同。PNG和GIF是一个更好的类比——相同的无损输出,但不同的格式、压缩等@manassehkatz我考虑过这一点,但GIF支持动画,所以它也不理想。SVN和EMF听起来太花哨了。
int main(){
    int j = 2;
    j = j + 3;
}