You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

带继承与指针的C++类内存布局疑问

带继承与指针的C++类内存布局疑问

嗨,我来帮你拆解这个内存布局的问题~首先先把你的代码和输出整理清楚:

你的代码

class Msg {
public:
    int info1;
    int info2;
    char* msg;
};

class SpeMsg : public Msg {
public:
    struct speInfos {
        int speInfo1;
        int speInfo2;
    };
    speInfos spe;
};

int main()
{
    SpeMsg* sm = new SpeMsg();
    sm->info1 = 5;
    sm->info2 = 10;
    sm->spe.speInfo1 = 15;
    sm->spe.speInfo2 = 20;
    sm->msg = new char[sizeof("25")];
    memcpy(sm->msg, "25", sizeof("25"));
    uint8_t * payload = (uint8_t*)sm;
    SpeMsg::speInfos* sI = &sm->spe;
    cout << "Info 1 : " << &sm->info1 << endl;
    cout << "Info 2 : " <<&sm->info2 << endl;
    cout << "msg : " <<&sm->msg << endl;
    cout << "pointing to : " <<(void *)sm->msg << endl;
    cout << "SpeInfo1 : " <<&sm->spe.speInfo1 << endl;
    cout << "SpeInfo2 : " <<&sm->spe.speInfo2 << endl;
    delete sm->msg;
    delete sm;
    return 0;
}

输出结果

Info 1 : 0x1515eb0
Info 2 : 0x1515eb4
msg : 0x1515eb8
pointing to : 0x1515ed0
SpeInfo1 : 0x1515ec0
SpeInfo2 : 0x1515ec4

首先要先澄清一个你可能混淆的点:你提到的“msg address is 0x1515ed0”其实是**msg指针指向的内存地址**,而msg这个成员变量本身在SpeMsg对象里的地址是0x1515eb8——这是两个完全不同的东西,前者是动态分配的字符串内存,后者是对象里存储指针的那个位置。

接下来解释为什么spe.speInfo1的地址是0x1515ec0而不是你预期的0x1515ec8:这完全是C++内存对齐规则导致的,不是随机的。

我们一步步拆解对象的内存布局(假设你是在64位系统下编译,这是现在的主流环境):

  1. 基类Msg的成员布局:

    • info1int,占4字节,地址从0x1515eb0开始,到0x1515eb3结束
    • info2int,占4字节,紧跟在info1后面,地址0x1515eb40x1515eb7
    • msgchar*指针,在64位系统下占8字节,所以它的起始地址是0x1515eb8,结束地址是0x1515ebf(8字节)
    • 基类Msg的总大小是4+4+8=16字节,刚好是8字节的倍数(64位系统下,指针大小是8字节,内存对齐通常要求类的大小是最大成员大小的倍数),所以不需要额外的填充字节。
  2. 派生类SpeMsg继承自Msg,所以它的内存布局是先包含基类Msg的所有成员,再加上自己的成员spe

    • 基类Msg的内存结束于0x1515eb0 + 16 = 0x1515ec0,所以派生类的spe成员就从这个地址开始
    • spe.speInfo1int,占4字节,地址0x1515ec00x1515ec3
    • spe.speInfo2int,占4字节,紧跟其后,地址0x1515ec40x1515ec7

你之前误以为speInfo2之后才是msg的位置,但其实msg是基类里的成员,早就放在了0x1515eb8的位置,而spe是派生类添加的成员,紧跟在基类的内存之后。而0x1515ed0是你通过new char[]动态分配的字符串内存,和SpeMsg对象本身的内存布局完全无关,它的地址是操作系统在堆的另一块区域随机分配的。

总结一下:

  • 对象内部的成员是按照继承顺序和声明顺序布局的,同时要遵循内存对齐规则
  • 指针成员的存储地址(对象里的位置)和它指向的地址(动态分配的内存)是完全独立的两个概念

备注:内容来源于stack exchange,提问作者Kaldir

火山引擎 最新活动