c++ const 符号膨胀链接文件
在 C++ 中,将 const 放在头文件中是合法的,通常 C 方式是将 extern 声明放在头文件中,并将定义放在一个编译单元中,但在 C++ 中,前一种技术会导致二进制文件增加,因为链接时不会删除符号(使用 gnu ld 和 Visual Studio 进行测试)。有没有好的方法来做这些事情?我只能想到定义或C方式,但后者可能会为更少的优化提供空间......
piotr@gominola:0:/tmp$ g++ -c b.cc
piotr@gominola:0:/tmp$ g++ -c a.cc
piotr@gominola:0:/tmp$ nm a.o | c++filt | grep COOK
0000000000000000 r AI_LIKE_COOKIES
piotr@gominola:0:/tmp$ nm b.o | c++filt | grep COOK
0000000000000000 r AI_LIKE_COOKIES
piotr@gominola:0:/tmp$ g++ -o a a.o b.o
piotr@gominola:0:/tmp$ nm a | c++filt | grep COOK
0000000000400610 r AI_LIKE_COOKIES
0000000000400618 r AI_LIKE_COOKIES
piotr@gominola:0:/tmp$ cat a.h
#ifndef a_h
#define a_h
//const double A = 2.0;
//extern const double AI_LIKE_COOKIES;
const double AI_LIKE_COOKIES = 5.0;
#endif
piotr@gominola:0:/tmp$ cat a.cc
#include "a.h"
using namespace std;
extern void f();
//const double AI_LIKE_COOKIES = 2.0;
int main(int argc, char *argv[])
{
f();
}
piotr@gominola:0:/tmp$ cat b.cc
#include "a.h"
void f()
{
}
piotr@gominola:0:/tmp$
In C++ is legal to put a const in the header file, usually the C way would be to put the extern declaration in the header and the definition in just one compilation unit, but in C++, the former technique leads to an increased binary since the symbols are not removed while linking (tested with gnu ld and visual studio). Is there a good way to do these things? I can only think of a define or the C way, but the later might give room to less optimizations...
piotr@gominola:0:/tmp$ g++ -c b.cc
piotr@gominola:0:/tmp$ g++ -c a.cc
piotr@gominola:0:/tmp$ nm a.o | c++filt | grep COOK
0000000000000000 r AI_LIKE_COOKIES
piotr@gominola:0:/tmp$ nm b.o | c++filt | grep COOK
0000000000000000 r AI_LIKE_COOKIES
piotr@gominola:0:/tmp$ g++ -o a a.o b.o
piotr@gominola:0:/tmp$ nm a | c++filt | grep COOK
0000000000400610 r AI_LIKE_COOKIES
0000000000400618 r AI_LIKE_COOKIES
piotr@gominola:0:/tmp$ cat a.h
#ifndef a_h
#define a_h
//const double A = 2.0;
//extern const double AI_LIKE_COOKIES;
const double AI_LIKE_COOKIES = 5.0;
#endif
piotr@gominola:0:/tmp$ cat a.cc
#include "a.h"
using namespace std;
extern void f();
//const double AI_LIKE_COOKIES = 2.0;
int main(int argc, char *argv[])
{
f();
}
piotr@gominola:0:/tmp$ cat b.cc
#include "a.h"
void f()
{
}
piotr@gominola:0:/tmp$
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(4)
声明为 const 且未显式声明为 extern 的对象在 C++ 中具有内部链接。这意味着每个翻译单元都会获得自己的对象副本。
但是,由于它们具有内部链接,因此无法从其他翻译单元命名,因此编译器可以检测对象本身是否未使用 - 对于基本的 const 对象这仅意味着它的地址从未被占用;它的值可以根据需要进行替换 - 并从目标文件中省略它。
即使在
-O1
下,gcc 也会执行此优化。Objects declared
const
and not explicitly declaredextern
have internal linkage in C++. This means that each translation unit gets it's own copy of the object.However, as they have internal linkage and so can't be named from other translation units, the compiler can detect if the object itself is not used - and for basic
const
objects this just means if it's address is never taken; it's value can be substituted as needed - and omit it from the object file.gcc will perform this optimization even at
-O1
.您有两个真正的选择。您可以定义具有外部链接的常量,也可以不定义。通过内部链接,您将仅在每个实际使用常量的翻译单元中获得一个副本,假设优化已打开。
内部链接:
外部链接:
但是, 您可以会问,“内联常量怎么样?”不幸的是,浮点操作数不能真正内联。每当您在函数中使用浮点常量时,该值都会作为常量存储在内存中。考虑这两个函数:
两个文件很可能都会在某处包含常量 5.7,然后从内存中加载该常量。没有真正执行优化*。您将获得两份 5.7 的副本,就像您执行了以下操作一样:
建议:在头文件中使用
extern
,并在一个翻译单元中定义常量。代码可能不会变慢,并且除非链接时优化,这是确保最终产品中只有一个副本的唯一好方法。不过,听起来好像对 8 个字节有很多麻烦...
汇编器:
这是一个函数:
这是 x86_64 上的汇编器:
注意符号
LC0
,它是一个包含值 5.0 的常量。内联除了使符号不可见之外什么也没做,因此它不会显示在nm
中。您仍然会在每个使用常量的翻译单元中获得该常量的副本。There are two real choices you have. You can define a constant with external linkage, or not. With internal linkage, you'll only a copy in each translation unit that actually uses the constant, assuming optimization is turned on.
Internal linkage:
External linkage:
However, you may be asking, "what about inlined constants?" Unfortunately, floating point operands can't really be inlined. Whenever you use a floating point constant in a function, that value gets stored as a constant in memory. Consider the two functions:
In all likelihood, both files will contain a constant 5.7 somewhere, which is then loaded from memory. No optimization is really performed*. You get two copies of 5.7, just as if you had done this:
Recommendation: Use an
extern
in the header file, and define the constant in one translation unit. The code likely won't be any slower and barring link-time optimizations, this is the only good way to make sure only one copy ends up in the final product.It sounds like a lot of fuss over eight bytes, though...
Assembler:
Here's a function:
Here's the assembler, on x86_64:
Notice the symbol
LC0
, which is a constant containing the value 5.0. Inlining has done nothing but make the symbol invisible so it doesn't show up innm
. You still get a copy of the constant sitting around in every translation unit which uses the constant.将
const
放入每个标头中会隐式使其成为内部链接,因此它会在每个翻译单元中重复。我相信“C 方式”是处理这个问题的正常方式。您还可以使用简单的内联函数定义“常量”(请参阅 std::numeric_limits::max())
Putting the
const
in each header implicitly makes it internal linkage so it's duplicated in every translation unit. The "C way" is the normal way of dealing with this I believe.You can also define "constants" with trivial inline functions (see
std::numeric_limits<T>::max()
)这是合乎逻辑的行为。如果您需要使模块依赖于外部名称,请改为包含
extern
。大多数情况下是不需要的。It's logical behaviour. If you need to make your module dependent on external name include
extern
instead. In most cases it's not needed.