14

我有一个程序,我使用 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";
4

3 回答 3

15

std::cout正如 Luchian 所指出的,在构造的第一个实例之前不能使用ios_base::Init。但是,您不必定义实例;包括<iostream>应该足够了。

初始化顺序在单个翻译单元中定义。如果您包含<iostream>在所有具有静态实例的文件的顶部,您应该没问题。但是,如果静态对象的构造函数调用另一个翻译单元中的函数,并且输出在该翻译单元中,则仅包含在执行输出的翻译单元中是不够<iostream> 的。您必须将其包含在定义静态变量的翻译单元中。即使他们不做任何输出。

于 2012-09-07T13:32:35.370 回答
14

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 中的值加一。

于 2012-09-07T13:07:46.343 回答
0

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

于 2012-09-07T13:37:33.650 回答