文章详情

短信预约-IT技能 免费直播动态提醒

请输入下面的图形验证码

提交验证

短信预约提醒成功

C++内存对齐如何实现

2023-07-05 02:41

关注

本篇内容介绍了“C++内存对齐如何实现”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!

内存对齐的基本原则:

空类/静态成员

程序 1

class A{};int main() {    cout << sizeof(A) << endl;    // 1}

对于一个什么都没有的空类,实际并不是空的,因为有默认的函数,具体可以参考 (待填入网址),大小是 1,这是因为需要有一个地址,C++ 不允许两个不同的对象有相同的地址,所以 C++ 中空的类和结构体大小都是 1。

程序 2

class A{    A(){}    ~A(){}    void print() { printf("print()\n"); }    void foo() { printf("print()\n"); }    static void sprint() { printf("sprint()\n"); }};int main() {    cout << sizeof(A) << endl;    // 1}

这个类的大小仍然是1,成员函数、静态成员函数、静态成员变量都是不占用类的内存的,这是因为这些东西都是类的,而不是每个对象分别存储。static变量就是存储在全局静态区。

需要注意的是,子类继承空类后,子类如果有自己的数据成员,而空基类的1个字节并不会加到子类中去。

程序 3

class Empty {};struct D : public Empty {    int a;};

sizeof(D)为4。

再来看另一种情况,一个类包含一个空类对象数据成员,则空类对象的大小仍为1。

程序 4

class Empty {};class HaveAnInt {    int x;    Empty e;}

在大多数编译器中,你会发现 sizeof(HaveAnInt) 输出为8。这是由于,Empty类的大小虽然为1,然而为了内存对齐,编译器会为HaveAnInt额外加上一些字节,使得HaveAnInt被放大到足够又可以存放一个int。

内置类型数据成员

程序 1

class Data{    char c;    int a;}; cout << sizeof(Data) << endl;

程序 2

class Data{    char c;    double a;}; cout << sizeof(Data) << endl;

显然程序 1 输出的结果为 8,程序 2 输出的结果为 16 .

程序 1 最大的数据成员是4bytes,1+4=5,补齐为4的倍数,也就是8。而程序 2 为8bytes,1+8=9,补齐为8的倍数,也就是16。

程序 3

class Data{    char c;    int a;    char d;}; cout << sizeof(Data) << endl;

程序 4

class Data{    char c;    char d;    int a;}; cout << sizeof(Data) << endl;

程序 3 运行结果为 12,程序 4 运行结果为 8

class中的数据成员放入内存的时候,内存拿出一个内存块来,数据成员们排队一个一个往里放,遇到太大的成员时,不是将其劈成两半能放多少就放多少,而是等下一个内存块过来。这样的话,就可以理解为什么程序 3 和程序 4 两段代码输出结果不一样了,因为程序 3 是
1 + (3) + 4 + 1 + (3) = 12,而程序 4 是1 + 1 + (2) + 4 = 8。括号中为补齐的bytes。

结构体数据成员

在默认条件下,内存对齐是以class中最大的那个基本类型为基准的,如果class中的数据成员包含其他class,则递归的取其中最大的基本类型来参与比较。

程序 1

class BigData{    char array[33];}; class Data{    BigData bd;    int integer;    double d;}; cout << sizeof(BigData) << "   " << sizeof(Data) << endl;

程序 2

class BigData{    char array[33];}; class Data{    BigData bd;    double d;}; cout << sizeof(BigData) << "   " << sizeof(Data) << endl;

程序 1 和程序 2 运行结果均为:33 48

程序 1 和程序 2 中内存对其的基准均为8字节,BigData的大小均为33。在程序 1 中,BigData接下来是个int(4bytes),能够放下,这时候内存块还剩3bytes,而接下来是个double(8bytes),放不下,所以要等下一个内存快到来。因此,程序 1 的Data的size = 33 + 4 + (3) + 8 = 48,同理程序 2 应该是
33 + (7) + 8 = 48。

程序 3

 class A {                                                                                          public:                                                                                                double len;                                                                                        char str[33];                                                                                  };                                                                                                                                                                                                    class B {                                                                                          public:                                                                                                                                               A a;                                                                                               int b;                                                                                         };cout << sizeof(A) << "  " << sizeof(B) << endl;

以上代码输出的结果为: 48 56
不同于程序 1 和程序 2 ,程序 3 中的class A实际会占用41字节,但会发生8字节对齐,所以大小为48字节。对于class B,成员b的起始位置已发生8字节对齐,而class B整体还会发生8字节对齐,所以最终大小为56。

虚函数

C++ 的类中如果有虚函数,类内就会有一个虚函数表的指针 _vptr,指向自己的虚函数表,vptr 一般都是在类的最前边(取决于编译器的实现)。

class A {public:    A(){}    virtual ~A(){}    virtual void foo(){}    virtual void print() {}};

由于只是存一个指向虚函数表的指针,所以不管有多少个虚函数,都是 4 字节大小(32位下,任何指针大小都是 4,64位下,任何指针大小都是 8),比如上面这个类 A,size 就是 4。

需要注意的是就是,对于没有 override 的虚函数,基类和子类中 _vptr 指向的虚函数表中,这个虚函数的地址是一样的,也就是上边的 foo() 函数,而对于重写了的或者默认重写的析构函数来说,_vptr 指向的虚函数表中,函数地址是不一样的(当然两个类的 _vptr 地址也是不一样的,这是肯定的),这就能窥探到多态的实现了。

继承

不同的编译器对继承后类的大小的计算方式不同,有的是先继承后对齐,有的是先对齐后继承。

class A{    int i;    char c1;}class B:public A{    char c2;}class C:public B{    char c3;}

sizeof(C)结果是多少呢,gcc和vs给出了不同的结果,分别是8、16。

内存对齐的意义

比较两个结构体可以使用memcmp(void*, void*)吗?

不可以,memcmp函数是逐个字节进行比较的,而struct存在内存对齐,内存对齐时补的字节内容是垃圾值,所以无法比较。

“C++内存对齐如何实现”的内容就介绍到这里了,感谢大家的阅读。如果想了解更多行业相关的知识可以关注编程网网站,小编将为大家输出更多高质量的实用文章!

阅读原文内容投诉

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

软考中级精品资料免费领

  • 历年真题答案解析
  • 备考技巧名师总结
  • 高频考点精准押题
  • 2024年上半年信息系统项目管理师第二批次真题及答案解析(完整版)

    难度     813人已做
    查看
  • 【考后总结】2024年5月26日信息系统项目管理师第2批次考情分析

    难度     354人已做
    查看
  • 【考后总结】2024年5月25日信息系统项目管理师第1批次考情分析

    难度     318人已做
    查看
  • 2024年上半年软考高项第一、二批次真题考点汇总(完整版)

    难度     435人已做
    查看
  • 2024年上半年系统架构设计师考试综合知识真题

    难度     224人已做
    查看

相关文章

发现更多好内容

猜你喜欢

AI推送时光机
位置:首页-资讯-后端开发
咦!没有更多了?去看看其它编程学习网 内容吧
首页课程
资料下载
问答资讯