为什么可执行文件如此之大?(为什么不删除死代码?)

Meh*_*dad 19 c c++ visual-c++

编译和链接此文件会生成1-KiB可执行文件:

#pragma comment(linker, "/Entry:mainCRTStartup") // No CRT code (reduce size)
#pragma comment(linker, "/Subsystem:Console")    // Needed if avoiding CRT

#define STRINGIFIER(x)    func##x
#define STRINGIFY(x)      STRINGIFIER(x)
#define G   int STRINGIFY(__COUNTER__)(void) { return __COUNTER__; }

int mainCRTStartup(void) { return 0; }  // Does nothing

#if 0
    // Every `G' generates a new, unused function
    G G G G G G G G G G G G G G G G G G G G G G G G G G G G G G G G
    G G G G G G G G G G G G G G G G G G G G G G G G G G G G G G G G
#endif
Run Code Online (Sandbox Code Playgroud)

当您更改#if 0#if 1)时,输出大小将增加一倍至2 KiB.

它似乎与迄今为止所有版本的Visual C++都这样做,即使我的命令行选项包含我能想到的所有优化:

/Ox /MD /link /fixed /OPT:ICF /OPT:REF
Run Code Online (Sandbox Code Playgroud)

而且,具体来说,我没有包含任何调试信息.

有谁知道为什么/OPT:REF不导致链接器删除未使用的函数?

Gre*_*ill 20

从广义上讲,编译器在"对象记录"中生成代码,其中包含一堆汇编代码和支持信息.链接器将这些对象记录链接在一起以创建可执行文件.

编译器通常会为整个源文件创建单个对象记录.在这种情况下,链接器只能决定是否链接整个对象记录.由于在对象记录中至少有一个函数,它必须在所有函数中链接.

在某些编译器上,您可以告诉它为每个函数生成单独的对象记录(对象文件可以有多个对象记录).在这种情况下,如果从未调用过,链接器可以决定省略某些对象记录.

/ OPT的Microsoft文档:

/ OPT:REF

LINK默认删除未引用的打包函数.如果对象包含已使用/ Gy选项编译的打包函数(COMDAT),则该对象包含该函数.此优化称为传递COMDAT消除.要覆盖此默认值并在程序中保留未引用的COMDAT,请指定/ OPT:NOREF.您可以使用/ INCLUDE选项覆盖特定符号的删除.

/Gy编译器选项可用功能级链接.

作为参考,gcc中也存在此功能:

-ffunction-sections
-fdata-sections

如果目标支持任意节,则将每个函数或数据项放入输出文件中的自己的部分.函数名称或数据项名称确定输出文件中节的名称.

在链接器可以执行优化以改善指令空间中引用的位置的系统上使用这些选项.大多数使用ELF对象格式的系统和运行Solaris 2的SPARC处理器都具有这种优化的链接器.AIX可能会在将来进行这些优化.

只有在获得重大好处时才使用这些选项.指定这些选项时,汇编器和链接器将创建更大的对象和可执行文件,并且速度也会更慢.如果指定此选项,则无法在所有系统上使用"gprof",如果同时指定此选项和-g,则可能无法进行调试.

和ld中的伴侣选项:

--gc截面

启用未使用输入节的垃圾收集.在不支持此选项的目标上会被忽略.此选项与-r或--emit-relocs不兼容.可以通过在命令行上指定--no-gc-sections来恢复默认行为(不执行此垃圾回收).

  • +1.Re:"我不确定如何在Microsoft工具链中执行此操作":请参阅http://msdn.microsoft.com/en-us/library/xsa71f43.aspx,它提供了四种不同的方法.(OP最简单的方法是将`/ Gy`添加到命令行选项中.) (2认同)