至此,该位段结构的内存分配结束,共占据3个char 类型数据的大小,即 3 个字节
2.3 位段的跨平台问题:
我们上面说过,位段涉及很多不确定因素,位段是不跨平台的,注重可移植的程序应该避免使用位段,并且在未来位段结构的使用过程中,我们一定要提前仔细地研究好位段在不同编译器下使用时,究竟是如何进行内存分配的,再结合我们的实际需求实现跨平台使用。
而在位段进行跨平台使用时,我们通常需要注意以下四个关键点:
int 位段被当成有符号数还是无符号数是不确定的。
位段中最大位的数目不能确定。(16位机器最大16,32位机器最大32,写成27,在16位机器会出问题)
位段中的成员在内存中从左向右分配还是从右向左分配的标准尚未定义。
当一个结构包含两个位段,第二个位段成员比较大,无法容纳于第一个位段剩余的位时,是舍弃剩余的位还是利用,这是不确定的。
总结下来,跟结构相比,位段可以达到跟结构相同的效果,并且可以更好的利用空间,但同时存在着跨平台问题
。
3.枚举
枚举是列出某些有穷序列集的所有成员的程序,或者是一种特定类型对象的计数。这两种类型经常但不总是重叠。是一个被命名的整型常数的集合。简单来说就将某种特定类型的对象一一进行列举。
枚举的声明与结构和联合相似, 其形式为:
enum 枚举名{
标识符(=整型常数),
标识符(=整型常数),
…
标识符(=整型常数)
} 枚举变量;
3.1 定义:
#include<stdio.h> //枚举类型1: enum Sex { MALE, FEMALE, SECRET }s1 = MALE; //声明时进行定义与初始化(全局) enum Sex s2 = FEMALE; //枚举类型2: enum Day { Mon, Tues, Wed, Thur, Fri, Sat, Sun }; int main() { enum Day s3 = Mon; //定义与初始化(局部) return 0; }
我们可以看到,枚举类型的声明、定义与初始化与结构十分类似。然后我们再来看一看枚举类型内部各成员的值,我们以日期为例:
#include<stdio.h> enum Day { Mon, Tues, Wed, Thur, Fri, Sat, Sun }; int main() { //打印各成员的值: printf("The value of Mon is %d\n", Mon); printf("The value of Tues is %d\n", Tues); printf("The value of Wed is %d\n", Wed); printf("The value of Thur is %d\n", Thur); printf("The value of Fri is %d\n", Fri); printf("The value of Sat is %d\n", Sat); printf("The value of Sun is %d\n", Sun); return 0; }
将上面这个示例编译运行起来看看结果的反馈:
我们看到,枚举类型内部各成员的默认值是从 0 开始依次递增的。
但是成员的值不仅限于默认值,同时也允许我们在定义时给各成员附合适的初值:
#include<stdio.h> enum Day { Mon, Tues=5, Wed, Thur, Fri, Sat=15, Sun }; int main() { printf("The value of Mon is %d\n", Mon); printf("The value of Tues is %d\n", Tues); printf("The value of Wed is %d\n", Wed); printf("The value of Thur is %d\n", Thur); printf("The value of Fri is %d\n", Fri); printf("The value of Sat is %d\n", Sat); printf("The value of Sun is %d\n", Sun); return 0; }
我们可以依照上面这种方式对枚举类型成员的初值进行修改:
我们看到,经过修改本应按序赋值为 1 的枚举成员 Tues 被赋值成了 5 ,于是接下来的成员就从 5 开始依次赋值,直到成员 Sat 被赋值为 15 后,接下来的成员就从 15 开始依次递增。
3.2 枚举类型的优点:
枚举类型的成员均为常量,不可在使用中被修改,那么我们同样可使用宏 #define 去定义常量,为什么非要使用枚举类型呢?
这是因为,相比于宏,枚举类型具有很多优点:
优点:
- 增加代码的可读性和可维护性。
- 和 #define 定义的标识符相比较,枚举有类型检查,更加严谨。
- 防止了命名污染(通过封装实现)。
- 便于调试。
- 使用方便,一次可以定义多个常量。
3.3 枚举类型的使用:
同时我们要注意,在使用枚举类型时只能用枚举常量给枚举变量赋值,只有这样才不会出现类型差异:
#include<stdio.h> //声明枚举类型 enum TEST { test1, test2, test3 }; //其中test1、test2、test3为枚举常量 int main() { //定义枚举变量: enum TEST t; //使用枚举常量给枚举变量赋值: t = test3; //验证赋值结果: printf("The value of t is %d\n", t); return 0; }
4.联合(共用体)
在进行某些算法的编程的时候,需要将几种不同类型的变量存放到同一段内存单元中。也就是使用覆盖技术使几个变量互相覆盖。这种几个不同的变量共同占用一段内存的结构,在C语言中,被称作 “ 联合体 ” 类型结构,简称联合,也叫共用体。
4.1 联合类型的定义:
联合是一种特殊的自定义类型,这种类型定义的变量也包含有一系列的成员,但不同的是这些成员共用同一块空间(也被称作共用体)
它的定义也基本与结构体一致:
#include<stdio.h> union TEST { char a; int b; }test; //在定义联合体的同时定义联合体变量test int main() { //查看联合体的占用空间: printf("The size of test is %d\n", sizeof(test)); //查看联合体成员的存储地址: printf("The address of test is %p\n", &test); printf("The address of a is %p\n", &test.a); printf("The address of b is %p\n", &test.b); return 0; }
但不同的是,我们编译运行后发现,联合体成员 char 类型变量 a 与 int 类型变量 b 共同占用同一片空间(一个 int 类型所占的空间):
这种方式定义的联合体结构,是三种结构中最节省空间
的一种,但同时,极致的空间节省能力导致了它在使用时需要满足的条件极为苛刻。
4.2 联合类型的特点:
联合体最大的特点就是,联合体的成员是共用同一块内存空间的,则联合至少得有足够的空间容纳最大的成员,这样一个联合变量的大小就至少得是最大成员的大小。既然联合体的大小会随着内部成员大小的变化而变化,那么是不是联合体类型也可以通过判断内容大小,来帮助我们判断机器的大小端存储模式
呢?
#define _CRT_SECURE_NO_WARNINGS 1 #include<stdio.h> int check_sys() { union CHECK { char check1; int check2; }check; check.check2 = 1; return check.check1; } int main() { if (1 == check_sys()) { printf("您的机器采用小端存储模式!\n"); } else { printf("您的机器采用大端存储模式!\n"); } return 0; }
我们将其编译运行发现,该思路可以帮助我们检查机器的大小端存储模式
4.3 联合类型大小的计算:
联合体类型的大小计算需要按照以下规则进行计算:
- 联合的大小至少是最大成员的大小。
- 当最大成员大小不是最大对齐数的整数倍时,要对齐到最大对齐数的整数倍。
#include<stdio.h> //联合体1: union TEST1 { char c[5]; int i; }; //联合体2: union TEST2 { short c[7]; int i; }; int main() { //检查联合体的大小: printf("The size of TEST1 is %d\n", sizeof(union TEST1)); printf("The size of TEST2 is %d\n", sizeof(union TEST2)); return 0; }
1.在联合体 TEST1 中,占用空间最大的成员是 char 类型数组 c ,且其中含有 5 个元素,则其所占空间大小为 5 个字节,而我们都知道 VS 的对齐数默认为 8 ,则将会对齐至默认对齐数的整数倍,即 8 个字节。
2.而联合体 TEST2 中,占用空间最大的成员是 short 类型数组 c ,且其中含有 7 个元素,则其所占空间的大小为 14 个字节,那么就将会对齐至对齐数的整数倍,即 16 个字节
5.总结:
今天我们对结构体的相关原理与使用等知识又有了新的了解,学习了结构体、位段、枚举、以及联合(共用体)的相关知识,完成了通过联合体类型判断机器的大小端存储模式,希望我的文章和讲解能对大家的学习提供一些帮助。
当然,本文仍有许多不足之处,欢迎各位小伙伴们随时私信交流、批评指正!我们下期见~