为什么C ++的初始分配比C大得多?

Rer*_*umu 135 c c++ benchmarking

当使用相同的代码时,只需更改编译器(从C编译器到C ++编译器)将更改分配的内存量。我不太确定为什么会这样,并且想进一步了解。到目前为止,我得到的最好的答复是“可能是I / O流”,它的描述性不是很强,这使我对C ++的“不用付钱,不用付钱”感到好奇。

我正在使用分别为7.0.1-8和8.3.0-6的Clang和GCC编译器。我的系统在最新的Debian 10(Buster)上运行。基准通过Valgrind Massif完成。

#include <stdio.h>

int main() {
    printf("Hello, world!\n");
    return 0;
}
Run Code Online (Sandbox Code Playgroud)

所使用的代码不会更改,但是无论我是以C还是C ++进行编译,它都会更改Valgrind基准测试的结果。但是,这些值在编译器之间保持一致。该程序的运行时分配(峰值)如下:

  • GCC(C):1,032字节(1 KB)
  • G ++(C ++):73,744字节(〜74 KB)
  • lang(C):1,032字节(1 KB)
  • Clang ++(C ++):73,744字节(〜74 KB)

为了进行编译,我使用以下命令:

clang -O3 -o c-clang ./main.c
gcc -O3 -o c-gcc ./main.c
Run Code Online (Sandbox Code Playgroud)
clang++ -O3 -o cpp-clang ./main.cpp
g++ -O3 -o cpp-gcc ./main.cpp
Run Code Online (Sandbox Code Playgroud)

对于Valgrind,我运行valgrind --tool=massif --massif-out-file=m_compiler_lang ./compiler-lang每种编译器和语言,然后ms_print显示峰。

我在这里做错什么了吗?

Nik*_* C. 148

堆用法来自C ++标准库。它在启动时分配内存供内部库使用。如果您不反对它,则C和C ++版本之间的差应该为零。使用GCC和Clang,可以使用以下命令编译文件:

g ++ -Wl,-根据需要main.cpp

这将指示链接器不要链接未使用的库。在您的示例代码中,未使用C ++库,因此它不应链接到C ++标准库。

您也可以使用C文件进行测试。如果您使用:

gcc main.c -lstdc ++

即使您已经构建了C程序,也会重新显示堆使用情况。

堆的使用显然取决于您正在使用的特定C ++库实现。在您的情况下,这就是GNU C ++库libstdc ++。其他实现可能不分配相同数量的内存,或者根本不分配任何内存(至少在启动时不分配)。例如,LLVM C ++库(libc ++)在启动时不进行堆分配,至少在Linux上不这样机:

clang ++ -stdlib = libc ++ main.cpp

堆的使用与完全不针对它进行链接相同。

(如果编译失败,则可能未安装libc ++。程序包名称通常包含“ libc ++”或“ libcxx”。)

  • 看到这个答案后,我的第一个念头是:“ _如果此标志有助于减少不必要的开销,为什么默认情况下不启用它?_”。有一个好的答案吗? (50认同)
  • @Nat-按需-可以具有不需要的副作用,它可以通过检查您是否使用了库的任何符号并将那些未通过测试的符号踢出而起作用。但是:库也可以隐式执行各种操作,例如,如果库中有静态C ++实例,则将自动调用其构造函数。在极少数情况下,没有显式调用的库是必要的,但它们存在。 (36认同)
  • @Nat`-Wl,-aseded`标记删除您在`-l`标志中指定但实际上没有使用的库。因此,如果您不使用库,则不要链接它。您不需要此标志。但是,如果您的构建系统添加了太多的库,并且清理它们并仅链接所需的库将需要大量的工作,那么您可以改用此标志。但是标准库是一个例外,因为它是自动链接的。所以这是一个极端的情况。 (24认同)
  • @Nat我的猜测是在开发时编译速度较慢。当您准备创建发行版本时,可以将其打开。同样在普通/大型代码库中,差异可能很小(如果您使用大量的STD库等) (4认同)
  • @NikosC。构建系统不自动知道您的应用程序使用哪些符号,以及哪些库实现它们(编译器,arch,发行版和c / c ++库之间的差异)。对于基本运行时库,正确地做到这一点相当麻烦。但是在极少数情况下,您需要一个库,您应该只使用--no-need-the-a-need-learned所需的库,而在其他任何地方都按需使用-le。我看到的一个用例是用于跟踪/调试(lttng)的库和执行某种身份验证/连接的库。 (3认同)
  • @NorbertLange是的,在某些极端情况下,“-按需”可能会出错。不过,这似乎非常罕见。在AFAICT中,大多数Linux发行版都使用启用了此标志的软件包来构建其软件包,以处理那些仅与所有内容挂钩的软件包,因此最终使软件包依赖关系膨胀。当然,最好的解决方案当然是保持构建系统的良好状态,以便您始终知道实际需要什么库。 (2认同)

Ste*_*ebb 15

GCC和Clang都不是编译器-它们实际上是工具链驱动程序。这意味着它们将调用编译器,汇编器和链接器。

如果使用C或C ++编译器编译代码,则将产生相同的程序集。汇编器将产生相同的对象。区别在于,工具链驱动程序将为两种不同的语言向链接器提供不同的输入:不同的启动程序(C ++需要代码来执行在名称空间级别具有静态或线程本地存储持续时间的对象的构造函数和析构函数,并且需要用于堆栈的基础结构例如在异常处理期间支持展开的框架,C ++标准库(在命名空间级别还具有静态存储持续时间的对象)以及可能的其他运行时库(例如,具有堆栈展开基础结构的libgcc)。

简而言之,不是编译器引起了占用空间的增加,而是您通过选择C ++语言选择使用的东西的链接。

C ++确实具有“只为所用内容付费”的理念,但是通过使用这种语言,您可以为此付费。您可以禁用部分语言(RTTI,异常处理),但是随后您将不再使用C ++。如另一个答案中所述,如果您根本不使用标准库,则可以指示驱动程序将其忽略(--Wl,-按需),但是如果您不打算使用任何功能,关于C ++或其库,为什么还要选择C ++作为编程语言?