为啥重新声明 std::cout 会导致分段错误?

Posted

技术标签:

【中文标题】为啥重新声明 std::cout 会导致分段错误?【英文标题】:Why does redeclaring std::cout cause a segmentation fault?为什么重新声明 std::cout 会导致分段错误? 【发布时间】:2019-07-13 15:57:45 【问题描述】:

我正在用这段代码测试 C++。我在 Ubuntu 版本 18.10 上使用 GCC 版本 8.3.0 编译。

#include <ostream>
namespace std
    extern ostream cout;
;
int main()
    std::cout << "Hello world\n";

代码编译没有问题。但是,执行时,程序会打印出来:

分段错误(核心转储)

这是为什么呢?

【问题讨论】:

因为它是未定义的行为。 namespace std extern ostream cout; ; - 不,你不能那样做。 唯一允许程序添加到 std 命名空间的东西是模板特化。 “代码编译没有问题” - 这意味着几乎没有。 C++ 中的很多 不正确代码确实 需要编译器发出诊断。有很多事情是不允许你做的,如果你还是做了,编译器就免除了所有的义务,并且可以生成它想要的 whatever 代码。避免这种情况的唯一方法是了解所有规则并且不编写无效代码。是的,有许多规则和许多陷阱 - C++ 困难 公平地说,这种对标准合同的特殊违反导致这种特殊实际结果的原因可能很有趣。乍一看,人们可能会合理地期望一个简单的重新声明会起作用。来自我:+1 和标题改进。 如果您告诉我们您的编译器(包括版本)和平台是什么,我们可能会解决这个问题。 【参考方案1】:

首先,我们已经介绍了您的程序具有未定义的行为,因为您不允许像这样在命名空间std 中声明事物。只允许使用工具链。

通常当你的程序有 UB 时,比如读取内存的一些随机部分或混淆优化器,所有的赌注都没有了,我们甚至不费心去弄清楚为什么我们看到了一些特殊的结果。但是,在这种情况下,它比这更简单。

例如,在 libstdc++v3(您正在使用的)中,由于版本控制原因,the actual stream declarations 是 in namespace std::__8,其中 __8 是内联命名空间,具体取决于某些配置。您的 extern 声明不是,因此它与真实的原始声明不匹配。这可能导致a linker error,因为新添加的声明实际上并没有与任何存在的对象结合。

在其他配置中,声明中的conflicting visibility settings 会进一步混淆链接器并导致运行时出现分段错误。 真正确定原始声明对您来说是什么样子的唯一方法是在您的计算机上观察程序 #include &lt;iostream&gt; 的预处理源(这并不难;使用 g++ -E!)并查看它是如何结束的适合您的特定配置。

这个故事的寓意是,尽管浏览 cppreference.com 会让你相信 cout 的生命是从一个简单的开始:

namespace std 
   ostream cout(/* some args */);

...现实情况通常更复杂,您的重新声明没有考虑任何复杂情况,其中一些复杂情况可能会以产生您观察到的结果的方式违反规则。

【讨论】:

好的,所以我在细节上对冲我的赌注,但我坚持要点 编译器发出一个重定位条目以引用代码中的数据。由于未定义全局变量,链接器应该抱怨。在这种情况下,链接器不知何故没有抱怨并且重定位没有完成,因此在机器代码中留下了一个未定义的地址(我的猜测是它使用了 PC 相对寻址,它最终试图写入机器代码(只读!),同时尝试向std::cout 对象写入内容)。 对不起..我不是说“全局变量”而是写的。我指的是 std::cout。仔细想想,libstdc++ 库必须同时包含std::cout std::__v8::cout,因为-fabi-version 是编译/链接时选项。因此,我可以想象动态链接器成功地将他的std::cout 声明解析为“旧”-ABI std::cout 定义。但是由于他的程序中的其他结构被编译为使用新的 ABI,这会导致兼容性问题(例如,std::basic_ostream 的实例化使用新的 ABI 版本)。所以它崩溃了。 嗯,看来我被-fabi-version 愚弄了。它指的是语言 ABI,如名称修饰。不适用于 libstdc++ ABI 版本。不确定 libstdc++ 是否真的包含两个 ABI,但听起来很浪费空间。

以上是关于为啥重新声明 std::cout 会导致分段错误?的主要内容,如果未能解决你的问题,请参考以下文章

为啥释放内存会导致分段错误?

为啥 const int main = 195 会导致程序正常工作,但没有 const 会导致分段错误?

为啥我的字符串分配会导致分段错误?

为啥这个字符串反转 C 代码会导致分段错误? [复制]

为啥 MPI_Barrier 在 C++ 中会导致分段错误

debian 和 std::thread c++ 的即时分段错误