讲一讲C语言中的结构体、位段、枚举与联合

简介: 讲一讲C语言中的结构体、位段、枚举与联合

大家好,今天我简单讲一讲C语言中的那些自定义类型,即结构体、位段、枚举与联合。

1.结构体

结构体是C语言中最常用的自定义类型,关于它的声明、定义这里就不作过多介绍,只讲一讲关于结构体内存对齐的规则。声明一个结构体,它的内存是多大呢?例如下面这个结构体:

最后会输出多少呢?

通过运行程序,可以发现得到了12这个结果,为什么会是12而不是char + int + char = 6呢?其实结构体的大小有其自己的对齐规则,具体如下:

(1)结构体的第一个成员对齐在结构体变量偏移量为0处。

(2)从第二个成员开始,对齐到“对齐数”的整数倍处。对齐数:是指该成员的大小与编译器默认的对齐数中较小的那个,若成员是数组,则算对齐数时该成员的大小按数组每个元素的大小计算。

(3)结构体的总大小必须是最大对齐数的整数倍。最大对齐数:是指结构体中所有成员的对齐数中最大的那个。

(4)若结构体中嵌套了结构体,则嵌套的结构体的对齐数是该结构体的最大对齐数,结构体的总大小是所有最大对齐数(含嵌套)的整数倍。

了解了以上结构体内存对齐的规则,我们重新算一下例子中的结构体的大小:其第一个变量group占一个字节,对齐在偏移量为0处;第二个变量int占四个字节,但要对齐到“对齐数”的整数倍处,VS的默认对齐数是8,大于该成员的大小,所以第二个成员要对齐到4的整数倍处,这里就要浪费掉3个字节的空间,age变量占用4~7这个内存块;再看第三个成员,char类型占一个字节,且对齐数就是1,它占用第9个字节(偏移量是8);而结构体的总大小必须是最大对齐数的整数倍,这里最大对齐数是4,所以又要浪费掉3个字节的空间。这样,该结构体的大小就是12了。

关于结构体内存对齐的原因,主要有一下两点:

(1)平台的原因。很多时候我们的代码需要跨平台运行,这就要求代码的可移植性要好,但不是所有的硬件平台都可以访问任意地址上的任意数据,而内存对齐的规则方便大多数平台,提高代码的可移植性。

(2)性能原因。若不存在内存对齐,有时处理器访问内存时要进行两次,但内存对齐后处理器访问内存只需一次,要知道访问内存是很浪费时间的!简而言是,这是以空间换取时间的做法。

知道了结构体内存对齐的规则后,我们以后定义结构体时可以将占用内存小的成员集中在一起,这样更节省空间。

下面说一说默认对齐数的事情,在Linux环境下是没有默认对齐数的。即使是在Windows环境下,不同编译器的默认对齐数也不尽相同,但是默认对齐数是可以修改的。看下面的代码:

与上面例子中的结构体是一样的,但是大小确实6,原因在于#pragma  pack(1)改变了平台的默认对齐数,改为了1,而#pragma  pack()又将默认对齐数改了回去。这时计算该结构的大小时自然就是6了。

最后讲一个可以计算结构体中各个成员偏移量的宏:ofsetof。使用方法为:size_t  ofsetof(声明的结构体类型,结构体中某成员名); 该宏包含在头文件<stddef.h>中。

2.位段

位段其实就是用结构体实现的,它的声明与结构体十分类似。看下面的代码:

struct X就是一个位段,注意三点:

(1)位段的成员要么全部是int型,要么全部是unsigned  int型,要么全部是char型,这表示它的内存是一次性4个字节(int型)或是一次性一个字节(char型)来开辟的。

(2)位段的成员名后面有一个冒号和一个数字,表示该成员占用几个比特位的空间。上述例子中会一次开辟四个字节的空间,共32个比特位,而四个成员总共才占用24个比特位,足够使用(剩余的8个比特位浪费掉了),所以位段的总大小就是4个字节。

(3)位段的使用很多是不确定的,并且位段不跨平台。

从某种意义上说,位段也能达到结构体的效果,并且更省空间,但是它不跨平台。

位段通常在数据传输时使用,能更好的节省空间。

3.枚举

枚举就是一一列举,在数学里面集合的内容中就有枚举法,和这里的枚举是类似的。看下面的代码:

例子中的enum  DAY,enum  Color就是枚举类型,其中的Mon,Tue,........,Red,Green, Blue是枚举类型的可能取值,也叫做枚举常量。它们都是有值的,默认从0开始,并依次递增1,如enum  Color中Red就是0,Green就是1,Blue就是2。当然,在声明时就可以给这些枚举常量赋值。

下面说一说枚举的优点,在C语言中还有#define定义的标识符常量,我们完全可以使用它来代替枚举常量,那为什么还存在枚举常量呢?原因有以下几点:

(1)枚举常量可以增加代码的可读性和维护性。

(2)和#define定义的标识符常量相比枚举有类型检查,更严谨。

(3)枚举将许多常量封装在一起,防止了命名污染。

(4)枚举常量便于调试。

(5)枚举使用方便,一次可以定义多个常量。

最后简单讲一讲枚举常量的使用,我们只能用枚举常量给枚举变量赋值,不然会有类型上的差异。

如上面的例子中:enum  Color  x  =  Red是可以的,但enum  Color  x  =  0却不行!虽然Red在数值上就是0,但在类型上却不一样。

4.联合

联合体也叫共用体,因为它的成员共用同一块空间。看下面的代码:

其中union  Stu就是一个联合体,因为是共用同一块内存,所以该联合体的大小就是四个字节,其第一个成员group与第二个成员的第一个字节是同一块内存空间。正是因为这一点,联合体的大小至少要是最大成员的大小。另外,当联合体的大小不是其最大对齐数的整数倍时,要对齐到最大对齐数的整数倍。

好了,以上就是这篇博客的全部内容了,水平有限,若有不足或错误之处,还请评论指出。如果您觉得这篇文章对你有帮助,不妨点赞收藏,谢谢。

相关文章
|
2月前
|
C语言
【C语言程序设计——循环程序设计】枚举法换硬币(头歌实践教学平台习题)【合集】
本文档介绍了编程任务的详细内容,旨在运用枚举法求解硬币等额 - 循环控制语句(`for`、`while`)及跳转语句(`break`、`continue`)的使用。 - 循环嵌套语句的基本概念和应用,如双重`for`循环、`while`嵌套等。 3. **编程要求**:根据提示在指定区域内补充代码。 4. **测试说明**:平台将对编写的代码进行测试,并给出预期输出结果。 5. **通关代码**:提供完整的代码示例,帮助理解并完成任务。 6. **测试结果**:展示代码运行后的实际输出,验证正确性。 文档结构清晰,逐步引导读者掌握循环结构与嵌套的应用,最终实现硬币兑换的程序设计。
56 19
|
2月前
|
C语言
【C语言程序设计——枚举】得到 3 种不同颜色的球的可能取法(头歌实践教学平台习题)【合集】
本关任务要求从红、黄、蓝、白、黑五种颜色的球中,每次取出3个不同颜色的球,列举所有可能的排列情况。通过定义枚举类型和使用嵌套循环语句实现。枚举类型用于表示球的颜色,循环语句用于生成并输出所有符合条件的排列 编程要求:在指定区域内补充代码,确保输出格式正确且完整。测试说明:平台将验证代码输出是否与预期一致,包括每种排列的具体顺序和总数。 示例输出: ``` Output: 1 red yellow blue 2 red yellow white ... 60 black white blue total: 60 ```
69 4
|
3月前
|
存储 网络协议 编译器
【C语言】深入解析C语言结构体:定义、声明与高级应用实践
通过根据需求合理选择结构体定义和声明的放置位置,并灵活结合动态内存分配、内存优化和数据结构设计,可以显著提高代码的可维护性和运行效率。在实际开发中,建议遵循以下原则: - **模块化设计**:尽可能封装实现细节,减少模块间的耦合。 - **内存管理**:明确动态分配与释放的责任,防止资源泄漏。 - **优化顺序**:合理排列结构体成员以减少内存占用。
225 14
|
3月前
|
存储 编译器 C语言
【C语言】结构体详解 -《探索C语言的 “小宇宙” 》
结构体通过`struct`关键字定义。定义结构体时,需要指定结构体的名称以及结构体内部的成员变量。
230 10
|
4月前
|
存储 数据建模 程序员
C 语言结构体 —— 数据封装的利器
C语言结构体是一种用户自定义的数据类型,用于将不同类型的数据组合在一起,形成一个整体。它支持数据封装,便于管理和传递复杂数据,是程序设计中的重要工具。
|
4月前
|
存储 C语言
C语言如何使用结构体和指针来操作动态分配的内存
在C语言中,通过定义结构体并使用指向该结构体的指针,可以对动态分配的内存进行操作。首先利用 `malloc` 或 `calloc` 分配内存,然后通过指针访问和修改结构体成员,最后用 `free` 释放内存,实现资源的有效管理。
344 13
|
4月前
|
存储 编译器 数据处理
C 语言结构体与位域:高效数据组织与内存优化
C语言中的结构体与位域是实现高效数据组织和内存优化的重要工具。结构体允许将不同类型的数据组合成一个整体,而位域则进一步允许对结构体成员的位进行精细控制,以节省内存空间。两者结合使用,可在嵌入式系统等资源受限环境中发挥巨大作用。
129 12
|
4月前
|
存储 人工智能 算法
数据结构实验之C 语言的函数数组指针结构体知识
本实验旨在复习C语言中的函数、数组、指针、结构体与共用体等核心概念,并通过具体编程任务加深理解。任务包括输出100以内所有素数、逆序排列一维数组、查找二维数组中的鞍点、利用指针输出二维数组元素,以及使用结构体和共用体处理教师与学生信息。每个任务不仅强化了基本语法的应用,还涉及到了算法逻辑的设计与优化。实验结果显示,学生能够有效掌握并运用这些知识完成指定任务。
86 4
|
2月前
|
存储 算法 C语言
【C语言程序设计——函数】素数判定(头歌实践教学平台习题)【合集】
本内容介绍了编写一个判断素数的子函数的任务,涵盖循环控制与跳转语句、算术运算符(%)、以及素数的概念。任务要求在主函数中输入整数并输出是否为素数的信息。相关知识包括 `for` 和 `while` 循环、`break` 和 `continue` 语句、取余运算符 `%` 的使用及素数定义、分布规律和应用场景。编程要求根据提示补充代码,测试说明提供了输入输出示例,最后给出通关代码和测试结果。 任务核心:编写判断素数的子函数并在主函数中调用,涉及循环结构和条件判断。
83 23