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

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

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

月球上的星星

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值