3.2练习
#include<stdio.h> struct abc { char a; int b; char c; }; int main() { printf("%d", sizeof(struct abc)); }
第一步,将char a放进首地址即偏移量为0所在的空间,占了1,下一个空间的偏移量为1,因int型的对齐数为4,故对齐到偏移量为4时存储,存放4个字节,4,5,6,7的空间被占据,故最后char c会存放在偏移量为8的位置,空间大小好像是9个字节,但别忽略了第三条,结构体总大小为最大对齐数的整数倍,这里的最大对齐数,显然是4,故结构体大小应为12
最后来一道复杂点的
#include<stdio.h> struct abc { char a; int b; char c; }; struct xyz { char x; float y; struct abc xyz; }; int main() { printf("%d", sizeof(struct xyz)); }
首先将char x放进偏移量为0所在的空间,y的对齐数为4,故从偏移量为4的位置存储,偏移量为4,5,6,7的空间被float y使用,接着是结构体abc类型的存储,根据第四条,结构体在存储之前也得先对齐,观察abc,发现abc的最大对齐数为4,刚好我们此时的位置就是偏移量为8的位置,是4的倍数,故直接存储即可。先将char a存放进去,偏移量为8的空间被占据,再存放int b,此时,到了偏移量为9的空间,先对齐到偏移量为4的倍数的位置,即12,故12,13,14,15的空间被int b占据,偏移量为16的位置被char c占据,结构体xyz的空间似乎已经确实,为17,但根据第四条规则,结构体的大小,为所有最 大对齐数的整数倍,即4的倍数,为20
4.拓展:结构体实现的位段操作
4.1什么是位段
位段的声明和结构是类似的,有两个不同:
1.位段的成员必须是 int、unsigned int 或signed int(其实char也可以,因为字符型在内存中的存储使用的是ASCII码值的形式,可以这样理解,一个一个的字符,为一个一个的数)
2.位段的成员名后边有一个冒号和一个数字。
#include<stdio.h> struct abc { int a : 3; //冒号后面的数字代表的二进制位的数量,这里可看作用3个二进制位存储整型a int b : 20; int c : 20; }; int main() { printf("%d", sizeof(struct abc)); }
试想一下,这里打印出来的结果是多少,存储int a我们用了3个二进制位,存储int b用了20个二进制位,存储int c也用了20个二进制位,那么一共用了43个二进制位(bit位),而一个字节为8个bit位,那么是不是就用了6个字节来存储这个结构体呢?
在vs上,a用了3个二进制存储,b用了20个二进制位存储,此时还没达到32bit位,但是当把c用20个二进制位存储的时候就会发现,超出一个整型的大小了,vs采用的方式便是再开辟一个整型的空间给你存放这个c,之前剩下的就不要了。但由于c语言对于位段粗糙的定义,导致在不同的编译器有不同的实现,有的编译器秉承着不浪费的原则,先用完之前剩下的空间再开辟,所以位段的使用尽量不要跨平台。要注意的一点:先位段的空间上是按照需要以4个字节( int )或者1个字节( char )的方式来开辟。在vs上只有char时按1个字节开辟,当既有char又有int或者只有int的时候按4个字节开辟。
4.2位段有什么用?
聪明的小伙伴恐怕早就想到了,3这个数字,我用2个bit位就能够存放,但是我创建了一个整型,我就用了32个bit位去存放,因此,位段可以很好的节省空间。
4.3.位段的跨平台问题
(1)int 位段被当成有符号数还是无符号数是不确定的。
(2) 位段中最大位的数目不能确定。(16位机器最大16,32位机器最大32,写成27,在16位机
器会出问题。
(3) 位段中的成员在内存中从左向右分配,还是从右向左分配标准尚未定义。
(4) 当一个结构包含两个位段,第二个位段成员比较大,无法容纳于第一个位段剩余的位时,是
舍弃剩余的位还是利用,这是不确定的。
二、枚举类型
1.枚举类型的定义
#include<stdio.h> enum sex { male, female, no };//这里就定义了一个关于性别的枚举类型,它会按照从0开始的顺序给male,female,no赋值 int main() { int arr1[no] = {9,7}; //值得再次强调的一点,它定义出来的是常量(而非常变量),可以用在数组中 printf("%d %d %d", male, female, no); }
当然你也可以自己给它们赋值,不用根据默认的来
#include<stdio.h> enum sex { male=3, female, no=99 };//这里就定义了一个关于性别的枚举类型,它会按照从0开始的顺序给male,female,no赋值 int main() { int arr1[no] = {9,7}; //值得再次强调的一点,它定义出来的是常量(而非常变量),可以用在数组中 printf("%d %d %d", male, female, no); }
2.枚举的优点
1. 增加代码的可读性和可维护性
2. 和#define定义的标识符比较枚举有类型检查,更加严谨。
3. 防止了命名污染(封装)
4. 便于调试
5. 使用方便,一次可以定义多个常量
三、联合体类型
1.联合体类型的定义
这种类型定义的变量也包含一系列的成员,特征是这些成员公用同一块空间(所以联合也叫共用体)。
union abc { int a; int b; char c; }; int main() { union abc x; x.a = 20; x.b = 10; x.c = 'a'; printf("%d %d %c\n",x.a,x.b,x.c); printf("%d\n", x.a); printf("%d\n", x.b); }
由此可知,改变联合体上的一个数就会导致牵一发而动全身的效果
2.联合体类型的特点
(1)牵一发而动全身
(2)所有的数据都从同一个地址开始存储
union abc { int a; int b; char c; }; int main() { union abc x; x.a = 20; x.b = 10; x.c = 'a'; printf("%p %p %p\n", &(x.a),&(x.b),&(x.c)); }
今天的分享到这里就结束了,感谢各位友友的来访,祝各位友友前程似锦O(∩_∩)O