1. 共用体
共用体:不同的成员使用共同的存储区域的数据 构造类型 称为共用体,简称共用,又称联合体。
共用体在定义、使用和说明上与结构体相似。本质不同在于内存的使用方式。
union在操作系统底层的代码中用的比较多。
1.1. 类型定义与变量定义
定义形式:
union 共用体名
{
成员列表;
};
union untest
{
char c;
short s;
int i;
}myun;
1.2. 内存分析
1.2.1. 大小
共用体占用空间的大小取决于类型长度最大的成员。
int main()
{
printf("sizeof(union untest) = %d\n", sizeof(union untest)); // 4
return 0;
}
1.2.2. 成员访问
共用体变量myun的三个成员:myun.c,myun.s和myun.i共用一块内存(4个字节的大小)。
myun.c使用第一个字节,myun.s使用前两个字节,myun.i使用全部4个字节,三成员享有同一个其实地址,而结构体中每个成员均有自己的地址
当给其中一个成员赋值时会影响到其他的成员,如myun.i = 0x11223344,赋之后myun.c变成了0x44,myun.s变成了0x3344
// 对于共用体而言,所有的成员,独有一个地址
typedef struct ms
{
char c;
int i;
short s;
} MyStruct;
typedef union mu
{
char c;
int i;
short s;
} MyUnion;
int main()
{
MyStruct ms;
MyUnion mu;
mu.i = 0x12345678;
printf("sizeof(MyStruct) = %lu\n", sizeof(MyStruct)); // sizeof(MyStruct) = 7
printf("sizeof(MyUnion) = %lu\n", sizeof(MyUnion)); // sizeof(MyUnion) = 4
printf("%p %p %p\n", &ms.c, &ms.i, &ms.s); // 0x16d57efd5 0x16d57efd6 0x16d57efda
printf("%p %p %p\n", &mu.c, &mu.i, &mu.s); // 0x16d57efd1 0x16d57efd1 0x16d57efd1
printf("%x %x %x\n", mu.c, mu.i, mu.s); // 78 12345678 5678
return 0;
}
1.2.3. 成员瞬时有效性
int main()
{
union mix m;
strcpy(m.name, "wyb");
printf("%s\n", m.name); // wyb
printf("%d\n", m.age); // 6453623
m.age = 20;
printf("%s\n", m.name); // 输出: 非预期值 (因为age的赋值覆盖了name的值)
printf("%d\n", m.age); // 20
return 0;
}
1.3. 共用体小结
- 共用体变量的地址和它的各成员的地址都是同一地址
- 同一个内存段可以用来存放几种不同类型的成员,但在每一瞬时只能存放其中一种
- 共用体变量中起作用的成员是最后一次存放的成员,在存入一个新的成员后原有成员就失去作用
- 共用体类型可以出现在结构体类型定义中,也可以定义共用体数组。同理,结构体也可以出现在共用体类型定义中,数组也可以作为共用体的成员。
1.4. 应用
当需要把不同类型的变量存放到同一段内存单元或对同一段内存单元的数据按不同类型处理则需要使用共用体结构数据。
1.4.1. 信息存储
设有若干个人员的数据,其中有学生和老师。学生的数据包括:姓名,编号,性别,职业,年级。老师的数据包括:姓名,编号,性别,职业,职务。可以看出,学生和老师所包含的数据是不同的。
要求设计程序输入人员信息然后输出。
如果把每个人都看作一个结构体变量的话,可以看出老师和学生的前4个成员变量是一样的,并且第五个成员变量可能是grade或position。
struct Staff {
char name[30];
char job;
union {
int grade;
char position[50];
} gOrp;
};
int main() {
struct Staff s[2];
for (int i = 0; i < 2; i++) {
printf("name: ");
scanf("%29s", s[i].name); // 限制输入长度,防止溢出
getchar(); // 吸收上一个输入的换行符
printf("job: ");
scanf(" %c", &s[i].job); // 读取单个字符
if (s[i].job == 't') {
printf("position: ");
scanf("%49s", s[i].gOrp.position); // 限制输入长度,防止溢出
} else {
printf("grade: ");
scanf("%d", &s[i].gOrp.grade); // 读取整数
}
}
for (int i = 0; i < 2; i++) {
printf("name: %s\n", s[i].name);
if (s[i].job == 't') {
printf("job: %c\n", s[i].job);
printf("position: %s\n", s[i].gOrp.position); // 输出职位
} else {
printf("job: %c\n", s[i].job);
printf("grade: %d\n", s[i].gOrp.grade); // 输出等级
}
}
return 0;
}
1.4.2. 写程序判断大小端序
大端模式:数据的低位保存在内存的高地址中;而数据的高位保存在内存的低地址中,数据从高位往低位放
小端模式:数据的低位保存在内存的低地址中;而数据的高位保存在内存的高地址中,高地址部分权值高低地址部分权值低
union
{
char ch;
int integer;
} un;
int main()
{
un.integer = 0x12345678;
if (un.ch == 0x12)
printf("big endian\n");
else
printf("little endian\n");
return 0;
}
2. 枚举
枚举类型定义了一组整形常量的集合,目的是提高程序的可读性。他的语法跟结构体相同。
2.1. 枚举类型定义
语法格式:
enum 枚举类型
{
常量列表
};
定义
enum DAY
{
MON=1, THU, WED, FRI, SAT, SUN // 提供一组可选的常量
};
- 枚举型是一个集合,集合中的元素(枚举成员)是一些命名的整形常量,元素之间用逗号隔开
- 第一个枚举成员的默认值为整数的0,后续枚举成员的值在前一个成员上加1
- 可以人为设定枚举成员的值,从而自定义某个范围内的整数
- 枚举型是预处理指令#define的替代
#define MON 1
#define TUE 2
#define WED 3
#define THU 4
#define FRI 5
#define SAT 6
#define SUM 7
类似于如上的宏定义,通常用enum来统一管理
2.2. 枚举变量与初始化
int main()
{
printf("MON = %d FRI = %d\n", MON, FRI);
enum DAY day;
printf("请输入星期:");
scanf("%d", &day);
switch (day)
{
case MON:
case TUE:
case WED:
case THU:
case FRI:
printf("work day");
break;
case SAT:
case SUN:
printf("holiday");
break;
}
return 0;
}
enum DAY可以定义枚举变量,即可以取枚举过命名值,也可赋予其他整形,c++中则不可以,只能取枚举过的命名值
2.3. 枚举常量
枚举常量,在使用时,应用更广泛,提高程序的可读性。常用于出错处理的枚举
2.4. 应用
2.4.1. 枚举常量在Qt中的应用
判断键盘的按键值和绘制图形使用的画笔颜色
Qt::Key_A;
Qt::black;
还有常用的,返回正确与错误的枚举常量
typedef enum _BOOL
{
False,
True
} BOOL;
enum ret
{
SUCCESS, FAILURE
}
int main()
{
BOOL b;
b = True;
return SUCCESS;
}
2.4.2. 判断日期
判断一年中第125天是工作日还是休息日,假设一年365天,新年第一天是星期一
int main()
{
unsigned int today;
printf("pls input today num: ");
scanf("%d", &today);
today = today % 7;
enum DAY day = today;
switch(day) ......
}
2.4.3. 增加bool类型
c中没有bool类型,通过enum类型来枚举
typedef enum BOOLEAN
{
false,
true
}Bool;
Bool a = true;