【问题标题】:What does a C++ program require to run?运行 C++ 程序需要什么?
【发布时间】:2011-04-25 21:33:05
【问题描述】:

这个问题困扰了我一段时间。让我们考虑以下两个程序:

#incude <iostream>
int main()
{
   std::cout << "Hello, World!";
}

int main()
{
   int x = 5;
   int y = x*x;
}
  1. 窗口: 第一个示例自然需要一些用于控制台的系统 .dll。我明白那个。第二个呢?它需要什么来运行吗?一些运行时库?顺便问一下,运行时库实际上是做什么的?
  2. Linux: 不知道,能指教一下吗?

我知道这取决于编译器和操作系统,但我需要一般答案或特定示例。 TIA。

【问题讨论】:

  • 您的第二个示例不需要任何处理,除了来自main()return 0;。它不调用任何类型的函数,也不访问volatile 变量,因此它没有可观察到的行为。
  • @David:是的,但是假设我们设置了无优化
  • @Armen:它仍然不需要处理,因为标准不包括优化(它允许)。编译器所做的是其他事情,但这会因编译器而异。如果您想强制处理,请在其中添加一些volatiles。
  • @David - 由于main() 之前和之后的 CRT 调用,一个空程序仍在处理。看我的回答。
  • @Ashleys:@David 完全正确。第二个程序等价于int main() { return 0; }。该程序不需要任何东西。如果它恰好是托管的,那就是另一个问题了。

标签: c++ operating-system runtime dependencies


【解决方案1】:

作为一般答案,第一个将需要 C++ 运行时库(支持标准库调用所需的东西)。这些在语言支持库之间形成了一个接口,而后者又知道如何在给定的环境中实现它们的功能。

第二个不使用运行时库。它将使用 C 启动和终止代码(初始化和拆除 C 环境),但它是关于这些是否被视为运行时库的一部分的讨论点。如果您将它们视为一部分,那么,是的,它们将被使用。它可能会是一个非常使用的部分,因为启动代码和流内容之间的大小通常存在很大差异。

您可以将代码静态(在链接时绑定)与运行时库或动态链接(以便在加载时完成实际绑定)。 Windows 和 Linux 都是如此。

【讨论】:

  • -1 表示“第二个不使用运行时库”。它通常依赖于运行时库来启动和终止进程。在 Windows 中,运行时库提供程序入口点,在 main 之后它调用 ExitProcess(如果我没记错的话)。
  • @Alf,你没有看到下面的文字:"other than the C startup code ..."?这也可能是一个定义问题。启动和终止代码可能在 CRT 中,但我见过很多情况,它位于独立的 start.o 中,与标准库无关。我会澄清的。
  • init/term 代码是否可以被认为是 RTL 的一部分确实是有争议的。从物理上讲,它是相连的,所以是的。但从逻辑上讲,它没有公开面向应用程序的可调用资源或数据,所以不,不是。鉴于答案清楚地说明了这一点,-1 绝对是不必要的。
  • 是的,但我担心的是终止,通常不能优化它,因为它是关于操作系统要求,而不是 C 要求。它可能还包括处理未捕获的(OS 和/或 C++)异常。既然您已经添加了关于此的措辞,我已经删除了反对票。干杯,
  • 没有问题,@Alf。我很高兴得到批评,毕竟它确实让我的答案变得更好。
【解决方案2】:

对于 Windows 应用程序,您可以使用 Dependency Walker 查看所有依赖项。

【讨论】:

  • dependencywalker.com - 在检查依赖项时可以挽救生命。
  • Linux 等价物将是ldd:一个列出您所依赖的库(以及在哪里找到它们)的命令。还有nm 用于获取引用的符号列表,标有U(未定义)的符号是您将在另一个DLL 中选择的符号。
【解决方案3】:

第一个程序执行流 I/O,这意味着它必须与操作系统管理的资源(控制台、gui)进行交互。因此,最终,操作系统必须通过系统 dll 中实现的 API 来调用。

在 Windows 上,第二个程序不需要库。我很确定在 Linux 上也是如此。

【讨论】:

    【解决方案4】:

    用 GCC 编译它们,并在控制台写入名为“hi”的可执行文件:

    ldd hi
    

    将为您提供连接到您的程序的共享对象(动态库)。

    为了快速回答,这里有一个输出:

    ldd tifftest
      libtiff.so.3 => /usr/lib/libtiff.so.3 (0x4001d000)
      libc.so.6 => /lib/libc.so.6 (0x40060000)
      libjpeg.so.62 => /usr/lib/libjpeg.so.62 (0x40155000)
      libz.so.1 => /usr/lib/libz.so.1 (0x40174000)
      /lib/ld-linux.so.2 => /lib/ld-linux.so.2 (0x40000000)
    

    【讨论】:

    • @hiena:鉴于该程序是“tifftest”,我怀疑它是图像格式的测试程序。
    • 确实很奇怪。在我的机器上,C++ 程序总是喜欢 linux-gate、libstdc++、libm、libgcc_s、libc 和 ld-linux。 C 程序只喜欢 linux-gate、libc 和 ld-linux。除非明确要求,否则不得使用 libtiff、libjpeg 或 libz。
    • 这只是一个例子,总是需要的东西是 libc 和 ld-linux。
    • @Edgar:感谢您的精彩评论,非常感谢。
    【解决方案5】:

    好吧,让我们从更一般的角度来看这个:

    • 首先,您需要一台具有兼容 CPU 的计算机,该计算机可与编译器输出的目标计算机配合使用。您可能认为这很明显,但假设代码编译为 x86 机器代码,它不会在使用不同指令的 Alpha CPU 上运行。或者,如果您编译为 x64 机器代码,它将无法在仅 x86 的 CPU 上运行。因此,运行 C++ 程序需要正确的硬件,而 Java 等基于虚拟机的语言会将其抽象出来。

    • 您还需要正确的操作系统。我不是移植程序方面的专家,但我认为不可能用 C++ 构建在多个操作系统上运行的单个可执行文件。例如,即使将您的第二个示例编译到 Windows,在实际调用您的 main() 函数之前和之后都会有大量运行时库代码。这将执行诸如准备堆和初始化 CRT 库之类的操作。 Windows 的 CRT 是通过 Windows API 实现的。您可以静态链接库,因此不需要 CRT DLL,但程序中的代码仍然调用 Windows API,因此仍然依赖于平台。作为一个实验,我在 Windows 上使用 Visual Studio 编译了一个带有静态链接的空程序,根据 Dependency Walker 的说法,它仍然引用 KERNEL32.DLL 来获取 HeapCreateExitProcess 等函数。所以“空”程序仍然会为你做一大堆操作系统的东西,为做一些有用的事情做准备(不管你的程序是否做任何有用的事情)。

    还请注意,可能存在最低操作系统版本:由于调用了EncodePointerDecodePointer,Visual Studio 2010 甚至需要 Windows XP SP2 或更高版本才能运行一个空程序。见this question

    • 系统必须有内存才能启动您的程序。您可能认为它什么都不做,但如上所示,在调用main() 之前,您的程序库会执行整个操作系统初始化调用负载。这些可能需要一些内存,以及执行它所需的处理时间。

    • 根据操作系统的配置,您可能需要足够的安全权限来启动可执行程序。

    因此,简而言之,即使使用静态链接也能运行空的 C++ 程序,您需要正确的 CPU、操作系统、运行可执行文件的权限以及完成程序所需的内存/处理时间。与 Java 或 .NET 等 VM 技术相比,要求可能会减少到可能只是正确的虚拟机、必要的权限以及运行程序所需的内存/CPU 时间。这可能不像听起来那么简单:您可能需要虚拟机的正确版本,例如 .NET 框架 4.0。这可能会使您的分发过程复杂化,因为更新机器的整个 JVM 或 .NET 框架可能是一个耗时的过程,需要管理员权限并且可能需要 Internet 连接。在某些狭隘的情况下,这可能会破坏交易,因为在极少数情况下,可能更容易说“它将在自 XP 以来的任何 x86 兼容的 Windows 操作系统上运行”,而不是“任何具有最新虚拟昨天才发布的机器”。然而,在大多数情况下,虚拟机允许您(理论上)忘记 CPU 和操作系统这一事实使程序的分发更容易;对于 C++,无论您使用的库有什么额外要求,您都需要至少为您想要支持的每个平台和 CPU 组合编译单独的可执行文件。

    【讨论】:

      【解决方案6】:

      Windows 上的 C 程序需要 Windows 附带的 CRT 库。 C++ 有时需要所谓的“C++ 可再发行”。它们可以通过链接嵌入到应用程序中,但这会使 EXE 更大。

      【讨论】:

      • CRT可以与程序静态链接
      • @Andrey。是不是 Visual Studio 中的选项,它说运行时库,您可以将其设置为 MT MD MTd 等?
      • @Andrey。另外,如果您指定程序在什么情况下需要 C++ redist,我会接受您的回答
      • @Armen Tsirunyan redist 始终是必需的(链接的或独立的),无论您使用什么类。每个 C++ 应用程序都需要它们。
      • @Armen Tsirunyan:至少 C++ 标准库中所有与 C 不同且不是模板的部分,以及一些 C++ 魔法的所有支持代码(例如异常、RTTI、 ...)。
      【解决方案7】:

      对于您问题的第一部分 - 您已经得到了几位成员的回答。 但我所说的是一般情况,这两种情况都需要 - (以防你不知道)

      要运行任何程序,都必须为其提供所需的资源。在回答第一部分时,团队已经列出了几个项目。

      但总的来说,它需要的是定义良好的地址空间(在主内存中)、它的属性和 CPU 时间。操作系统可确保您在执行程序时得到它。除非有一些荒谬的冲突,否则你的程序会得到这个(这就是为什么我猜 Chubsdad 评论“你需要运气”)。

      操作系统调度、CPU 要求从内存中获取指令/数据然后执行它......所有这些都形成了执行您的程序的“机器”。

      找到入口点(或程序中要执行的第一个点)是在编译时(例如 main 函数)或使用诸如 exec() 之类的系统调用(在 Unix 中)加载程序时决定的全部/ CreateProcess()(在 Windows 中)。

      【讨论】:

        【解决方案8】:

        在 Linux 上,任何 C 程序都静态链接到某些 CRT 库。程序的真正入口点是/usr/lib/crt1.o 中定义的_start() 函数。此函数调用一些 libc 函数,例如 __libc_start_main()。因此,您仍然需要 libc 库...

        你可以不用 libc,但这很棘手。您需要将入口点重命名为_start(),或指示链接器从main() 开始。当程序完成时,您还需要一些内联汇编来发出_exit() 系统调用,否则它只会崩溃。当然,使用ld 命令而不是通过gcc 前端显式执行链接。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 2015-02-06
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2011-05-21
          • 2011-08-07
          • 1970-01-01
          相关资源
          最近更新 更多