深入 C 语言和程序运行原理
于航
PayPal 技术专家
21121 人已学习
新⼈⾸单¥59
登录后,你可以任选4讲全文学习
课程目录
已完结/共 49 讲
深入 C 语言和程序运行原理
15
15
1.0x
00:00/00:00
登录|注册

07|整合数据:枚举、结构与联合是如何实现的?

结构体大小:24 bytes (考虑对齐和填充)
提高应用程序健壮性
使用枚举标签标识生效字段
影响 sizeof 结果
保证自然对齐
影响结构对象内存大小
提高数据访问效率
结构体大小分析
Tagged Union
大小等于最大字段大小
所有字段共享同一内存空间
填充字节
内存数据对齐
数据字段在内存中顺序排列
组织异构数据的复合类型
注意避免枚举值与整数值的混用
编译器将枚举值替换为整数值
枚举值为“具名整型”
表示有限抽象实体
思考题
联合 (union)
结构 (struct)
枚举 (enum)
C语言数据类型实现

该思维导图由 AI 生成,仅供参考

你好,我是于航。
C 语言为我们提供了高于机器指令的一定抽象能力,这使得我们能够以接近自然语言的方式来构建应用程序。如果说使用 C 语言是用砖块来造房子,那使用其他高抽象粒度编程语言,就是直接以墙面为单位来搭建。很明显,从这个角度来说,C 语言用起来不如其他高级语言方便,但它也同时给予了更细的构建粒度,让我们能够按照自己的想法,灵活自定义墙面的形态。
对于这里提到的砖块和墙面,你可以将它们简单理解为编程语言在构建程序时使用的数据类型。比如在 Python 语言中,我们可以使用集合(set)、字典(dict)等复杂数据类型。而在 Java 语言中,Map 本身又会被细分为 HashMap、LinkedHashMap、EnumMap 等多种类型,供不同应用场景使用。
为了在保持自身精简的同时也保证足够高的灵活性,C 语言在提供基本数值类型和指针类型的基础上,又为我们提供了结构(struct)、联合(union)与枚举(enum)这三种类型。结合使用这些类型,我们就能将小的“砖块”组合起来,从而将它们拼接成为更大的、具有特定功能结构的复杂构建单元。
接下来,就让我们一起看看:编译器是如何在背后实现这三种数据类型的?而在实现上,为了兼顾程序的性能要求,编译器又做了哪些特殊优化?
确认放弃笔记?
放弃后所记笔记将不保留。
新功能上线,你的历史笔记已初始化为私密笔记,是否一键批量公开?
批量公开的笔记不会为你同步至部落
公开
同步至部落
取消
完成
0/2000
荧光笔
直线
曲线
笔记
复制
AI
  • 深入了解
  • 翻译
    • 英语
    • 中文简体
    • 中文繁体
    • 法语
    • 德语
    • 日语
    • 韩语
    • 俄语
    • 西班牙语
    • 阿拉伯语
  • 解释
  • 总结

本文深入探讨了C语言中枚举、结构和联合的实现原理。首先,枚举类型在C语言中被编译为int类型,但存在一些潜在问题。其次,结构类型在内存中的布局并非严格连续,编译器会进行数据对齐以满足CPU的最高效率读写需求。文章通过C代码和对应的汇编代码展示了枚举和结构类型的实现细节,以及内存数据对齐的重要性。最后,联合是一种特殊的复合数据类型,内部定义的所有数据字段将占用同一块内存空间。总结来说,本文通过深入分析编译器对这三种数据类型的实现方式,为读者提供了对C语言中数据类型底层实现原理的深入理解,为编写高质量程序提供了有益的参考。

仅可试看部分内容,如需阅读全部内容,请付费购买文章所属专栏
《深入 C 语言和程序运行原理》
新⼈⾸单¥59
立即购买
登录 后留言

全部留言(15)

  • 最新
  • 精选
  • zxk
    共 24 字节: 1. short a、char b、char c 共 2 + 1 + 1 = 4 bytes; 2. padding = 4 bytes; 3. int* d 为 8 bytes; 4. 联合大小取决于最大的 double,为 8 byes; 综上,共为 2 + 1 + 1 + 4 + 8 + 8 = 24 bytes。

    作者回复: 回答正确!

    2022-01-22
    11
  • Pushsadfc
    老师,那个关于c语言如何构建项目或者编程规范的书有推荐的咩

    作者回复: 有一本叫《C in a Nutshell: The Definitive Reference》的书,不知道符不符合你的需求。

    2022-01-02
    2
  • liu_liu
    a - 2 字节 b - 1 c - 1 padding - 4 d - 8 union - 8 共 24 字节。 ------- 但老师,我有些地方不太明白。 当给思考题中的结构体赋值(我给它起名叫 User),并且 union 赋值为浮点数 1.25,如下: int main() { struct User u = {1, 'a', 'b', 0x10000, .e = 1.25}; return 0; } 汇编代码如下: ------ main: push rbp mov rbp, rsp mov WORD PTR [rbp-32], 1 mov BYTE PTR [rbp-30], 97 mov BYTE PTR [rbp-29], 98 mov QWORD PTR [rbp-24], 65536 movsd xmm0, QWORD PTR .LC0[rip] movsd QWORD PTR [rbp-16], xmm0 mov eax, 0 pop rbp ret .LC0: .long 0 .long 1072955392 -------- .LC0 这段应该是浮点数 1.25 的值。 movsd xmm0, QWORD PTR .LC0[rip],我猜它的含义是把 1.25 的值放入 xmm0 寄存器中。 1. 那 .LC0[rip] 的意思是什么呢?为什么要把 rip 传入呢? 2. 为什么 .LCO 中使用两个 .long 来表示呢?这里的具体含义是什么呢? 望老师解答~

    作者回复: 很好的问题!下面是回答: 1. 这里的 “.LC0[rip]” 实际上是汇编代码中的一种通过 rip 寄存器来进行相对寻址的方式,一般也称为 pc-relative 或 rip-relative。rip 寄存器中存放着下一条将要执行的指令。而这里该条指令的寻址结果便是 .LC0 这个 label 的开头。 2. .long 是一个汇编指示标记,用于让汇编器在当前这个位置保留一个 4 字节的值。这里第一个 .long 0 是 1.25 对应二进制表示的低 32 位;第二个 .long 1072955392 则是对应高 32 位的十进制值。

    2021-12-22
    3
    2
  • sky
    问答题,X86_64架构下,24字节。综合考虑两个点: 1.结构体 内部成员 自然对齐; 2.结构体数组的内存对齐。

    作者回复: 正确!

    2021-12-22
    1
  • pedro
    x86_64 的平台下,sizeof 得到的大小为:24 = 8 + 8 + 8, 加上 packed 属性后,sizeof 为 20 = (1 + 1 + 2) + 8 + 8。

    作者回复: 没错!

    2021-12-22
    1
  • tanatang
    "因此,让枚举类型有助于组织程序代码的同时并确保它不被乱用,也是我们在构建高质量程序时需要注意的一个问题。" 这个具体怎么做呢? 比如将 foo()入参定义为 枚举类型。 但是调用 foo(3) 或者 int i= 6;foo(i)。 一样可以编译执行。

    作者回复: 是的,你举的例子就是一个乱用的方式。编译器实际上有很大的宽容性,可以允许各种样式的代码通过编译,但在代码组织层面,各种语法结构怎么使用,需要团队整体有一个规范来约束。就比如你既然用枚举了,那就不要直接传数字,而是应该用带有业务含义的枚举值,等等。

    2023-02-08归属地:四川
  • may_huang
    老师有比较好的讲汇编的书推荐吗?代码中有些汇编代码不是很懂。

    作者回复: 可以看看有关 x86 汇编的书,推荐一本入门的:《x86汇编语言:从实模式到保护模式》,进阶一点可以看《X86/X64 体系探索及编程》。

    2022-08-03归属地:北京
    2
  • 好吃不贵
    可以用gdb的ptype来查看各字段的大小。 ptype /o a /* offset | size */ type = struct { /* 0 | 2 */ short a; /* 2 | 1 */ char b; /* 3 | 1 */ char c; /* XXX 4-byte hole */ /* 8 | 8 */ int *d; /* 16 | 8 */ union { /* 8 */ double e; /* 4 */ int f; /* total size (bytes): 8 */ }; /* total size (bytes): 24 */ }

    作者回复: 👍🏻

    2022-06-15
  • 张贺
    40个字节

    作者回复: 不对哦,再看看?

    2021-12-22
  • =
    x86-64 gcc 11.2编译器下,为24bytes。 short类型的a占用2bytes; char类型的b占用1bytes,此时满足自然对齐; char类型的c占用1bytes,此时满足自然对齐; 指针类型的d占用8bytes,此时不满足自然对齐,需要先填充4bytes的padding,然后再放置8bytes的d; 联合的大小与double类型保持一致,因此是8bytes,此时满足自然对齐。 综上,总共是2+1+1++4+8+8=24bytes
    2022-01-05
    2
    6
收起评论
显示
设置
留言
15
收藏
沉浸
阅读
分享
手机端
快捷键
回顶部