1.联合体
联合体的语法形式和结构体差不多;
联合体的关键字是union,我们来对比一下两者都语法形式:
我们可以看见这两者似乎只有关键字上的差异;
那么我们来计算两者都内存关系是否相同;
关于默认对齐数,是根据编译器来确定的,vs的默认对齐数是8;
我们发现他们的成员类型都是一样的,但是内存占用大小却不一样,我们前面知道了结构体的内存计算方式,那联合体的内存又是怎样计算的呢?
我们说联合体又叫共用体,联合体的成员共用同一个空间;所以联合体的内存大小至少得存的下那个最大的那个成员的大小;
我们来分析一下上面联合体的内存分布;
按照我们上面说的,那么这个联合体的内存大小至少得是1个整型就能存的下整个联合体成员的数据,这2个成员共用同一块空间的化i只需要一块空间就可以存下它的数据;
当然我们也可以通过内存的地址来观察一下:
我们可以看到三个地址都是同一个起始位置,这就说明联合体的内存的确是上面那样分布的;
我们前面也特别说了,联合体的内存大小至少是成员里所占内存最大的大小;那就说明还不一定:联合体的内存大小得是最大对齐数的整数倍;(关于对齐数结构体那一章有讲过)
我们来结合实例:
union Un1 { char c[5]; //1 8 1 int i; //4 8 4 };
我们知道这个联合体的内存大小至少得是5个字节;
如果是结构体成员是数组的话,对齐数就是类型的大小;那么最大对齐数就是4;那么联合体的大小就得是4的整数倍,也就是8;
所以这个联合体的大小实际上是8;
union Un2 { short c[7];//14 //2 8 2 int i;//4 //4 8 4 };
这个联合体的内存大小至少是14个字节,对齐数最大为4,则内存大小至少得是4的整数倍,所以大小是16;
基于以上,我们知道联合体的成员是共用一块空间的,那么我们如果改了一个成员的数据是不是所有成员都变了呢?
union Un { char c; int i; }; union Un un = {0}; int main() { un.i=0x11223344; un.c=0x55; printf("%x\n",u.i); return 0; }
确实啊,当我们给成员c赋值后,成员i的值也发生了变化;我们可以来看图:
最开始i里面存的是0x11223344,因为是小端机器所以是倒着存的;
我们给从c赋值后,因为2跟成员共用一块空间,且c只占用一个字节,所以就把第一个字节的内容给改成55了;
所以在使用联合体时使用其中一个成员就不要使用另外一个成员;
我们再来看一个练习:
例子:判断一个机器是大端存储还是小端存储:
int main() { union m { char ch; int i; }k; k.i = 1; int j = k.ch; if (j) { puts("小端"); } else { puts("大端"); } return 0; }
i如果是小端存储的话,那么在内存中存储的就是01 00 00 00;反之则是00 00 00 01;
那么我们取出ch不就是取出i的第一个字节吗?
还有,如果要我们取出一个整型的每个字节呢?
int main() { union s { int i; struct m { char ch1; char ch2; char ch3; char ch4; }j; }l; l.i = 0x11223344; printf("%x %x %x %x\n", l.j.ch1, l.j.ch2, l.j.ch3, l.j.ch4); return 0; }
我们知道结构体中每个成员都是独立的空间,而联合体的成员共用一块空间;按照结构体的内存对齐规则结构体的大小是4个字节,而联合体的成员的最大内存为4字节;对齐数为4,则联合体的大小为4字节;
2.枚举
枚举很好理解,枚举的意思就是一一列举;
枚举的语法形式和结构体也一样;
至少关键字不一样,枚举的关键字是enum;
枚举可以将可能用到的值一一列举出来,这些枚举类型的成员就叫做枚举常量;
这些枚举常量是有默认值的,我们可以打印出来看一下;
如果我们赋初始值的话,第一个成员默认值是0,依次往后递增1;
那如果我们给第一个成员赋值了呢?
我们可以看到是从前一个成员的值依次往后地增1的;我们也可以每一个都给一个初始值;
但我们一定要注意,当我们在枚举内部给了初始值后,这个值是不能修改的;
创建的枚举变量的取值只能取枚举常量;
enum s { MAX=3, MIN=6 }m; int main() { m = MAX; printf("%d\n", MAX); printf("%d\n", MIN); return 0; }
枚举类型类似#define定义的标识符常量;
但是使用枚举定义常量的话会比#define定义常量好一点;
1.枚举定义的枚举常量会有严格的类型检查;
2.枚举可以一次性定义多个常量;
3.枚举是有作用域限制的,出了函数的作用域就失效了;
4,在调试程序的时候有更好的可读性;
5.增加代码的可读性和可维护性
再给枚举变量赋值的时候要给枚举常量的成员,不要给常量数字;
在c语言中对语法的检查会不那么严格,但c++会报错,3本身是一个int类型的,m是枚举类型,等号2边的类型不一样;