一个可遇不可求的 bug 全局变量初始化顺序问题 哈哈

这是今天下午帮同事查的一个客户端 C++ 的 bug,前人留下的谜之代码。。

具体情况是,客户端实现了有一个简单的内存池,每次申请内存的时候会把新申请到的内存信息存到一个 map 里,据说是为了检查内存泄漏。

大致是像下面这段代码

 1 std::map<unsigned long, int> m;
 2 
 3 template<typename T>
 4 char* jnew(int count)
 5 {
 6     char* p = new char[sizeof(T) * count];
 7     if (count == 1)
 8         m[(unsigned long)p] = 1;
 9     return p;
10 }

同事把第 7 行注释掉了,结果客户端就开始崩溃。神奇哟~

报错信息中有 0x00000005 字样,显然是空指针的问题,断点第 8 行,查看 count,是个很大的数,进入到 map 的代码里,看到果然里面的指针全他妈是空的,似乎没有被初始化。

然后我取消注释,断点第 7 行,和第 1 行。

程序启动,进入到第 7 行的断点,count 很大,continue,又在第 7 行停下来,count 还是很大。

于是取消第 7 行断点,继续断第 8 行,continue,

啪!停在了第 1 行!基本证实了是初始化的问题。就是最初 count != 1 时访问到 m,m 还没有被初始化,然后崩溃。

那么可以推断,程序第一次调用 jnew 的时候,是在初始化另外一个全局变量或者静态变量。

然后查看调用堆栈,果然是在一个类的静态成员两变量初始化中。

 

同事感叹,印象中只是在某本书上见过类似的问题,没想到今天自己遇上了。

 

重现一下车祸现场:

简单说就是,如果有两个全局变量 a 和 b,a 和 b 分别在两个文件中,a 的初始化又依赖 b,那么就有可能 a 初始化的时候 b 还没又被初始化,导致程序崩溃

nofuck.cpp

1 #include <map>
2 
3 static std::map<int, int> m;
4 
5 std::map<int, int>& GetMap()
6 {
7 return m;  
8 }

main.cpp

 1 #include <map>
 2 
 3 std::map<int, int>& GetMap();
 4 
 5 class Fuck{
 6 public:
 7     Fuck(){
 8         GetMap().clear();
 9     }
10 };
11 
12 static Fuck fuck;
13 
14 int main(int argc, char** argv)
15 {
16     return 0;
17 }

 

解决方法是,把 static std::map<int, int> m; 放到 GetMap 函数内部。

这一类 bug 的特征是,在进入 main 函数之前程序就挂掉。

最后,还是那个原则,全局的东西能不用的尽量不用。

 

参考链接

http://stackoverflow.com/questions/1005685/c-static-initialization-order

http://coolshell.cn/articles/10115.html

 

posted on 2017-03-17 18:05  明天有风吹  阅读(646)  评论(0编辑  收藏  举报

导航

+V atob('d2h5X251bGw=')

请备注:from博客园