C++疑难:调用类构造函数、成员函数致程序启动时访问空指针错误Access Memory at 0x0
这个错误你可能没有遇到过,也可能永远不会遇到,但如果遇到了,需要巨大的代价去找到它,因为错误发生在main()函数之前,常规的调试手段无法找出错误缘由。我只知道出现这个错误的源头在哪,却无法解释为何会出现这种现象,这应该涉及底层对象模型。造成该错误的实际原因几乎都是因为全局变量或静态成员变量初始化报错(这些过程在main()之前)导致的,但你在调试时调试器根本不会追踪到这些初始化失败的变量,而是直接在主线程0x0退出,没有给出任何有效信息。
在Google和StackOverflow上暂未找到类似疑问及答复,因而在此记录以作参考。
具体讲这个错误的表现如下:你写了一个类A,里面可能有静态函数,可能有内联函数,也可能有普通的成员函数,并且你已经检查过了无数次,代码没有任何错误(实际上确实没有错误,因为错误根源不出现在类A中,而是出在全局变量/静态成员变量里,但你的调试器无法追踪到它们),但是运行、调试时却总是在main()之前就程序崩溃,且无法给出除访问无效指针(空指针及其附近的地址)以外的任何有效信息。
下面是一个简单的例子。类B有一个全局变量b,但是这个全局变量b因为某些原因初始化失败(在我的代码中,是由于free()了一个错误的内存地址)。类A中有内联函数,有成员函数,但其中只有memb_func2()对全局变量b进行了访问及操作。
需要注意的是,类A的构造函数为inline,这意味着构造函数不会被储存在类的函数指针表里,而是直接内联到代码中(严格讲是否内联由编译器决定,在这里我们假设这个构造函数足够简单,编译器一定会内联它)

当你在main()函数里写下如下代码

此时程序正常运行,因为构造函数为inline(也可以是default),没有涉及到非内联函数。但如果把构造函数改为非inline

那么同样上面的代码,此时却出现运行崩溃,且崩溃发生main()之前。我们现在知道崩溃是由于全局变量b未正确初始化(抛出了错误)导致的。但是,只有memb_func2()访问了b,而main()中只调用了没有访问b的非内联构造函数,并没有调用访问了b的memb_func2(),却依然导致了崩溃。如果打开调试器,那么你只能得到一个访问了空指针的信息Access Memory at 0x0 或者 0x1、0x2 等从0x0开始算起的地址。
回到内联构造函数的情况,只调用内联函数inline_func()

程序依然是正确运行的,没有错误。但是,一旦你调用memb_func1()或者memb_func2(),那么程序编译虽然能通过,但同样会出现和上面一模一样的错误。


当然,你不一定非得调用,取成员函数的指针一样会报错,例如下面的代码也会出现上面的错误


因此,如果出现了这样的错误,你应该检查一下全局变量、静态变量这些在main()之前执行的操作是否抛出了错误,例如上面中类A的代码没有错,错误在全局变量b上,但是调用A的非内联函数却导致程序崩溃,给了一种错误出现在A上的错觉,让你一遍又一遍地检查A的代码却始终无法解决问题。
对这个错误最形象的说法就是一颗耗子屎打坏一锅汤,就因为memb_func2()访问了抛出错误的全局变量,导致A的所有非内联函数(包括构造函数、析构函数等)全都损坏,连取函数指针都会崩溃。当然实际上memb_func2()是无辜的,真正的耗子屎是全局变量b。