02 内存布局

  1. 参考链接

  2. 总览

    • 主要介绍对象的内存布局.
    • 比如对齐,偏移,虚表,虚表在内存位置等.
    • 关于一个类的信息,都在ABI中,可以随时访问.
  3. 基本概念

    • sizeof(O)

      • 关键字表示某个对象占用字节.
      • 比如sizeof(int) == 4一样.
    • align(O)

      • 关键字表示某个对象的对齐是多少.
      • C语言中的字节对齐,比如1,2,4,8字节对齐.
    • offset(C)

      • 表示在对象O中,成员变量C相对于第一个的偏移量.
    • 小结

      • 前面的这些信息在编译后都可以知晓.
      • 在执行时也可以知晓.
      • 对一个类即成员变量都有相关信息的记载.
    • dsize(O)

      • 也就是sizeof(O) = dsize(O) + padding.
      • padding即为了对齐在对应位置进行补齐.
    • nvsize(O)

      • 也就是去掉虚继承的类的大小.
      • virtul继承的类,包括父类用virtual继承的某个祖父类.
    • nvalign

      • 去掉virtul父类后的对齐.
  4. POD数据类型

    • POD for the purpose of layout

      • 对于这类型的size,alignmentC ABI中就已经指定,即:
      • bool占用一个字节.
      • 空类不会报错,size,alignment都是1.
      • T&,T&&都当作指针类型T*.
      • 成员指针和C一样.
  5. 成员指针

    • 成员变量指针

      • int T::*varname = &T::member;
      • 访问就可以通过对象访问.T().*varname;
      • ABI中,其属性使用ptrdiff_t记录. 即某个成员在某个类的内存布局中的偏移量.
      • 注意区别空指针和偏移量为0.虽然两者值都一样. 一般来说,用-1表示空指针,而不应该用NULL表示.
      • 但是-1也是可能的,比如,用父类的成员指针,通过偏移量,强转之类,计算前一个父类的成员. 这种不建议,也不合适.
      • 强转加上偏移量虽然也可以获取. 但是不建议.
      • 直接进行偏移量的加减以此访问前后的数据虽然也可以,但是也不可取.
      • 注意: virtual类型的类型转换不支持.
      • TODO
      • 总结: 成员变量指针的值是偏移量,解引用则是进行加减.
    • 成员函数指针

      • 不同平台实现不同.
      • 一般是用struct{fnptr_t ptr;ptrdiff_t adj;},ptr表示函数位置.adj表示对象位置.
      • ptr是虚函数,则是1+offset_in_vtable,如果是普通函数则是地址.+1是为了区分空指针.0表示空指针,1表示偏移量为0.在调用的时候加进来.
      • adj则是表示在调用之前,需要添加对应的偏移量.指针指向对应函数的对应对象.
      • 影响结构体和指针实现的依据:
      • 函数地址低位为0,这个很多都没有实现,这个依赖平台.
      • 虚函数中,空的函数指针必须要和偏移量为0区分. 前面介绍了+1的操作规避.
      • 函数在虚表中的偏移量不会是奇数. 这种也和实现有关,有的可能指针没有对齐,或者指针是奇数位.
      • 虚函数调用,在仅仅知道偏移量和类型的时候也可以调用. 大多都可以,偏移加上虚表首地址定位到对应位置存放的函数指针.
      • 可以进行指针的加减运算,以获取某个函数. 注意: 是否可以用于访问protected的虚函数指针.
      • 执行函数调用:
      • adj += this,以转移到正确位置.
      • ptr的转换,得到函数对应地址.
  6. POD类类型

    • 简介

      • 即一个类不符合POD规定,即C++风格的类.
      • 下面就是介绍这类数据的内存布局.
    • 背景

      • C类型的类,这个类C不符合POD for the purpose of layout的定义.
      • 并且,假设C的所有的信息已经处理,即成员变量和父类的size,dsize,nvsize,alignment,nvalign都已经明确.
      • 那么下面就介绍如何将这些组合成一个新的,符合规范的类C的内存布局.
      • 父类和成员都已经明确,但是自己还没有明确的组装好.下面就开始分析怎么组装.
    • 1.Initialization

      • 先将C的相关信息初始化为0.
        1. 如果C是一个多态类.
        • a. 找到父类中直接或间接继承的virtual类,从中赛选出是primary base class的类. 将这列命名为indirect primary base class,即间接的主类.
        • b. 如果C有一个类是多态类,从这些类中找出一个符合条件的类B作为primary base class,即公用一个virtual table. 先从直接父类中,非virtual继承的里面找符合条件的.如果一个都不符合,找广度优先遍历找到的第一个nearly empty base class作为primary base class,同时这个类还需要满足不是indirect primary base class. 最后则从indirect primary base class中找第一个作为primary base class.
        • c. 如果C还是没有找到primary base class,自己创建一个虚表,并初始化.
      • 前面2.b描述的设计现在看来有点问题. 将第一个indirect primary base class作为Cprimary base class并不会节约空间.甚至还会造成

Guess you like

Origin blog.csdn.net/rubikchen/article/details/121407854