目录
1.联合体
1.1联合体类型的声明
像结构体一样,联合体也是由一个或者多个成员构成,这些成员可以是不同的类型。 但是编译器只为最大的成员分配足够的内存空间。联合体的特点是所有成员共用同一块内存空间。所以联合体也叫:共用体。 给联合体其中一个成员赋值,其他成员的值也跟着变化。
#include<stdio.h>
//联合类型的声明
union Un
{
char c;
int i;
};
int main()
{
union Un u = { 0 };
printf("%zd", sizeof(u));//计算变量的大小
return 0;
}
运行结果:
为什么结果是4呢?
1.2联合体的特点
联合体的成员是共用同一块内存空间的,这样一个联合变量的大小,至少是最大成员的大小。
#include<stdio.h> union Un //联合类型的声明 { char c; int i; }; int main() { union Un u = { 0 }; printf("%zd\n", sizeof(u));//计算联合体变量的大小 //下面的输出结果一样吗? printf("%p\n", &u); printf("%p\n", &(u.i)); printf("%p\n", &(u.c)); return 0; }
输出结果:
我们发现上面代码输出的地址一模一样。
代码2
#include<stdio.h> union Un { char c; int i; }; int main() { union Un u = { 0 }; u.i = 0x11223344; u.c = 0x55; printf("%x\n", u.i); return 0; }
输出结果:
我们发现将i的第4个字节的内容修改为55了
1.3相同成员的结构体和联合体对比
1.4联合体大小的计算
- 联合的大小至少是最大成员的大小。
- 当最大成员大小不是最大对齐数的整数倍的时候,就要对齐到最大对齐数的整数倍。
#include<stdio.h>
union Un
{
char c[5]; 5 //1 8 1 最大对齐数4
int i; 4 //4 8 4
};
int main()
{
union Un u = { 0 };
printf("%zd\n", sizeof(u));
return 0;
}
运行结果:
使用联合体是可以节省空间的。
举例: 比如,我们要搞一个活动,要上线一个礼品兑换单,礼品兑换单中有三种商品:图书、被子、衬衫。 每一种商品都有:库存量、价格、商品类型和商品类型相关的其他信息。
图书:书名、作者、页数
杯子:设计
衬衫:设计、可选颜色、可选尺寸。
那我们不耐心思考,直接写出以下结构:
struct gift_list { //公共属性 int stock_number;//库存量 double price; //定价 int item_type;//商品类型 //特殊属性 char title[20];//书名 char author[20];//作者 int num_pages;//⻚数 char design[30];//设计 int colors;//颜⾊ int sizes;//尺⼨ };
上述的结构其实设计的很简单,用起来也方便,但是结构的设计中包含了所有礼品的各种属性,这样使得结构体的大小就会偏大,比较浪费内存。因为对于礼品兑换单中的商品来说,只有部分属性信息是常用的。比如:
商品是图书,就不需要design、colors、sizes。 所以我们就可以把公共属性单独写出来,剩余属于各种商品本身的属性使用联合体,这样就可以节省内存空间,一定程度上节省了内存。
struct gift_list
{
int stock_number;//库存量
double price; //定价
int item_type;//商品类型
union {
struct
{
char title[20];//书名
char author[20];//作者
int num_pages;//⻚数
}book;
struct
{
char design[30];//设计
}mug;
struct
{
char design[30];//设计
int colors;//颜⾊
int sizes;//尺⼨
}shirt;
}item;
};
1.5联合体的练习
写一个程序,判断当前机器是大端?还是小端?
#include<stdio.h>
int check_cys()
{
int i = 1;
return(*(char*)&i == 1);
}
int main()
{
int ret = check_cys();
if (ret == 1)
{
printf("小端\n");
}
else
{
printf("大端\n");
}
return 0;
}
#include<stdio.h>
int check_sys()
{
union
{
int i;
char c;
}u;
u.i = 1;
return u.c; //返回1是⼩端,返回0是⼤端
}
int main()
{
int ret = check_sys();
if (ret == 1)
printf("小端\n");
else
printf("大端\n");
return 0;
}
2.枚举类型
2.1枚举类型的声明
枚举顾名思义就是一一列举,把可能的取值一一列举。
比如我们现实生活中:
一周的星期一到星期日是有限的7天,可以一一列举
性别有:男、女、保密,也可以一一列举
月份有12个月,也可以一一列举
三原色,也可以一一列举
这些数据的表示就可以使用枚举。
enum Day//星期 { Mon, Tues, Wed, Thur, Fri, Sat, Sun };
enum Sex//性别 { MALE, FEMALE, SECRET };
enum Color//颜⾊ { RED, GREEN, BLUE };
以上定义的enum Day, enum Sex, enum Color 都是枚举类型。
{}中的内容是枚举类型的可能取值,也叫枚举常量。
这些可能的取值都是有值的,默认从0开始,依次递增1,当然在声明枚举类型的时可以赋初值。
#include<stdio.h>
enum Color//颜⾊
{
RED,
GREEN,
BLUE
};
int main()
{
printf("%d\n", RED);
printf("%d\n", GREEN);
printf("%d\n", BLUE);
return 0;
}
运行结果:
enum Color//颜⾊
{
RED=2,
GREEN=4, //赋初值
BLUE=8
};
2.2枚举类型的优点
我们可以使用 #define 定义常量,为什么非要使用枚举?
枚举的优点:
- 增加代码的可读性和可维护性
- 和#define定义的标识符比较,枚举有类型检查,更加严谨。
- 便于调试,预处理阶段会删除 #define 定义的符号
- 使用方便,一次可以定义多个常量
- 枚举常量是遵循作用域规则的,枚举声明在函数内,只能在函数内使用
1.增加代码的可读性和可维护性
#include<stdio.h>
void menu()
{
printf("**************1.add 2.sub********");
printf("**************3.mul 4.div********");
printf("************** 0.exit ********");
}
int main()
{
void menu();
int input = 0;
printf("请选择:");
scanf("%d", &input);
switch (input)
{
case 1:
break;
case 2:
break;
case 3:
break;
case 4:
break;
case 0:
break;
default:
break;
}
return 0;
}
以上代码可改写为:
#include<stdio.h>
void menu()
{
printf("**************1.add 2.sub********");
printf("**************3.mul 4.div********");
printf("************** 0.exit ********");
}
enum Option
{
exit,//0
add, //1
sub, //2
mul, //3
div //4
};
int main()
{
void menu();
int input = 0;
printf("请选择:");
scanf("%d", &input);
switch (input)
{
case add:
break;
case div:
break;
case sub:
break;
case mul:
break;
case exit:
break;
default:
break;
}
return 0;
}
2.和#define定义的标识符比较,枚举有类型检查,更加严谨
是否可以拿整数给枚举变量赋值呢?在C语言中是可以的,但是在C++是不行的,C++的类型检查比较严格。
正确写法为:
2.3枚举类型的使用
#include<stdio.h>
enum Color
{
RED,
GREEN,
BLUE
};
int main()
{
enum Color col = RED; //使用枚举常量给枚举变量赋值
return 0;
}