指向类的空指针异常调用,偶尔能正常运行,系统并未抛出调用栈异常
class A
{
public:
void func1()
{
printf("call the func1 by 0x%x\n",this);
};
int number;
};
int main()
{
A *pNULL = NULL;
A TestClass;
pNULL->func1();
TestClass.func1();
printf("pNULL address 0x%x\n",pNULL);
return 0;
}
上面这段代码运行的时候,会输出
call the func1 by 0x0
call the func1 by 0x5f8dcc2f
pNULL address 0x0
引起上述问题的,其实是类的this指针问题
- pNULL作为一个指针,并未指向任何类对象的时候,其持有的this指针为空,但是func1函数在代码段,编译器却认为pNULL->func1是在作func1函数调用,此时等同于外部调用一个类的static 类型的函数
- 由于this指针为空,所以如果pNULL不指向任何对象的情况下,如果调用p->number,试图打印一个数据,就会出现调用栈crash,因为number存储在数据段,pNULL必须指向一个实例化的对象才可以找到number的地址,其实是this->number。
call the func1 by 0x0
call the func1 by 0x17e5a75c
pNULL address 0x0
段错误 (核心已转储)
下面解析编译器生成的a.out文件
60: 00000000000006aa 143 FUNC GLOBAL DEFAULT 14 main
61: 000000000000073a 39 FUNC WEAK DEFAULT 14 _ZN1A5func1Ev//类的func1函数
62: 0000000000000762 21 FUNC WEAK DEFAULT 14 _ZN1AC1Ev
63: 0000000000201010 0 OBJECT GLOBAL HIDDEN 23 __TMC_END__
64: 0000000000000000 0 NOTYPE WEAK DEFAULT UND _ITM_registerTMCloneTable
65: 0000000000000000 0 FUNC WEAK DEFAULT UND __cxa_finalize@@GLIBC_2.2
66: 0000000000000548 0 FUNC GLOBAL DEFAULT 11 _init
a.out的符号表印证了我们的结论,finc1在程序加载阶段分配了地址空间(相对于主进程偏移0x000000000000073a),所以调用pNULL->func1并未出错。
需要注意两点
- 如果func1函数中调用了number这个成员,仍然会发生段错误
- 以这种方式调用类内部的函数,并非推荐的方式,所以我们采取预防措施
void func1()
{
if(NULL == this)
{
printf("Error,Call by NULL point\n");
}
else
{
printf("call the func1 by 0x%x\n",this);
}
};
运行修改后的程序会有如下提示:
Error,Call by NULL point