c++;膨胀链接文件的常量符号 C++中的P>在头文件中是合法的,通常C方式是将外部声明放入头和定义中,只在一个编译单元中,但是在C++中,前者的技术导致增加的二进制,因为在链接时符号不被移除(用GNU LD和VisualStudio测试)。有没有一个好的方法来做这些事情?我只能想一个定义或C方式,但后者可能会给更少的优化空间
你有两个真正的选择。可以使用外部链接定义常量,也可以不使用外部链接定义常量。使用内部链接时,假设启用了优化,则在每个实际使用常量的翻译单元中只需要一个副本 内部联系:c++;膨胀链接文件的常量符号 C++中的P>在头文件中是合法的,通常C方式是将外部声明放入头和定义中,只在一个编译单元中,但是在C++中,前者的技术导致增加的二进制,因为在链接时符号不被移除(用GNU LD和VisualStudio测试)。有没有一个好的方法来做这些事情?我只能想一个定义或C方式,但后者可能会给更少的优化空间,c++,linker,constants,C++,Linker,Constants,你有两个真正的选择。可以使用外部链接定义常量,也可以不使用外部链接定义常量。使用内部链接时,假设启用了优化,则在每个实际使用常量的翻译单元中只需要一个副本 内部联系: // a.h const double AI_LIKE_COOKIES = 5.0; // a.h extern const double AI_LIKE_COOKIES; // a.c const double AI_LIKE_COOKIES = 5.0; 外部链接: // a.h const double AI_LIKE
// a.h
const double AI_LIKE_COOKIES = 5.0;
// a.h
extern const double AI_LIKE_COOKIES;
// a.c
const double AI_LIKE_COOKIES = 5.0;
外部链接:
// a.h
const double AI_LIKE_COOKIES = 5.0;
// a.h
extern const double AI_LIKE_COOKIES;
// a.c
const double AI_LIKE_COOKIES = 5.0;
然而,您可能会问,“内联常量呢?”不幸的是,浮点操作数不能真正内联。无论何时在函数中使用浮点常量,该值都会作为常量存储在内存中。考虑这两个函数:
// In func1.c
double func1(double x) { return x + 5.7; }
// In func2.c
double func2(double x) { return x * 5.7; }
很可能,这两个文件都会在某个地方包含常量5.7,然后从内存中加载。没有真正执行优化*。您将获得两份5.7的副本,就好像您已经这样做了一样:
extern const double CONSTANT_1, CONSTANT_2;
const double CONSTANT_1 = 5.7;
const double CONSTANT_2 = 5.7;
double func1(double x) { return x + CONSTANT_1; }
double func2(double x) { return x * CONSTANT_2; }
*注意:在某些系统上,如果知道常量将链接到同一个二进制图像中,而不是从库中加载,则代码会变小
建议:在头文件中使用extern
,并在一个翻译单元中定义常量。代码可能不会再慢,除非进行链接时间优化,否则这是确保最终产品中只有一个副本的唯一好方法
这听起来像是对八个字节的大惊小怪,不过
汇编程序:
这里有一个函数:
double func(double x)
{
return x + 5.0;
}
这是x86_64上的汇编程序:
_Z4funcd:
.LFB0:
.cfi_startproc
.cfi_personality 0x3,__gxx_personality_v0
addsd .LC0(%rip), %xmm0
ret
.cfi_endproc
.LFE0:
.size _Z4funcd, .-_Z4funcd
.section .rodata.cst8,"aM",@progbits,8
.align 8
.LC0:
.long 0
.long 1075052544
请注意符号
LC0
,它是一个包含值5.0的常量。内联只是使符号不可见,所以它不会显示在nm
中。在每个使用常量的翻译单元中仍然会有一个常量的副本。将常量隐式放置在每个标题中会使其成为内部链接,因此它会在每个翻译单元中复制。我相信“C方式”是处理这个问题的正常方式
您还可以使用普通的内联函数定义“常量”(请参见std::numeric\u limits::max()
)这是逻辑行为。如果需要使模块依赖于外部名称,请改为包含extern
。在大多数情况下,不需要。 对象声明为代码> const ,而未显式声明 ExtNe>代码>在C++中有内部链接。这意味着每个翻译单元都会得到它自己的对象副本
但是,由于它们具有内部链接,因此无法从其他翻译单元中命名,编译器可以检测对象本身是否未被使用——对于基本const
对象,这仅意味着它的地址从未被使用;可以根据需要替换它的值,并从对象文件中省略它
即使在-O1
,gcc也将执行此优化
$ g++ -O1 -c a.cc
$ g++ -O1 -c b.cc
$ g++ -o a a.o b.o
$ nm a.o | c++filt | grep COOK
$ nm b.o | c++filt | grep COOK
$ nm a | c++filt | grep COOK
$
它仍然具有增加二进制大小的内部链接,但更糟糕的是,它现在是可修改的。static是const的默认值variables@AlanStokes哇,每天学习C和C++之间的不兼容的新东西。如果你使用最小的优化(<代码> -O1 < /代码>),const对象将在编译阶段消失。由于它们具有内部链接,编译器可以选择丢弃它们,因为它们不需要。@piotr:是的,大多数人也是这样。由于各种原因,“调试”配置对象文件将变得更大,未使用常量的额外副本不太可能是最大的因素。在调试配置中,您通常不需要关心二进制文件的大小。我不能将两者都标记为已回答,但我对您的答案投了更高的票。谢谢