头文件中的内部链接变量 - 每次包含头时,是否在内存中分配变量?

时间:2018-02-06 07:59:21

标签: c++ compilation allocation linkage translation-unit

假设我有一个头文件Resources.h,我在其中定义了这5个结构:

const IColor COLOR_BLACK(255, 0, 0, 0);
const IColor COLOR_GRAY(255, 127, 127, 127);
const IColor COLOR_WHITE(255, 255, 255, 255);
const IColor COLOR_RED(255, 255, 0, 0);
const IColor COLOR_GREEN(255, 0, 255, 0);

使用conststatic默认为C++internal linkage),他们“驻留”在翻译单元的范围内。

现在,假设我将这些文件包含10次到我的应用程序中(来自10个不同的.cpp)。 当我编译时,会创建一个目标文件,并且(稍后)链接器会将所有这些目标文件一起收集到一个唯一的可运行代码中。

这是否意味着当我run程序时,它将在内存中分配10次以上的每个结构?即10x5结构?

所以即使它们之后被链接在一起,它们对于翻译单元也是分开的?或者链接器是否足够聪明,可以将它们收敛到内存中的唯一分配?

不确定我是否巧妙地完成了这些步骤。我喜欢C ++中的新东西。

3 个答案:

答案 0 :(得分:1)

是。但是(1)少数字节与最小的可执行文件的通常大小无关,而且(2)无论如何它都可能被优化掉。如果你想避免这种情况,请使用constexpr,这使得它们只编译时间值。

在其他新闻中,C ++中的一个常见约定是仅使用宏的所有大写标识符。这是因为所有大写都是眼睛,因此惯例应该保留给那些通常也不好的东西。当你使用全部大写的任何其他东西时,你(1)似乎对许多程序员大喊大叫,(2)冒险无意中文本替换,以及(3)冒错误传达这些名称所代表的内容。

C ++不是Java或Python。

这些语言得到了早期C常量的大写约定,它没有const,因此它们必须将常量表示为预处理器宏。即,Java和Python约定实际上是来自C的大写宏规则。在C ++中使用常量宏是过时的,并且与这些语言不同,C ++确实有预处理器。

另外,请注意:类型的前缀I是指示(抽象)接口的常用约定,但根据您的声明IColor必须是具体的,可实例化的类型。

答案 1 :(得分:0)

请记住:标准仅指定抽象机器的行为。模拟抽象机器不需要实际实现,只需要模拟其可观察行为。

因此抽象机器的 每个内部链接变量的实例将按照翻译单元进行分配。但由于它们是常量,很可能您只使用它们的值而不是它们的地址 - 在标准术语中它们可能不会被使用(一个定义规则)。在这种情况下,它们将由编译器处理,因为它们只是编译时常量,并且根本不会为它们分配任何内存:它们将由编译器替换它们的值。

当const值的大小或构造时间昂贵时,唯一应该考虑手动优化的情况是恕我直言。然后在标题中声明它们是extern,并且只在一个转换单元中定义它们。

如果不是这样,因为它们具有内部联系,这不违反一个定义规则,如果它们是在不同的翻译单元中使用外部链接定义的话。

TL / DR:在所有正常使用案例中,只需使用成语即可。如果你真的非常内存不足,只需控制生成的汇编代码:这些变量可能会被优化掉并替换为它们的值。

答案 2 :(得分:0)

您可能在编译阶段有10个,但最终可能会在链接阶段进行优化。

但是,您可以尝试使用不同的方法来处理这种常量变量定义,以防由于某些内存限制而需要确定。

拥有头文件,只将变量声明放在extern

// my_consts.h
extern const IColor COLOR_BLACK;

然后创建一个显示实际定义的源文件。

// my_consts.cpp
const IColor COLOR_BLACK(255, 0, 0, 0);

这样实际的定义只编译一次,你仍然可以引用它们,因为你已经在头文件中声明了它们。

此外,有了这样的常量,将它们粘贴到某个命名空间以避免全局命名空间污染会很好 - 特别是如果你可以将它们划分为某些逻辑类别。