C++获取成员变量的偏移地址

本文通过C++代码示例详细解释了如何计算类成员变量在内存中的偏移地址,并解析了这一过程背后的原理,包括如何利用编译器特性避免非法内存访问。

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

下面我将从代码分析、偏移量计算原理和内存访问安全性三个方面进行说明:

#include<iostream>
#include<list>
#include<string>
#include<vector>
using namespace std;


class base
{
public:
	int m_i1;
	char m_c1;
	char m_c2;
	char m_c3;
};

#define GET(A , m)  (int)(&((A*)0)->m)
int main()
{
	cout << "大小:"<<sizeof(base) << endl;
	cout <<"m_i1:"<< GET(base, m_i1) << endl;
	cout <<"m_c1:"<< GET(base, m_c1) << endl;
	cout <<"m_c2:"<< GET(base, m_c2) << endl;
	cout << "m_c3:" << GET(base, m_c3) << endl;
	base s1;
	return 0;

}

代码功能解析

这段C++代码主要演示了如何计算类成员变量的内存偏移量。通过自定义的GET宏,程序输出了base类中各个成员变量的偏移位置和类的总大小。

偏移量计算原理

  1. (A)0的含义*
    将空指针0强制转换为A*类型,这告诉编译器将内存地址0x00000000视为一个A类型的对象。注意,这里只是进行类型转换,并没有真正访问该地址的内存。

  2. 成员偏移量的计算
    表达式((A*)0)->m通过刚才创建的"虚拟对象"访问其成员m。此时,&((A*)0)->m获取的是成员m在这个"虚拟对象"中的地址。由于该对象起始于地址0,这个地址值恰好等于成员m相对于对象起始位置的偏移量(以字节为单位)。

  3. 宏定义的作用
    GET(A, m)宏通过(int)(&((A*)0)->m)将偏移地址转换为整数类型,从而得到一个可以直接打印的偏移量数值。

关于内存访问安全性

虽然代码中使用了0x00000000这个通常被视为无效的地址,但整个过程中并没有对该地址进行读写操作。编译器在处理&((A*)0)->m时,只是根据类型信息计算成员的偏移量,并不需要真正访问内存。因此,这个操作是安全的,不会导致程序崩溃。

输出结果说明

在32位系统中,程序的输出可能如下:

大小:8
m_i1:0
m_c1:4
m_c2:5
m_c3:6

这表明:

  • base类的总大小为8字节(由于内存对齐)
  • m_i1位于对象起始位置(偏移量0)
  • m_c1m_c2m_c3依次排列在m_i1之后(偏移量4、5、6)
  • 最后2字节为填充字节,用于保证内存对齐

在这里插入图片描述

&((A*)0)->m_i1即是m_i1的成员变量的偏移地址,那为什么这么说呢,来详细分析一下:

1、(A*)0什么意思?标识0x00000000这个地址空间强行转化成(A*)类型,这是告诉编译器,0x00000000这块空间你按照(A*)来解析;

2、那么接下来&((A*)0)->m_i1就代表m_i1从0x00000000这块内存开始的所在的地址空间,因为他的起始地址是0,那么&((A*)0)->m_i1也就代表了m_i1在类对象内的偏移地址,当对象的首地址为0时,得到的成员变量地址就是它的偏移量。

3、那么访问0x000000不会内存崩溃吗;答案是不会,因为在整个过程中,我们只是告诉编译器0x00000000这块内存的空间你按照A*去解析,但是我们并未对这块内存进行任何读写操作,只是进行了获取偏移地址m_i1,所以不会崩溃。

评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值