1.数据对齐

为什么要对齐:通俗点解释就是CPU对数据访问时,每次都是取固定数量的字节数,假如一次取4个字节,若有个int存在0x01-0x04,则一次就能取出,若存在0x03-0x06,则需要分两次才能取到(第一次0x01-0x04,第二次0x05-0x08),这样会降低CPU效率,更何况还有像short,char之类的不是4个字节的数据。因此,编译器会对数据进行强制对齐。

对齐规则:

1.任何K字节的基本对象的地址必须是K的倍数

2.在结构末尾根据需要会做一些填充,使其一旦被拓展为数组时可以满足条件1

例子:

struct S1

{

  int i;

  char c;

  int j;

};

分析:

i起始地址偏移量为0,满足条件1,占用4个字节,c起始地址偏移量为4,满足条件1,占用1个字节,本来j起始地址偏移量应该为5,然而为了满足条件1,编译器会在c后面填充3个字节,使j起始地址偏移量为8,另外j占用4个字节,所以S1一共占用12个字节。再看一下是否满足条件2:一旦声明了 struct S1[2],第二个结构体的第一个元素i的起始地址偏移量为12,满足条件1,因此S1一共占用12个字节没问题。

再来一个例子:

struct S2

{

  int i;

  int j;

  char c;

};

分析:

i起始地址偏移量为0,满足条件1,占用4个字节,j起始地址偏移量为4,满足条件1,占用4个字节,c起始地址偏移量为8,满足条件1,占用1个字节,所以S2一共占用9个字节。再看一下是否满足条件2:一旦声明了 struct S2[2],第二个结构体的第一个元素i的起始地址偏移量为9,不满足条件2,因此结构体需在c后面填充3个字节,这样第二个结构体的第一个元素i的起始地址偏移量变为12,满足条件2,综上S2一共占用12个字节。

看起来似乎没问题,但其实上面的分析有个误区,下面通过一道例题来展现:

A,B,C,D都没问题,但E中,按之前的分析,a的起始地址应该是0,每个P3结构体占10个字节,P2结构体的第一个元素是int,所以t的起始地址应该是4的倍数,因此起始地址为20没问题,因为P2占16个字节,所以P5总大小为36个字节。

然而,答案是t的起始地址为24,P5总大小为40个字节。

仔细查看书前面的描述,书里并没有清晰地描述过第二条结论,我觉得第二条自己的总结有误,现在修正为:

2.在结构末尾根据需要会做一些填充,使其一旦被拓展为数组时可以满足条件1,而且拓展时是以数组中下一个结构体元素中数据大小最大那个元素为标准来对齐的。

现在重新来看E,a的起始地址为0,每个P3占10个字节,所以a[0]先占10个字节,又因为P3中最大的元素是short,因此a[1]起始地址为10,符合条件2,此时t的起始地址本应是20,但P2中最大的元素为long,因此要按8字节对齐,所以t的起始地址为24,P2占16个字节,因此P5一共占40个字节。

我们使用新修改的结论来看S2那个例子,发现也没有问题,之所以之前没看出问题,那个例子中最大的元素大小刚好和第一个元素i大小相同,所以问题没有暴露。

另外,根据之前的几个例题,很明显可以看出,结构体中元素排布的顺序不同,会直接影响浪费的空间大小,若要最小化浪费的空间,按从大到小的顺序来排即可,自己思考下就明白了,相应例题可以看习题3.45。

2.union和struct

它们的区别其实很简单,struct类似数组,把不同类型的元素按顺序存储在内存中一段连续区域中,而union则是所有元素起始地址都相同(当然,若某个元素是数组,数组中所有元素仍是顺序排列的),因此union的大小等于其最大元素的大小。

什么时候使用union呢?

当我们知道某结构中的元素是互斥使用时,就可以把它声明成union来节约空间。

例子:

node_t一共占用了24个字节的空间。

分析:

首先,枚举类型本质上是一组常数的集合体,只是这些常数有各自的命名。由于枚举变量的赋值,一次只能存放枚举结构中的某个常数。所以枚举变量的大小,实质是常数所占内存空间的大小(常数为int类型,当前主流的编译器中一般是32位机器和64位机器中int型都是4个字节),枚举类型所占内存大小也是这样。

以上这段话引用自https://blog.csdn.net/bulebin/article/details/54388735

好了,枚举变量type占4个字节,union变量info里最大数据类型为8个字节,因此需要8字节对齐,编译器在type后面填充4个字节,然后union中internal结构体和data数组起始地址相同,且都占16个字节,因此node_t一共占用8+16=24个字节。

最后要注意的一点是union中的字节序:
例子:

本书前几章提到过小端法机器的特点是高字节存高地址,低字节存低地址,以小端法机器为例来看本例:

显然u数组一共占8个字节,且起始地址和d相同,按照小端法来看,那么d的低4个字节存的是u[0],高4个字节存的是u[1].

CSAPP阅读笔记-struct, union, 数据对齐-来自第三章3.9的笔记-P183-P191的更多相关文章

  1. CSAPP阅读笔记-数组分配与访问-来自第三章3.8的笔记-P176-P183

    这一节比较简单,仅记录几个比较重要的点: 1.C语言允许对指针进行运算,计算出的值会根据该指针引用的数据类型大小进行伸缩. 例子: 其中,xE是数组的起始地址.注意,指针运算时,若最终结果为指针,则指 ...

  2. CSAPP阅读笔记-gcc常用参数初探-来自第三章3.2的笔记-P113

    gcc是一种C编译器,这次我们根据书上的代码尝试着使用它. 使用之前,先补充前置知识.编译器将源代码转换为可执行代码的流程:首先,预处理器对源代码进行处理,将#define指定的宏进行替换,将#inc ...

  3. CSAPP阅读笔记-栈帧-来自第三章3.7的笔记-P164-P176

    1.基本结构: 如上图所示,是通用的栈帧结构.大致分两块,调用者函数P和被调用者函数Q. 对P来说,要做的工作是把传递参数中多于6个的部分压栈,随后把Q返回时要执行的下一条指令的地址压栈. 对Q来说, ...

  4. CSAPP阅读笔记-汇编语言初探(控制类指令)-来自第三章3.6的笔记-P135-P163

    1.正溢出与负溢出: 首先,一个正数与一个负数相加,不可能溢出,因为结果的绝对值一定小于两个加数的绝对值,既然两个加数能合理表示出来,结果一定也能合理表示出来. 其次,正溢出是由于两个很大的正数相加, ...

  5. CSAPP阅读笔记-变长栈帧,缓冲区溢出攻击-来自第三章3.10的笔记-P192-P204

    一.几个关于指针的小知识点: 1.  malloc是在堆上动态分配内存,返回的是void *,使用时会配合显式/隐式类型转换,用完后需要用free手动释放. alloca是标准库函数,可以在栈上分配任 ...

  6. CSAPP阅读笔记-汇编语言初探(算术和逻辑操作类指令)-来自第三章3.5的笔记-P128-P135

    1.算术和逻辑操作类指令分四类:加载有效地址,一元操作,二元操作和移位,如下: 2. leaq指令,类似mov指令,它左侧的数看似是给出一个地址,在内存中从给定的地址取操作数,传给右边的目的地.但其实 ...

  7. Java程序设计(2021春)——第三章类的重用笔记与思考

    Java程序设计(2021春)--第三章类的重用笔记与思考 本章概览: 3.1 类的继承(概念与语法) 3.2 Object类(在Java继承最顶层的类) 3.3 终结类和终结方法(只能拿来用,不可以 ...

  8. CSAPP阅读笔记-汇编语言初探(数据传送类指令)-来自第三章3.2-3.3的笔记-P115-P128

    1.如何由机器代码生成汇编代码? objdump -d再加上文件名即可直接在终端看到由反汇编器恢复的汇编代码.注意,文件名并不一定得是.o文件,任何可执行文件都可以. 结果如下: 仅列举了反汇编tes ...

  9. CSAPP阅读笔记-32位64位的区别--来自第三章引言的笔记--P110

    仅从寻址上看,32位和64位机器能寻址的内存空间大小不同. 需要知道的是,计算机系统对存储器作了抽象,程序“认为”内存是一个很大的字节数组,然而实际上它是由多个硬件存储器和操作系统组合起来实现的. 程 ...

随机推荐

  1. ubuntu-server部署php+mysql运行环境

    1.apt-get install git php5 mysql-server apache2 phpmyadmin 2.sudo ln -s /usr/share/phpmyadmin /var/w ...

  2. JSTL 引入

    首先要明白jstl有如下版本:  jstl1.0的引入方式为: <taglib uri="http://java.sun.com/jstl/core" prefix=&quo ...

  3. 编写高质量代码改善C#程序的157个建议——建议115:通过HASH来验证文件是否被篡改

    建议115:通过HASH来验证文件是否被篡改 MD5算法作为一种最通用的HASH算法,也被广泛用于文件完整性的验证上.文件通过MD5-HASH算法求值,总能得到一个固定长度的MD5值.虽说MD5是一种 ...

  4. Alpha冲刺 - (8/10)

    队名:彳艮彳亍团队 组长博客:戳我进入 作业博客:班级博客本次作业的链接 Part.2 成员汇报 组员1(组长)柯奇豪 过去两天完成了哪些任务 进一步优化代码,结合自己负责的部分修改功能 代码规范完整 ...

  5. 一、Numpy基础--数组

    (一)Numpy数组对象 Numpy中的nadrray是一个多维数组对象,该对象由两部分组成: 实际的数据 描述这些数据的元数据 大部分的数组操作仅仅修改元数据部分,而不改变底层的实际数据. 数组的数 ...

  6. 基于FPGA的XPT2046触摸控制器设计

    基于FPGA的XPT2046触摸控制器设计 小梅哥编写,未经许可,文章内容和所涉及代码不得用于其他商业销售的板卡 本实例所涉及代码均可通过向 xiaomeige_fpga@foxmail.com  发 ...

  7. 【微服务架构】SpringCloud之Eureka(服务注册和服务发现基础篇)(二)

    上篇文章讲解了SpringCloud组件和概念介绍,接下来讲解一下SpringCloud组件相关组件使用.原理和每个组件的作用的,它主要提供的模块包括:服务发现(Eureka),断路器(Hystrix ...

  8. Java内存模型(转载)

    本文章节: 1.JMM简介 2.堆和栈 3.本机内存 4.防止内存泄漏 1.JMM简介 i.内存模型概述 Java平台自动集成了线程以及多处理器技术,这种集成程度比Java以前诞生的计算机语言要厉害很 ...

  9. 群晖Synology

    简介 群晖是做的最好的一家NAS公司,声称是买软件卖硬件,软件有丰富的功能. 白群晖就是指从正规渠道买软件+硬件,价格昂贵,性价比低. 黑群晖是指自己搭建或购买单独的硬件(可以是电脑主机.可以是其他厂 ...

  10. unit vs2017基于nunit framework创建单元测试

    unit  vs2017基于nunit framework创建单元测试 一.简叙: 单元测试大型项目中是必备的,所以不可忽视,一个项目的成败就看是否有单元测试,对后期的扩展维护都带来了便利. 二.安装 ...