【问题标题】:c++: "double free or corruption" for global extern variable?c ++:全局外部变量的“双重释放或损坏”?
【发布时间】:2013-06-27 13:49:24
【问题描述】:

我有兴趣在整个程序中一次性使用一个全局变量。所以我认为实现这一点的最佳方法是在头文件中定义它,如下所示:

extern const std::string CONST_STR = "global string";

但这导致了“双重释放或损坏”运行时错误。 删除extern 使问题消失。

  1. 谁能解释这种行为?
  2. AFAIK,如果没有extern 定义,每个翻译单元都会有一个 CONST_STR,难道没有办法获得一个完全 const 的全局变量吗?

【问题讨论】:

  • 这是否有效?你应该使用外部或初始化器
  • @BalogPal 与const 有效。 extern 只是意味着“不是静态的”。

标签: c++ global-variables constants extern


【解决方案1】:

解决第一部分和关于失去 extern 的其他问题。

const std::string CONST_STR = "global string";

按照 C++ 规则,这等同于说:

static const std::string CONST_STR = "global string";

如果这在包含文件中,您将在每个翻译单元 (TU) 中创建不同的字符串。它们自己都可以正常工作,但假设您还在同一个标​​题中添加了一个函数:

inline void foo() { std::cout << CONST_STR; }

如果&lt;&lt; 运算符通过const&amp; 获取字符串,则在每个 TU 中它将绑定到单独的字符串。因此违反了“一个定义规则”并使您陷入未定义的行为(UB)。在实践中它很可能有效,但它仍然是 UB。

原始的extern 形式与此类似,因为相同的字符串文字在不同的 TU 中也是分开的。

如果你只是说extern 没有初始化,它是一个声明,将由链接器解析为单个定义。如果您使用初始值设定项,则使其成为定义。所以再次在每个 TU 中创建对象,但使用公共公共名称,期望其他 TU 访问它。由于您必须确保实际上只提供了一个定义,因此免除了实现的责任。

不幸的是,单一定义规则太容易被打破,而且它的大多数形式都明确允许实现不发出任何诊断。实际上,链接器只是从池中选择一个随机定义。双重释放可能是由为构造函数和析构函数调用发出_atstart_atexit 条目引起的,对象本身融合为一个,然后获得与TU 一样多的构造函数和析构函数调用。

对于实现来说,一切都是公平的游戏,至于 UB 什么都可以。

【讨论】:

  • 抱歉我的无知,我没有得到inline 示例问题。据我了解,就像std::cout &lt;&lt; CONST_STR; 出现在每个TU 中,这有什么问题?为什么和在几个 TU 中显式写 std::cout &lt;&lt; CONST_STR; 不一样?
  • 如果你只是在单独的函数中编写它,它们都会选择绑定到一个可能不同的 CONST_STR,但由于内容相同,行为是相同的。而且solo功能只有一个TU。但是几个 TU 中包含的内联函数在很多层面上必须是相同的:相同的标记序列和相同的标记含义。后半部分被颠覆了。
  • 你能回答我对芭丝谢芭的问题吗?
  • 是的,如果某些东西是静态的,它只能在那个 TU 中看到,其他 TU 无论如何都找不到该名称(尽管对象本身可以通过地址或引用传递)跨度>
  • 那么芭丝谢芭的建议是如何起作用的呢?所有包含extern const std::string CONST_STR; 的UT 如何访问CONST_STRstatic
【解决方案2】:

放置定义

const std::string CONST_STR = "global string";

恰好在一个编译单元中(常规做法是将其放入源文件中)。

在标题中,只写声明

extern const std::string CONST_STR;

这将确保您在整个程序中只有一个版本的字符串。考虑到你的方式,我很惊讶你的链接器没有抱怨。

【讨论】:

  • 我说的不是一个版本,而是一个单一的存在。其实我主要是好奇第一个问题。
  • 您使用的是哪个编译器和平台?
  • @Subway:IMO 您的代码要么一开始就格式错误,要么如果您将该代码包含在多个文件中,则会破坏 ODR,然后任何事情都可能发生。
  • 我在 Rad Hat 64 上使用 g++
  • @BalogPal 如果多次包含它会破坏 ODR,但它本身的格式是正确的。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2023-03-04
  • 1970-01-01
相关资源
最近更新 更多