书里给了一段代码,假如有个结构体如下:
struct test {
    char a;
    int b;
    long c;
    void* d;
    int e;
    char* f
}
这个结构体的大小是多少呢?
 
先来看一下 C 语言中不同数据类型的长度,因操作系统而异:
数据类型
bool
char
short
int
float
double
long
long long
指针
长度(字节/64位)
1
1
2
4
4
8
8
8
8
长度(字节/32位)
1
1
2
4
4
8
4
8
4
假设是64位机器,则上面代码段中的结构体总大小是 1+4+8+8+4+8=33,然而正确的答案是 40!
这是因为结构体按照8字节对齐,如图所示:
虽然char a 只占了1字节,int b 只占了4字节,但根据 8 字节对齐后,a 和 b 之间空了 3 字节。同样,char* f 和 int e 之间空了 4 字节,因此总大小为 40 字节。
 
以上是书中对结构体对齐的介绍,这么简单!我稍微改了一下结构体,发现自己还是不能快速算出结构体的大小,而书中关于结构体对齐的介绍有限,因此补了一下结构体对齐这方面的知识。
首先是 3 个基本概念:
1) 自身对齐值:
    数据类型的自身对齐值参见上面的表格,如:64位机中,char型数据自身对齐值为 1 字节,int型为 4 字节等。
    结构体或类的自身对齐值就是其成员中自身对齐值最大的那个值。如:上面的 test 结构体,其自身对齐值是 8,因为有 long 型和指针型,它们的长度最大,都是 8。
2) 指定对齐值:#pragma pack (n),n 就是对齐系数。如:#pragma pack (2) 指定按 2 字节对齐。
3) 数据成员、结构体和类的有效对齐值:自身对齐值和指定对齐值中较小者,即有效对齐值=min{自身对齐值,当前指定的pack值}。
 
然后是对齐原则:
1) 结构体的起始存储位置能够被该结构体中最大的数据类型的大小所整除;
2) 每个数据成员存储的起始位置是自身大小的整数倍(比如int在 64 位机为 4 字节,则int型成员要从 4 的整数倍地址开始存储),若不满足,会根据需要自动填充空缺的字节;
3) 结构体的总大小为该结构体最大基本类型成员大小的整数倍,若不满足,会根据需要自动填充空缺的字节。
简而言之就是:
1) 第一个成员在与结构体变量偏移量(offset)为0的地址处。
2) 其他成员变量要对齐到有效对齐值的整数倍的地址处。
3) 结构体总大小为最大自身对齐值的整数倍。
 
补充说明:
1) 如果结构体包含另一个结构体成员,则被包含的结构体成员要从其原始结构体内部最大对齐模数的整数倍地址开始存储。比如 struct a 里存有struct b,b 里有 char,int,double 等元素,而 double 的长度是8,那 b 应该从 8 的整数倍开始存储。
2) 如果结构体包含数组成员,比如 char a[3],它的对齐方式和分别写 3 个 char 是一样的,即它还是按一个字节对齐。如果写:typedef char Array[3],Array这种类型的对齐方式还是按 1 个字节对齐,而不是按它的长度 3 对齐。
3) 如果结构体包含共用体成员,则该共用体成员要从其原始共用体内部最大对齐模数的整数倍地址开始存储。
4) 一般情况下32位默认 4 字节对齐,64位默认 8 字节对齐。
 
练习:按 4 字节对齐,struct A 的大小是多少?
struct A {
    long a1;
    short a2;
    int a3;
    int *a4;
};
long a1;    //8
short a2;  //2 8+2=10(不是4的倍数)对齐到4的倍数12
int a3;     //4 4+12=16(4的倍数)
int *a4;   //8 8+16=24(4的倍数)
所以 struct A 的大小是 24。
 
有网友说:快速计算总大小可以归纳成 2 步:
1) 前面单元的大小必须是后面单元大小的整数倍,如果不是就补齐;
2) 整个结构体的大小必须是最大字节的整数倍。
举个例子,结构体如下:
struct B {
    int a;
    char b;
    char c;
};
int a 是 4 字节,char b、c 都是 1 字节,最大的长度是 4 字节;
step1:int a 在前,char b 在后,int a 的长度是 char b 的 4 倍,累计 4+1=5 字节,
          char b 在前,char c 在后,char b 的长度是 char c 的 1 倍,累计 5+1=6 字节;
step2:6(累计大小)不是4(最大长度)的整数倍,所以最后结果为 8。
下图是内存分配图,左图为按 8 对齐,右图为按 4 对齐。
再比如:
struct C {
     char b;
     int a;
     char c;
};
int a 是 4 字节,char b、c 都是 1 字节,最大的长度是 4 字节;
step1:char b 在前,int a 在后,1 != 4*N,所以 b 要补到 4 字节,累计 4+4=8 字节,
       int a 在前,char c 在后,int a 的长度是 char c 的 4 倍,累计 8+1=9 字节;
step2:9(累计大小)不是4(最大长度)的整数倍,所以最后结果为 12。
这个方法很好用,但还是要注意具体情况具体分析。
 
为什么要进行内存对齐?
若无内存对齐情况下,按照连续存储时,1234 5678作为8字节,在结构体中,char c会存储在1号位上,而int i会存储在2345位上,而CPU在读取在访问c的时候,每次访问4个字节,没有什么问题,会先拿出1---4,再拿出5---8,但是int i被切割开了,仍需要做字节切割及字节拼接,效率很低。
而进行内存对齐时,将char c存放在1号位,再偏移3个字节,将int i存储在5--8号位,这样CPU进行访问的时候,不必做字节上的拼接和切割,效率会大大提高。是一种典型的空间换时间以提高效率的方式。

《PHP7底层设计与源码实现》学习笔记2——结构体对齐的更多相关文章

  1. 《PHP7底层设计与源码实现》学习笔记1——PHP7的新特性和源码结构

    <PHP7底层设计与源码实现>一书的作者陈雷亲自给我们授课,大佬现身!但也因此深感自己基础薄弱,遂买了此书.希望看完这本书后,能让我对PHP7底层的认识更上一层楼.好了,言归正传,本书共1 ...

  2. zepto 源码 $.contains 学习笔记

    $.contains(parent,node)  返回值为一个布尔值 ==> boolean parent,node我们需要检查的节点检查父节点是否包含给定的dom节点,如果两者是相同的节点,返 ...

  3. c++ stl源码剖析学习笔记(一)uninitialized_copy()函数

    template <class InputIterator, class ForwardIterator>inline ForwardIterator uninitialized_copy ...

  4. C#学习笔记之结构体

    1.概述 结构是一种与类相似的数据类型,不过它较类更为轻量,一般适用于表示类似Point.Rectangle.Color的对象.基本上结构能办到的类全都能办到,但在某些情况下使用结构更为合适,后面会有 ...

  5. STL源码剖析 学习笔记 MiniSTL

    https://github.com/joeyleeeeeee97 目录: 第二章 空间适配器 第三章 迭代器 第四章 序列式容器(vector,list,deque,stack,heap,prior ...

  6. requests源码阅读学习笔记

    0:此文并不想拆requests的功能,目的仅仅只是让自己以后写的代码更pythonic.可能会涉及到一部分requests的功能模块,但全看心情. 1.另一种类的初始化方式 class Reques ...

  7. c++ stl源码剖析学习笔记(二)iterator

    ITERATOR 迭代器 template<class InputIterator,class T> InputIterator find(InputIterator first,Inpu ...

  8. c++ stl源码剖析学习笔记(三)容器 vector

    stl中容器有很多种 最简单的应该算是vector 一个空间连续的数组 他的构造函数有多个 以其中 template<typename T> vector(size_type n,cons ...

  9. STL源码剖析-学习笔记

    1.模板是一个公式或是蓝图,本身不是类或是函数,需进行实例化的过程.这个过程是在编译期完成的,编译器根据传递的实参,推断出形参的类型,从而实例化相应的函数 2. 后续补充-.

随机推荐

  1. Oracle自定义脱敏函数

    对于信息安全有要求的,在数据下发和同步过程中需要对含有用户身份信息的敏感字段脱敏,包括用户姓名.证件号.地址等等,下面是自定义函数的代码 CREATE OR REPLACE FUNCTION F_GE ...

  2. 交叉编译用于生成aarch64指令的GCC (9.2)

    参考 Building GCC as a cross compiler for Raspberry Pi How to Build a GCC Cross-Compiler 环境 PC: ubuntu ...

  3. 团队第五次作业:alpha2成绩汇总

    一.作业题目 团队第五次作业:alpha2发布 二.作业评分标准 博客评分规则(总分100)博客要求 给出开头和团队成员列表(10') 给出发布地址以及安装手册(20') 给出测试报告(40') 给出 ...

  4. Java 静态、类加载

    1.静态是什么?有什么用? static的主要作用在于创建独立于具体对象的域变量或者方法. 每创建一个对象,都会在堆里开辟内存,存成员(属性),但是不存方法,方法是共用的,没必要每一个对象都浪费内存去 ...

  5. org.springframework.beans.NotWritablePropertyException:Bean property 'xxxService' is not writable or has an invalid setter method.

    完整报错提示信息:Caused by: org.springframework.beans.NotWritablePropertyException: Invalid property 'blogDe ...

  6. 微信小程序 scroll-view 横向滚动条 隐藏无效

    看了许多网上教程说是添加如下样式可以解决,我加入到组件wxss中无效,加入全局wxss生效. 添加css代码如下: ::-webkit-scrollbar { ; ; color: transpare ...

  7. gnome 3 插件设置

    插件安装及管理方法 应该需提前在gnome-tweaks中打开user-theme,重启电脑后才可找到Add-ons Debian9 下在应用商店插件add-ons里进行选择安装,在应用商店已安装应用 ...

  8. pcm混音的一种方式

    转载 混音: Mix的意思是混音,无论在自然界,还是在音频处理领域这都是非常普遍的现象.自然界里你能同时听到鸟鸣和水声,这是因为鸟鸣和水声的波形在空气中形成了叠加,耳朵听到后能区分鸟鸣和水声这两种波形 ...

  9. 第02组Alpha冲刺(3/4)

    队名:十一个憨批 组长博客 作业博客 组长黄智 过去两天完成的任务:写博客,复习C语言 GitHub签入记录 接下来的计划:构思游戏实现 还剩下哪些任务:敲代码 燃尽图 遇到的困难:Alpha冲刺时间 ...

  10. ZROI 暑期高端峰会 A班 Day1 组合计数

    AGC036F Square Constriants 一定有 \(l_i<p_i\le r_i\). 考虑朴素容斥,枚举每个数是 \(\le l_i\) 还是 \(\le r_i\).对于 \( ...