空对象指针为什么有时候能调用成员函数

文章探讨了在x8632位系统中,空对象指针如何能够调用成员函数,解释了NULL指针与空对象指针的区别,以及为什么空指针调用非静态成员函数会失败。重点在于内存布局,如栈、堆、代码段的存储位置,以及虚函数调用时的机制。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

在谈这个话题之前呢,还是得了解一下内存布局,以x86的32位系统为例:

在这里插入图片描述

然后得明确一点,NULL指针是无法访问的,如果强行访问,则会引发异常

在这里插入图片描述

然而空对象指针有时候却能够调用成员函数

class C
{
public:
	int a;
	static int b;
	static void f1()
	{
		cout << "fl" << endl;
	}
	void f2()
	{
		cout << "f2" << endl;
	}
	void f3()
	{
		cout << "f3:" << a << endl;
	}
	void f4()
	{
		cout << "f4:" << b << endl;
	}
	virtual void f5()
	{
		cout << "virtual f5" << endl;
	}
};

int C::b = 10;

int main()
{
	C* p = NULL;
	p->f1();   //正确
	p->f2();   //正确
	p->f3();   //错误
	p->f4();   //成功
	p->f5();   //错误
	return 0;
}

假设现在new了一个对象,并把它赋值给指针p,此时指针p则保存在栈上,对象中的a保存在堆上,b保存在.data段中(数据段),f1、f2、f3、f4和f5都保存在代码段,也就是说非静态成员变量在运行时储存在对象内部,成员函数并不储存在对象内部,而是位于程序的二进制代码里,所以与对象的地址无关。

为了方便理解,可以大致的认为编译会把以上代码转换为以下代码

class C
{
public:
	int a;
	virtual void f5()
	{
		cout << "virtual f5" << endl;
	}
};

static int b = 10;

static void f1(C* p)
{
	cout << "fl" << endl;
}

void f2(C* p)
{
	cout << "f2" << endl;
}

void f3(C* p)
{
	cout << "f3:" << p->a << endl;
}
void f4(C* p)
{
	cout << "f4:" << b << endl;
}

f1和f2调用成功,就是因为函数与对象的地址无关,即使是空地址,也不会去访问。

f4调用成功,因为除了函数与对象的地址无关之外,静态变量的地址也与对象无关。

f3调用失败,因为非静态成员变量在运行时储存在对象内部,在访问时,需要先对p解引用,然后才能访问找到堆中的数据,但是p是空指针,所以报错。

f5调用失败,因为f5是虚函数,在调用虚函数时,会通过虚函数表去查找函数的地址,而虚函数表(存放在.rodata中)可以看成是对象内的隐藏变量,因此也需要对指针p解引用,但是p是空指针,所以报错。

总的来说,空指针能够调用成员函数,其本质就是没有对这个空指针进行解引用,从而引发访问异常,在Linux下,又称为段错误(segmentation fault)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值