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

时间:2023-12-28 18:43:20

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

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

大致是像下面这段代码

 std::map<unsigned long, int> m;

 template<typename T>
char* jnew(int count)
{
char* p = new char[sizeof(T) * count];
if (count == )
m[(unsigned long)p] = ;
return p;
}

同事把第 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

 #include <map>

 static std::map<int, int> m;

 std::map<int, int>& GetMap()
{
return m;
}

main.cpp

 #include <map>

 std::map<int, int>& GetMap();

 class Fuck{
public:
Fuck(){
GetMap().clear();
}
}; static Fuck fuck; int main(int argc, char** argv)
{
return ;
}

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

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

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

参考链接

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

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