【问题标题】:C++ Segmentation Fault when using cout in static variable initialization在静态变量初始化中使用 cout 时出现 C++ 分段错误
【发布时间】:2012-09-01 08:55:42
【问题描述】:

我有一个程序,我使用 cout 来发出调试信息。代码在静态全局变量的初始化中执行,即在程序执行的早期。当我使用自己的构建脚本构建程序时,它在第一次使用 cout 时出现段错误(只有一个字符串文字被转移到 cout,所以它不能是值)。我使用 valgrind 检查早期对无效位置的写入,但没有(也没有可能生成这些写入的代码,我在输出之前没有做太多)。当我将源代码复制到 Eclipse 项目并让 Eclipse 内置构建器构建它时,一切正常。我没有使用任何奇怪的构建器设置,只是用-ggdb -std=c++0x 编译,这是仅有的两个标志。

如果之前没有无效写入,那么带有字符串文字的 cout 段错误的原因是什么?构建配置对此有何影响?

(对不起,我不能给你一个最小的例子,因为这个例子可以简单地在你的机器上编译,就像我在使用 eclipse builder 时所做的那样)

编辑:这是堆栈跟踪:

0x00007ffff7b6d7d1 in std::ostream::sentry::sentry(std::ostream&) () from /usr/lib   /x86_64-linux-gnu/libstdc++.so.6
(gdb) backtrace
#0  0x00007ffff7b6d7d1 in std::ostream::sentry::sentry(std::ostream&) () from /usr/lib/x86_64-linux-gnu/libstdc++.so.6
#1  0x00007ffff7b6dee9 in std::basic_ostream<char, std::char_traits<char> >& std::__ostream_insert<char, std::char_traits<char> >(std::basic_ostream<char, std::char_traits<char> >&, char const*, long) ()
from /usr/lib/x86_64-linux-gnu/libstdc++.so.6
#2  0x00007ffff7b6e2ef in std::basic_ostream<char, std::char_traits<char> >& std::operator<< <std::char_traits<char> >(std::basic_ostream<char, std::char_traits<char> >&, char const*) ()
  from /usr/lib/x86_64-linux-gnu/libstdc++.so.6
#3  0x00000000004021be inTest::fill (this=0x6120f8, funcs=...) at inTest.cpp:92

最后一帧是我的代码。第 92 行简单地写着:

std::cout << "Test";

【问题讨论】:

  • 使用调试器尝试找出它可能是什么。如果不出意外,至少请发布相关的代码行(在您在调试器中确认它们是什么之后)。
  • @JoachimPileborg 编辑了我的帖子,包括 gdb 的回溯
  • 您使用的完整编译和链接命令有哪些?

标签: c++ initialization segmentation-fault cout


【解决方案1】:

正如 Luchian 所指出的,您不能在第一个之前使用 std::cout ios_base::Init 的实例已构建。你不必 然而,定义一个实例;包括&lt;iostream&gt; 就足够了。

初始化顺序在单个翻译单元中定义的。 如果您在所有具有静态的文件的顶部包含 &lt;iostream&gt; 实例,你应该没问题。如果静态对象的构造函数 然而,在另一个翻译单元中调用一个函数,输出是 在该翻译单元中,仅包含 &lt;iostream&gt; 是不够的 仅在执行输出的翻译单元中。你必须包括它 在定义静态变量的翻译单元中。甚至 如果他们不做任何输出。

【讨论】:

  • 那是我的问题!一个静态调用另一个翻译单元的方法,并且只有那个其他翻译单元包含。我将 的包含移到了标头中,以确保每个包含被调用函数的翻译单元也包含 。现在可以了。
  • @gexicide:我可以进一步说,您的问题实际上是静态变量和初始化顺序的依赖关系。你说的是你的问题实际上是症状。
  • @DavidRodríguez-dribeas:我自己的代码没有初始化顺序问题。我只是在初始化中调试了一些令人讨厌的错误,并为此使用了couts(我知道,调试器通常更好,但有时只是转储一些调试文本更好)
  • @DavidRodríguez-dribeas 有(今天)或多或少的标准习语可以用来避免这类问题。但是,std::cout 不使用它们。他的问题涉及到他的对象和std::cout之间的初始化顺序。
  • @DavidRodríguez-dribeas:是的,但问题只存在于调试过程中。我想指出原始代码没有缺陷,我肯定不会仅仅因为调试couts 失败而将代码重写为不使用静态:)。
【解决方案2】:

std::cout 是静态存储中的对象。它保证在输入main 之前被初始化,但不一定在代码中的其他静态变量之前被初始化。好像静态初始化顺序惨败。

经过一番挖掘:

27.4.2.1.6 类 ios_base::Init

Init ();

3) 效果:构造一个 Init 类的对象。如果 init_cnt 是 零,函数将值 1 存储在 init_cnt 中,然后 构造和初始化对象 cin, cout, cerr, clog (27.3.1), wcin、wcout、wcerr 和 wclog (27.3.2)。无论如何,函数然后 将 init_cnt 中存储的值加一。

【讨论】:

  • 但是std::cout不使用漂亮的计数器模式来确保它在第一次使用之前被初始化?
  • @gexicide 我现在正在搜索标准,但还没有找到任何东西。
  • @JanHudec:gexicide其实是对的,它是通过ios_base::Init对象完成的
  • @JanHudec 啊,不过现在的TU不一定要包含才能间接调用cout::operator &lt;&lt;
  • 哦,好的。就是这样!将 的包含从 cpp 文件移动到标题后(以确保包含我的文件的所有翻译单元也包含 )它可以工作。
【解决方案3】:

静态变量初始化是无人区。如果你避免在那里做重要的工作,你就会避免问题。也许您应该将静态变量包装在 Singleton pattern 中,这样您就可以将初始化推迟到第一次使用时。

【讨论】:

    猜你喜欢
    • 2013-12-23
    • 1970-01-01
    • 2011-12-20
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多