大家好,今天我简单讲一讲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与第二个成员的第一个字节是同一块内存空间。正是因为这一点,联合体的大小至少要是最大成员的大小。另外,当联合体的大小不是其最大对齐数的整数倍时,要对齐到最大对齐数的整数倍。
好了,以上就是这篇博客的全部内容了,水平有限,若有不足或错误之处,还请评论指出。如果您觉得这篇文章对你有帮助,不妨点赞收藏,谢谢。