位段
什么是位段
位段的声明和结构是类似的
- 有两个不同:
- 位段的成员必须是 int、unsigned int 或signed int
- 位段的成员名后边有一个冒号和一个数字
- 示例:
struct A { int _a:2; int _b:5; int _c:10; int _d:30; };
位段的内存分配
说明:
- 位段的成员可以是 int unsigned int signed int 或者是 char (属于整形家族)类型
- 位段的空间上是按照需要以4个字节( int )或者1个字节( char )的方式来开辟的
- 位段涉及很多不确定因素,位段是不跨平台的,注重可移植的程序应该避免使用位段
示例:
struct S { char a:3; char b:4; char c:5; char d:4; }; struct S s = {0}; s.a = 10; s.b = 12; s.c = 3; s.d = 4;
- 对于上述空间是如何开辟的:
注:在vs平台下,其他平台不确定(可以自己进行测试)
- 首先对于char类型会开辟一个字节空间(8bite)
- 在这一个空间里先从高地址开始使用(从右边开始)
- 当存入的数据大于相应的空间(冒号后的数字表示属于该变量的空间大小,单位为bite)可以表示的数据大小时,会发生截断(从右边开始)
- 对于一个字节里还剩的空间,如果后面的位段空间能够放入时,将存入这一个字节里还剩的空间里
- 不够时将另开辟一个对应类型的空间来存放
位段的跨平台问题
存在问题:
- int 位段被当成有符号数还是无符号数是不确定的
- 位段中最大位的数目不能确定(例如:16位机器最大16,32位机器最大32,写成27,在16位机器会出问题
- 位段中的成员在内存中从左向右分配,还是从右向左分配标准尚未定义
- 当一个结构包含两个位段,第二个位段成员比较大,无法容纳于第一个位段剩余的位时,是舍弃剩余的位还是利用,这是不确定的
结论:
跟结构相比,位段可以达到同样的效果,但是可以很好的节省空间,但是有跨平台的问题存在
枚举enum
定义:
枚举型是一个集合(可以一一列举的类似元素),其元素(枚举成员)是一些命名的整型常量(元素之间用逗号隔开)
枚举类型的定义
示例:
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(可以人为设定枚举成员的值,从而自定义某个范围内的整数)
示例:
enum Color//颜色 { RED=1, GREEN=2, BLUE=4 };
枚举的优点
我们可以使用 #define 定义常量,为什么非要使用枚举?
- 示例:程序中为某些整数定义一个别名
预处理指令#define:
#define MON 1 #define TUE 2 #define WED 3 #define THU 4 #define FRI 5 #define SAT 6 #define SUN 7
枚举类型能完成同样的工作(更加简洁便捷):
enum DAY { MON=1, TUE, WED, THU, FRI, SAT, SUN };
总结枚举的优点:
- 增加代码的可读性和可维护性
- 和#define定义的标识符比较枚举有类型检查,更加严谨
- 防止了命名污染(封装)
- 便于调试
- 使用方便,一次可以定义多个常量
enum 与 #define 的区别
作用的时期:
#define是在预处理阶段直接进行替换,并且不进行类型检查,
枚举则是在程序运行之后才起作用
储存位置:
#define定义的类型存储在代码段
枚举常量存储在数据段的静态存储区里
赋值类型:
#define可以赋值多种类型数据
枚举变量的大小只能为整型数据(例如:0、1、2…)(enum当我们不主动对它进行赋值时,第一个枚举成员的默认值为整型的0,后续枚举成员的值在前一个成员上加1,#define则不会)
使用定义:
#define宏一次只能定义一个
枚举可以一次定义大量相关的常量
调试:
一般在编译器里,可以调试枚举常量,但是不能调试宏常量
定义类型:
枚举量具有类型,宏没有类型;枚举常量属于常量,宏定义不是常量
联合union
定义:
联合也称为共用体,很明显意思是多个变量共用一个空间,所以不能同一时间使用多个变量
联合类型的定义
示例:
//联合类型的声明 union Un { char c; int i; }; //联合变量的定义 union Un un;
联合的特点
- 联合变量的大小:
因为联合的成员是共用同一块内存空间的,所以联合变量至少是最大成员的大小(联合至少得有能力保存最大的那个成员)
- 示例:
//在上述代码的基础上 计算联合变量的大小 printf("%d\n", sizeof(un)); //输出结果:4
- 联合成员的空间使用:
任何成员变量都是从低地址开始使用
- 示例:
union Un { int i; char c; }; union Un un; // 下面输出的结果是一样的吗? printf("%p\n", &(un.i)); printf("%p\n", &(un.c)); //相同 都是从联合变量的低地址开始使用 //下面输出的结果是什么? un.i = 0x11223344; un.c = 0x55; printf("%x\n", un.i); //0x11223355
union和大小端
- 经典面试题:
判断当前计算机的大小端存储
#include<stdio.h> union var{ char c[4]; int i; }; int main(){ union var data; data.c[0] = 0x04;//因为是char类型,值对应ascii data.c[1] = 0x03;//16进制便于直接与内存中的值对比 data.c[2] = 0x02; data.c[3] = 0x01; //数组先使用低地址再使用高地址,内存内容依次为:04,03,02,11(共四字节) //而把四个字节作为一个整体,对于小端来说:低地址放在低权位 //读取出来则是:0x01020304 //反之则是大端存储模式 printf("%x\n",data.i);//共用空间 }
联合大小的计算
- 规则:
- 联合的大小至少是最大成员的大小
- 当最大成员大小不是最大对齐数的整数倍的时候,就要对齐到最大对齐数的整数倍
- 示例:
union Un1 { char c[5]; int i; }; union Un2 { short c[7]; int i; }; //下面输出的结果是什么? printf("%d\n", sizeof(union Un1)); //输出结果:8 printf("%d\n", sizeof(union Un2)); //输出结果:16