1.起因

今天写代码的时候遇到一个bug,一个类中的set函数在设置一个POD类型的时候出现了异常,直接段错误退出了。

小tips,POD类型指的是内置类型。比如INT、DOUBLE这类;

想了好久,都没发现这里的问题到底是因为什么。后来才知道,原来空的对象指针,也能被解引用访问到函数!

2.示例

下面是关于这个情况的示例代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
#include <iostream>
using namespace std;

class mytest
{
public:
mytest() = default;
mytest(int a):_a(0){}

void set_int(int a)
{
cout << "set int to " << a << endl;
_a = a;
}

void print(int a)
{
cout << "just a print " << a << endl;
}

private:
int _a = 0;
};


int main()
{
mytest* ptr = nullptr;
ptr->print(20);
ptr->set_int(10);

return 0;
}

运行这个程序,会出现段错误。但你会发现这个段错误并不是因为我们的ptr-> 里面出现的,而是成功进入了set_int函数,执行了打印,最终对成员变量_a赋值的时候出现的!

对象指针为空,代表压根不存在一个实际的对象,也没办法对不存在的成员变量操作。

1
2
3
4
> ./test
just a print 20
set int to 10
[1] 280362 segmentation fault ./test

当代码很多的时候,就会因为忽略这个特性(其实我当时是压根不知道)而误以为错误出现在set_int函数中。

没有想到是外层的对象指针为空导致的。

3.为什么?

在编译过程中,对象就已经绑定了函数地址。一个类的所有对象,使用的函数地址都是一样的。当编译ptr->set_int(10) 的时候,函数的地址就已经和这个指针绑定了,调用它等价于直接调用 set_int 函数。

如果这个函数中没有需要用到成员变量的地方,也就不需要解引用this指针,是不会出错的。比如上方代码中的print函数,就没有出现异常。

但如果函数内访问了内置成员,那么就会出现解引用空指针导致的段错误!