位段
位段与结构体类似,但有2个不同:
- 位段成员必须是int、unsigned int、或者 signed int,C99可以选择其它
- 位段的成员名之后是一个冒号和一个数字,数字代表其大小(以bit计)
举个例子:
struct A
{
int _a:2;
int _b:5;
int _c:10;
int _d:30;
};
以上就是一个位段的声明。
位段的内存分配
- 位段是以4个字节或者1个字节来开辟内存空间的;
- 位段使用涉及很多不确定因素,跨平台应避免使用位段
还是用例子来说明吧:
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为例):
a). 首先为a
开辟一个字节的空间(char
类型),但a
只占其中3个bit,假设初始化为0000 0000
b). b
占4个bit,那么接着a之后的4个bit是存放b的;
c). c
需要5个bit,第一个字节剩余部分无法容纳c
,那么c
会再开辟一个字节的空间,然后占用其中的5个bit;
d) d
是一样的,剩余部分不够了,因此会再开辟一个字节的空间,占用其中4个bit。
所以开辟的空间就是(假设初始化为0,地址由低到高):
0000 0000 0000 0000 0000 0000
之后赋值的时候:
10
写成2进制是1010
,但只有3个bit,因此截断,只存010
.这样,内存变成了:
00000 010 0000 0000 0000 0000
然后12
写成2进制是1100
,所以内存:
0 1100 010 0000 0000 0000 0000
然后是3
,转换成2进制是011
,占据5bit 就是00011
:
0 1100 010 000 00011 0000 0000
最后是4
,二进制是0100
,占据4个bit:
0 1100 010 000 00011 0000 0100
那么写成16进制:
0x 62 03 04
位段的跨平台问题
- int位段被当成有符号数还是无符号数是不确定的;
- 位段中最大位的数目不能确定(16位机器和32位机器);
- 位段成员在内存中是从左向右分配还是从右向左分配是不确定的;
- 当一个结构体包含位段,第二个位段成员比较大,无法容纳第一个位段剩余的位时,是舍弃还是利用,也是不确定的。
但是,位段可以节约空间。
位段的使用
TCP/IP协议中,IP数据的报文格式就适合使用位段。
位段的几个成员共有一个字节,这样有些成员的起始位置并不是字节的起始位置,那么它就是没有地址的。所以不能对位段的成员使用&操作符。赋值时,先放到一个变量中,然后再赋值。
举例:
struct A
{
int _a : 2;
int _b : 5;
int _c : 10;
int _d : 30;
};
int main()
{
struct A sa = {0};
scanf("%d", &sa._b);//这是错误的
//正确的示范
int b = 0;
scanf("%d", &b);
sa._b = b;
return 0;
}
联合体
像结构体一样,联合体也是由一个或者多个成员构成,这些成员可以是不同的类型。但编译器只为最大的成员分配内存。联合体的特点就是所有成员共用一块内存空间。所以联合体也叫共用体。给联合体一个成员赋值,其余成员的值也跟着变化。
联合体的特点
union UN
{
char c;
int i;
};
int main()
{
union UN un = {0};
printf("%p",&(un.i));
printf("%p",&(un.c));
printf("%p",&un);
return 0;
}
打印结果是一样的。
赋值会改变另一个成员的值:
int main()
{
union UN un = {0};
un.i = 0x11223344;
un.c = 0x55;
printf("%x",un.i);
return 0;
}
这里会打印0x11223355
。
联合体的大小
联合体的大小至少是最大成员的大小。
如果最大成员的大小不是最大对齐数的整数倍的时候,就需要对齐到最大对齐数的整数倍。
联合体判断大小端
union A
{
char a;
int b;
};
int check_sys()
{
union A test;
test.b = 1;
return test.a;
}
int main()
{
if (1 == check_sys())
printf("小端\n");
else
printf("大端\n");
return 0;
}
枚举
枚举就是一一列举。把可能的值一一列举出来。
举个例子:
enum Day
{
MON,
TUES,
WED,
THUR,
FRI,
SAT,
SUN
}
枚举内部可以赋值:
enum Color
{
RED = 2,
GREEN = 4,
BLUE =8
}
枚举的优点
- 增加代码的可读性;
- 枚举有类型检测,更加严谨;
- 便于调试,预处理阶段会删除
#define
; - 使用方便,一次可以定义多个常量;
- 枚举常量是遵循作用域规则的。