『C语言进阶』自定义类型详解(一):https://developer.aliyun.com/article/1392700
二、位段
2.1 什么是位段
- 位段的成员必须是int、unsigned int或者signed int(还可以是char类型)
- 位段的成员后边有一个冒号和一个数字
#include<stdio.h> struct A { int a : 2;//a需要2个比特位 int b : 5;//b需要5个比特位 int c : 10;//c需要10个比特位 int d : 28;//d需要28个比特位 };//这就是一个位段 int main() { printf("%d\n", sizeof(struct A)); return 0; }
运行结果:
8
首先因为a是int类型,所以开辟了4个字节(32个比特位)的空间,a需要2个比特位,还剩30个比特位,刚好b需要5个比特位,就给b,还剩下25个比特位,刚好c需要10个比特位,给c后还剩15个比特位,而d需要28个比特位,还剩下的15个比特位不够,d就只能再开辟一个int类型空间即四个字节,所以一共是8个字节。
这时候相信铁汁们有个这样的疑问?:
那个还剩下来的15个比特位怎么用的?
#include<stdio.h> struct S { char a : 3; char b : 4; char c : 5; char d : 4; }; int main() { printf("%d\n", sizeof(struct S)); return 0; }
运行结果:
3
所以在这里我们可以猜测VS2022,上一次开辟的空间剩下的不够时,是被遗弃了,并没有再次使用,直接用新开的空间。
2.2 位段的内存分配
- 位段的成员可以是 int 、unsigned int、signed int 或者是 char (属于整形家族)类型
- 位段的空间上是按照需要以4个字节( int )或者1个字节( char )的方式来开辟的。
- 位段涉及很多不确定因素,位段是不跨平台的,注重可移植的程序应该避免使用位段。
struct S { char a : 3; char b : 4; char c : 5; char d : 4; }; int main() { struct S s = { 0 }; s.a = 10; s.b = 12; s.c = 3; s.d = 4; return 0; }
2.3 位段的跨平台问题
- int 位段被当成有符号数还是无符号数是不确定的。
- 位段中最大位的数目不能确定。(16位机器最大16,32位机器最大32,写成27,在16位机器会出问题。
- 位段中的成员在内存中从左向右分配,还是从右向左分配标准尚未定义。
- 当一个结构包含两个位段,第二个位段成员比较大,无法容纳于第一个位段剩余的位时,是舍弃剩余的位还是利用,这是不确定的。
总结:
跟结构相比,位段可以达到同样的效果,并且可以很好的节省空间,但是有跨平台的问题存在。
三、枚举
枚举顾名思义就是列举,把可能的取值列举。(枚举是一个常量)
例如:
一周是从周一到周日,是有限的,可以一一列举
三原色,也可以一一列举
3.1 枚举类型的定义
#include<stdio.h> enum day//星期 { Mon, Tues, Wed, Thir, Fri, Sta, Sun }; enum Sex//性别 { MALE, FEMALE = 3, SECRET }; enum Color//颜色 { RED, GREEN = 89, BLUE = 78 }; int main() { enum Day a = Sun; printf("%d\n", Mon); printf("%d\n", Tues); printf("%d\n", Wed); printf("\n%d\n", MALE); printf("%d\n", FEMALE); printf("%d\n", SECRET); printf("\n%d\n", RED); printf("%d\n", GREEN); printf("%d\n", BLUE); return 0; }
运行结果:
0 1 2 0 3 4 0 89 78
以上定义的enum Day,enum Sex,enum Color都是枚举类型,{}中 的内容都是枚举类型的可能取值,也叫枚举常量
这些可能取值都是有值的,默认从0开始,依次加1,也可以在定义时赋值初值
3.2 的使用
enum Color { RED=1, GREEN=2, BLUE=4 }; enum Color clr = GREEN;//只能拿枚举常量给枚举变量赋值
注意:
- 枚举是一个常量,在定义枚举的时候(无论有没有赋值),不可以对可能取值的值进行改变,例如:Mon=3;(如果想让值发生改变,只能在定义枚举的时候进行赋值改变)
3.3 枚举的优点
- 增加代码的可读性和可维护性
- 和#define定义的标识符比较枚举有类型检查,更加严谨。
- 便于调试
- 防止命名污染(封装)
- 使用方便,一次可以定义多个常量
四、联合(共同体)
4.1 联合体的定义
联合是一种特殊的自定义类型,这种类型定义的变量也包含一系列的成员,特征是这些成员功用一块空间(所以联合也叫共用体)
#include<stdio.h> union Un { char c; int i; }; int main() { union Un u; printf("%d\n", sizeof(u)); printf("%p\n", &u); printf("%p\n", &(u.c)); printf("%p\n", &(u.i)); return 0; }
运行结果:
4 000000406BD4F784 000000406BD4F784 000000406BD4F784
起始地址是一样的,共用一块地址,所以是4个字节
4.2 联合体的特点
联合的成员是共用同一块内存空间的,这样一个联合变量的大小,至少是最大成员的大小(因为联 合至少得有能力保存最大的那个成员)。
习题:判断当前计算机的大小端存储:
低位放在低地址的是小端,地位放在高地址的是大端
常规写法:
#include <stdio.h> int cheak_sys() { int a = 1;//00 00 00 01(16进制) return *((char*)&a); } int main() { int ret = 0; ret = cheak_sys(); if (ret == 1) printf("小端\n"); else printf("大端\n"); return 0; }
用联合的方法写:
#include <stdio.h> int cheak_sys() { union Un { char c; int i; }u; u.i = 1; return u.c; } int main() { int ret = 0; ret = cheak_sys(); if (ret == 1) printf("小端\n"); else printf("大端\n"); return 0; }
运行结果:
小端
4.3 联合体大小的计算
联合的大小至少是最大成员的大小。
当最大成员大小不是最大对齐数的整数倍的时候,就要对齐到最大对齐数的整数倍。
(和结构体一样)但是注意起始地址是一样的
#include <stdio.h> union Un1 { char c[5]; int i; }; union Un2 { short c[7]; int i; }; int main() { printf("%d\n", sizeof(union Un1));//8 printf("%d\n", sizeof(union Un2));//16 return 0; }
本次的内容到这里就结束啦。希望大家阅读完可以有所收获,同时也感谢各位铁汁们的支持。文章有任何问题可以在评论区留言,小羊一定认真修改,写出更好的文章~~