如果说数组是同一种类型的数据的连续排列的数据组织形式,那么对于不同类型的数据来说,他们被有机组织起来的方式有两种,分别是structunion

Struct

C语言的 struct 创建一种数据类型,将可能不同的数据类型的对象聚合到一个对象中。结构体中各个组成部分由名字来引用。类似于数组的实现,结构体的所有组成部分都存放在存储器中的一段连续的区域内,指向结构体的指针就是结构体第一个字节的地址。编译器维护每个结构类型的信息,指示每个字段(field)的字节偏移。以这些偏移作为存储器引用中指令的位移,从而产生对结构体元素的引用。

一个例子:

 struct rec{int i;int j;int a[2];int *p;};

那么在内存中的排列是:

对于struct中的成员,我们可以使用结构地址 + 偏移量的方式来进行访问。例如指向结构体 rec 的指针变量 r 存放在寄存器 %rdi 中,下面的代码将元素 r->i 复制到 r->j

movl    (%rdi), %eax        # 将 r->i 放到寄存器中
movl    %eax, 4(%rdi)       # 将寄存器的值复制到 r->j 中

可以看到,为了访问字段 j,代码将 r 的地址加上偏移量 4。

Union

联合体提供了一种方式,能够规避C语言的类型系统,允许以多种类型来引用同一个对象。联合体的语法和结构体一样,但语义差别很大——联合体用不同的字段来引用相同的内存

考虑下面的声明:

 struct S{char c;int i[2];double v;}​union U{char c;int i[2];double v;}

在一台x86-64 Linux 机器上编译时,字段的偏移量、数据类型的完整大小如下:

可以发现:

  • 对于类型结构体 U 的指针 p,p->cp->i[0]p->v 都是引用数据结构的起始位置
  • 一个联合体的总的大小总是等于它的最大字段的大小。

union的作用在于节省内存空间。举一个例子:

现在我们要设计一个特殊的二叉树的数据结构,它有一个特点:每个叶子节点有两个 double 类型的值,但是指针不指向任何地方。每个内部节点有左右子节点的指针,但是没有数据。如果声明如下:

 // 每个节点都需要 32 个字节struct node_s {struct node_s * left;struct node_s * right;double data[2];};

如果用上述结构体,那么每个节点都需要 32 个字节。每个类型的节点都要浪费一半的内存。如果我们这样声明:

 union node_u {struct {union node_u *left;union node_u *right;} internal;double data[2];};

那么每个节点所需要的内存大小就仅为internaldouble[2]二者所占最大的大小了,也就是16字节:

  • 当它是中间节点:我们使用n->internal.leftn->internal.right 来访问子节点
  • 当它是叶节点时:我们使用n->data[0]n->data[1]来访问

然而这样表示,我们是没办法确定一个给定的节点到底是叶子节点,还是内部节点。有一个方法,是引入一个枚举类型,定义这个联合中可能的不同选择,再用一个结构体将两者包含起来:

typedef enum {LEAF, INTERNAL} nodetype;struct node_t{nodetype type;union {struct{union node_t *left;union node_t *right;} internal;double data[2];} info;
};

这个结构共需24个字节:

  • type是4个字节(int值)
  • union info是16个字节
  • 他们二者之间需要做填充,因此是 16 + 8 = 24个字节

但是从这个例子来说,使用union是得不偿失的,因为它并没有节省多少空间,但是它在编码带来了不少麻烦。

对于那些有较多字段的数据结构,这种空间上的节省可能才是足够吸引人的。

数据对齐

对齐主要是为了提高内存的访问效率。

计算机系统对于基本的数据类型的合法地址做出了一定的限制,要求地址必须是某个值的倍数(例如4、8、16)。这种对齐限制简化了处理器系统和存储器系统之间的接口硬件设计。

CPU在读取数据时,会从偶地址开始读取一定位的内存数据,如果一个数据存放的地址不是从偶地址开始的,那么为了读取这个完整的数据,CPU需要读取两次。

计算机通过将部分内存“闲置”的方法,来提高内存的访问效率,这种做法就叫做数据对齐。

计算机字节对齐的规则是:

一个例子是:

 struct S{char c;int i[2];double v;} *p;

它的总大小是24字节。显然不等于 sizeOf(c) + sizeOf(i) + sizeOf(v) = 1 + 4*2 + 8 = 17,如果是这样的话,它的内存排布则是十分紧凑的:

实际上,编译器使用了对齐的手段。它在x86-64位上内存中的排布方式是:

int值必须在以4的倍数的地址处被存储double值必须在以8的倍数的地址处被存储

那么对于

struct S{double v;int i[2];char c;
} *p;

则为,它在结尾会以8字节,即以struct内的double的对齐方式进行对齐:

原因在于,它基于一个假设,即,如果此结构体构建一个数组,且我们可以保证地址是8的倍数的话,这个结构体数组的每个元素都是合理对齐的。

c语言struct_Introduction to CSAPP(十七):复杂数据组织与C语言的 struct与union相关推荐

  1. c语言程序设计吉林大学康辉目录,[吉林大学康辉] 经典C语言程序设计视频教程 51讲...

    [吉林大学康辉] 经典C语言程序设计视频教程 51讲 [吉林大学康辉] 经典C语言程序设计视频教程 51讲 直接复制用迅雷下载.用专用的teachingplayer播放器.(不能下载留言) 第一集 t ...

  2. c语言中平均值用什么表示_学C语言有什么用?

    这个也是我刚学C语言的疑问,当时总是写一些比较大小,排序之类的函数,很是枯燥,也学的不怎么样.学东西是要有用它的地方才有动力继续学习, C语言的应用很广的,比如 我以前学习的单片机,现在学习的嵌入式, ...

  3. c语言 左补1,转专业后对于C语言补修的一些体会(1)

    在转入软件工程后,原来的C语言程序设计只有三学分,而信息学院的C语言程序设计有四学分.迫于无奈的我只能再补修一遍C语言,自我认为大一对于C语言的学习已经基本足够,但我发现信息学院用的是不一样的书后,对 ...

  4. c语言数组中的字母可以相等吗,C语言数组比较

    C语言数组比较教程 在 要比较两个 C 语言的数组是否相等,我们只有使用 案例 比较两个数组是否相等 通过循环遍历数组来比较数组 #include int main(){ printf("嗨 ...

  5. 每个c语言程序写完后 都要按照,c语言基础学习小结(习题总结)(5页)-原创力文档...

    书山有路勤为径 学海无涯苦作舟 一.思考题. 1.你如何向别人解释清楚什么是编程.什么是计算机语言? 2.什么是C语言? 二.解答题. 1.用C语言编写程序:求任意两个整数的和.如果是小数的话,要求输 ...

  6. python语言程序设计2019版第二章课后答案-python语言程序设计基础(嵩天)第二章课后习题...

    **第二学期第一周学习总结 一. 本周学习内容总结 一维数组,了解了一维数组的定义(定义一个数组,需要明确数组变量名,数组元素的类型和数组大小,即数组中元素的数量) 一维数组定义的一般形式为:类型名, ...

  7. python是一种语言还是一个软件-自动化专业想学一门语言 是学Python 还是Java ?...

    1.java语言排行榜 TIOBE发布了2019年5月份编程语言排行榜,以下是网站译文,(翻译的可能有点鬼畜,凑活看吧)5月标题:统计语言R不属于TIOBE指数前20名 在进入前20名大约3年后,统计 ...

  8. python与c语言在语法上的区别-python和c语言的区别是什么

    Python可以说是目前最火的语言之一了,人工智能的兴起让Python一夜之间变得家喻户晓,Python号称目前最最简单易学的语言,现在有不少高校开始将Python作为大一新生的入门语言.本萌新也刚开 ...

  9. python与c语言在语法上的区别-Python与C语言基础对比(Python快速入门)

    代码较长,建议使用电脑阅读本文. 10分钟入门Python 本文中使用的是Python3 如果你曾经学过C语言,阅读此文,相信你能迅速发现这两种语言的异同,达到快速入门的目的.下面将开始介绍它们的异同 ...

最新文章

  1. 关于Jsoup解析https网页的问题
  2. html4中basefont,html basefont标签怎么用
  3. 以下关于c语言中static和const,c语言中static const作用
  4. 顶会ICML特别开设“怼日”Workshop,意见不同您尽管来
  5. Foxmail配置IMAP账号
  6. NumPy学习笔记之zeros_like()函数(包含zeros函数)
  7. 五十九、备战蓝桥杯 - Java算法 (基础练习一)
  8. SQL Injection Bypassing WAF ——from DVWA ——2012-12-8
  9. 网络营销之QQ推广技巧
  10. no.4京东话费充值系统架构演讲读后感
  11. 个人防骗大全精选(1)
  12. bootstarp table完成数据渲染之后展示趋势图(echart折线图)
  13. 学会读懂traceback,处理Python异常
  14. RGB LED 七彩跳变
  15. 学计算机要学好数学吗,学好数学对计算机专业重要吗?
  16. RecyclerView 多条目
  17. MD5算法在PB中的实现(转载自 - 阿多米 - 博客园)
  18. Keil5下载程序报错问题总结
  19. HTC 系列手机解锁,刷recovery以及刷Rom教程
  20. 面试通过,背调凉了,HR:肯定不录用...

热门文章

  1. 解决mysql获取不到连接的问题
  2. Android 异常: failed to connect to localhost/127.0.0.1
  3. 如何使用jQuery替换div的innerHTML?
  4. 无法在终端中显示Git树
  5. 如何在JavaScript中验证电子邮件地址
  6. maven2创建或者导入eclipse工程 设置M2_REPO消除错误
  7. Silverlight+WCF 新手实例 象棋 棋子(三)
  8. iOS使用shell脚本批量修改属性
  9. selenium中webdriver识别class属性多个值中有空格的解决方案
  10. 关系类型总结和对应的注解