【问题标题】:Why is a C++ Hello World binary larger than the equivalent C binary?为什么 C++ Hello World 二进制文件比等效的 C 二进制文件大?
【发布时间】:2014-09-02 14:36:07
【问题描述】:

Bjarne Stroustrup 在他的常见问题解答中说,当使用 gcc -O2 编译时,使用 C 和 C++ 的 hello world 的文件大小是相同的。

参考:http://www.stroustrup.com/bs_faq.html#Hello-world

我决定试试这个,这里是 C 版本:

#include <stdio.h>

int main(int argc, char* argv[])
{
    printf("Hello world!\n");
    return 0;
}

这里是 C++ 版本

#include <iostream>

int main(int argc, char* argv[])
{
    std::cout << "Hello world!\n"; 
    return 0;
}

我这里编译,大小不一样:

r00t@wutdo:~/hello$ ls
hello.c  hello.cpp
r00t@wutdo:~/hello$ gcc -O2 hello.c -o c.out
r00t@wutdo:~/hello$ g++ -O2 hello.cpp -o cpp.out
r00t@wutdo:~/hello$ ls -l
total 32
-rwxr-xr-x 1 r00t r00t 8559 Sep  1 18:00 c.out
-rwxr-xr-x 1 r00t r00t 8938 Sep  1 18:01 cpp.out
-rw-r--r-- 1 r00t r00t   95 Sep  1 17:59 hello.c
-rw-r--r-- 1 r00t r00t  117 Sep  1 17:59 hello.cpp
r00t@wutdo:~/hello$ size c.out cpp.out
   text    data     bss     dec     hex filename
   1191     560       8    1759     6df c.out
   1865     608     280    2753     ac1 cpp.out

我用\n 替换了std::endl,它使二进制文件更小。我认为这么简单的东西会被内联,但我很失望它不是。

哇,优化后的程序集有数百行程序集输出?我可以使用 sys_write 用 5 个汇编指令编写 hello world,所有额外的东西是怎么回事?为什么 C 会在堆栈上放一些额外的东西来设置?我的意思是,就像 50 字节的汇编与 8kb 的 C 一样,为什么?

【问题讨论】:

  • 不,他没有这么说。他说 “2004 年,我在 Unix 上使用 gcc -O2 进行了测试,两个版本(iostreams 和 stdio)产生了相同的大小。”
  • 您的问题具体是什么?此外,我认为声称 Bjarne 对此撒谎有点过分。有可能在旧系统上使用旧编译器,二进制文件确实具有相同的大小(即使在新系统上这不是真的。)
  • 这根本不是“不清楚[他]在问什么”,你们这些缺乏想象力的巨魔。这是stackoverflow.com/q/10981140/560648的重复!
  • @LightnessRacesinOrbit 不,不是,文件大小相差 2 个数量级,并且在 Windows 上。 400 KB 值得担心,500 字节是学术性的。
  • 尽管 OP 犯了一些错误,但这是一个具有一个或多个合法技术答案的合法问题。可以衡量和解释程序之间的大小差异。

标签: c++ c size


【解决方案1】:

您正在查看容易被误解的混合信息。 8559 和 8938 字节的文件大小在很大程度上没有意义,因为它们大多是带有符号名称和其他杂项信息的标头,至少用于最小的调试目的。 有点有意义的数字是您稍后添加的size(1) 输出:

r00t@wutdo:~/hello$ size c.out cpp.out
   text    data     bss     dec     hex filename
   1191     560       8    1759     6df c.out
   1865     608     280    2753     ac1 cpp.out

您可以通过使用-A 选项来获得更详细的细分size,但简而言之,这里的差异相当微不足道。

更有趣的是,Bjarne Stroustrup 从未提及他是在谈论静态链接还是动态链接。在您的情况下,这两个程序都是动态链接的,因此大小差异与 stdio 或 iostream 的实际大小成本无关;您只是在测量调用代码的成本,或者(更可能的是,基于其他 cmets/answer)C++ 异常处理支持的基本开销。现在,有一个普遍的说法是,基于静态链接的 C++ iostream 的 hello world 甚至可以比基于 printf 的 hello world 更小,因为编译器可以准确地看到使用了 operator&lt;&lt; 的哪些重载版本并优化了不需要的代码(例如昂贵的浮点打印),而printf 对格式字符串的使用使得这在一般情况下很困难,在一般情况下是不可能的。但是,我从未见过 C++ 实现,其中静态链接的基于 iostream 的 hello 程序可以接近与 C 中基于 printf 的程序一样小,甚至更小。

【讨论】:

    【解决方案2】:

    我认为他将半 KB 视为舍入错误。两者都是“9 KB”,这就是您在典型文件浏览器中看到的内容。它们并不完全相同,因为在底层,C 和 C++ 库是完全不同的。如果您已经熟悉您的反汇编程序,您可以自己查看差异的详细信息。

    “额外的东西”是为了从标准库 shlib 中导入符号,并处理 C++ 异常。奇怪的是,大部分 GCC 编译的 C 可执行文件都被 C++ 异常处理表占用了。我还没有弄清楚如何使用 GCC 剥离它们。

    endl 内联的,但它包含打印\n 字符和刷新流的调用,这些调用不是内联的。大小的差异是由于从标准库中导入的。

    事实上,在任何具有动态加载库的系统上,单个千字节很少重要。嵌入式系统等自包含代码需要包含其使用的标准库功能,而 C++ 标准库往往比其 C 对应物更重——尤其是 &lt;iostream&gt;&lt;stdio.h&gt;

    【讨论】:

    • “我还没弄清楚如何使用 GCC 来剥离它们” -fno-exceptions 通常会阻止使用异常。 stdlibc++ 中还有一个弱函数需要重写,它提供终止(terminate_handler 左右)代码以进行异常处理。
    • @πάνταῥεῖ 当然这是我尝试的第一件事 :v) 。让我知道你是否找到了有用的东西。问题不是产生异常,而是为调用使用异常的函数提供支持。
    • 我必须检查我的同事的工作,他通过覆盖提到的弱函数来省略标准的未捕获异常处理代码。我只是暂时不记得确切的名字了。
    • @πάνταῥεῖ 垃圾根本不是可执行代码,它是告诉展开库如何解释堆栈的表格。
    • 如果 表格 没有被任何代码引用,它们就不会从链接器中拉出,对吗?
    猜你喜欢
    • 2021-06-26
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-09-25
    • 1970-01-01
    • 2012-01-11
    • 2015-04-02
    相关资源
    最近更新 更多