讲一讲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与第二个成员的第一个字节是同一块内存空间。正是因为这一点,联合体的大小至少要是最大成员的大小。另外,当联合体的大小不是其最大对齐数的整数倍时,要对齐到最大对齐数的整数倍。

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

相关文章
|
13天前
|
存储 C语言
如何在 C 语言中实现结构体的深拷贝
在C语言中实现结构体的深拷贝,需要手动分配内存并逐个复制成员变量,确保新结构体与原结构体完全独立,避免浅拷贝导致的数据共享问题。具体方法包括使用 `malloc` 分配内存和 `memcpy` 或手动赋值。
23 10
|
12天前
|
存储 大数据 编译器
C语言:结构体对齐规则
C语言中,结构体对齐规则是指编译器为了提高数据访问效率,会根据成员变量的类型对结构体中的成员进行内存对齐。通常遵循编译器默认的对齐方式或使用特定的对齐指令来优化结构体布局,以减少内存浪费并提升性能。
|
17天前
|
编译器 C语言
共用体和结构体在 C 语言中的优先级是怎样的
在C语言中,共用体(union)和结构体(struct)的优先级相同,它们都是用户自定义的数据类型,用于组合不同类型的数据。但是,共用体中的所有成员共享同一段内存,而结构体中的成员各自占用独立的内存空间。
|
17天前
|
存储 C语言
C语言:结构体与共用体的区别
C语言中,结构体(struct)和共用体(union)都用于组合不同类型的数据,但使用方式不同。结构体为每个成员分配独立的内存空间,而共用体的所有成员共享同一段内存,节省空间但需谨慎使用。
|
22天前
|
编译器 C语言 C++
C语言结构体
C语言结构体
21 5
|
27天前
|
存储 C语言 C++
深入C语言,发现多样的数据之枚举和联合体
深入C语言,发现多样的数据之枚举和联合体
深入C语言,发现多样的数据之枚举和联合体
|
23天前
|
编译器 Linux C语言
C语言 之 结构体超详细总结
C语言 之 结构体超详细总结
13 0
|
27天前
|
存储 编译器 Linux
深入C语言:探索结构体的奥秘
深入C语言:探索结构体的奥秘
|
26天前
|
C语言 C++
C语言 之 内存函数
C语言 之 内存函数
31 3
|
C语言
《C语言及程序设计》实践项目——枚举应用
返回:贺老师课程教学链接 【项目1-对称点】 设计函数,可以按指定的方式,输出一个平面点的对称点 下面给出枚举类型定义和main函数(测试函数),请写出output函数的实现。 #include&lt;stdio.h&gt; enum SymmetricStyle {axisx, axisy, point};//分别表示按x轴, y轴, 原点对称三种方式 void
1091 0