前言 :
文章涉及到之前讲过的一些概念,不了解的读者可以去看一下
一.位段
(1).位段介绍
位段的声明和结构类似,但它的成员是一个或多个位的字段。这些不同长度的字段实际上存储在一个或多个整形(字符)变量中。
位段的声明和任何普通的结构成员声明相同,但有两个例外
(1). 位段的成员必须是 int、unsigned int 或signed int 或 char 或 unsigned char类型
(2). 位段的成员名后边有一个冒号和一个整数,这个整数指定该位段所占用位的数目.
// 一个例子
struct A
{
unsigned int a:2;
unsigned int b:5;
unsigned int c:10;
unsigned int d:30;
};
(2).位段内存分配及空间开辟
#include<stdio.h>
int main()
{
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;
printf("%d\n", s);
printf("%d\n", sizeof(s));
}
位段的空间上是按照需要以4个字节( int )或者1个字节( char )的方式来开辟的
在vs2019的环境下 :
(1).内存分配时是从右向左进行分配的。
(2).当下一个位段无法容纳于上一个位段的剩余的位时,会再开辟一个空间
(3).位段的可移植性问题
(1). int 位段被当成有符号数还是无符号数是不确定的。
(2). 位段中最大位的数目不能确定。(16位机器整形变量16位,32位机器32位,写成27,在16位机器会出问题)
(3). 位段中的成员在内存中从左向右分配,还是从右向左分配标准尚未定义。
(4).当一个结构包含两个位段,第二个位段成员比较大,无法容纳于第一个位段剩余的位时,是舍弃剩余的位 还是利用,这是不确定的。
总结: 跟结构相比,位段可以达到同样的效果,但是可以很好的节省空间,但是有跨平台的问题存在。
(4).位段相关练习题
(1).
#define MAX_SIZE A+B
struct _Record_Struct
{
unsigned char Env_Alarm_ID : 4;
unsigned char Para1 : 2;
unsigned char state;
unsigned char avail : 1;
}*Env_Alarm_Record;
struct _Record_Struct *pointer = (struct _Record_Struct*)malloc
(sizeof(struct _Record_Struct) * MAX_SIZE);
// 当A=2, B=3时,pointer分配( )个字节的空间。
首先开辟一个字节的空间,给 Env_Alarm_ID分配4位,Para1分配2位,剩下2位不足以容纳 state,再开辟一个字节空间,再开辟一个空间,分配给 avail 1位
因此 sizeof(struct _Record_Struct)的值为 3 ,结果为 3 * 2 + 3 为 9
(2).
#include<stdio.h>
int main()
{
unsigned char puc[4];
struct tagPIM
{
unsigned char ucPim1;
unsigned char ucData0 : 1;
unsigned char ucData1 : 2;
unsigned char ucData2 : 3;
}*pstPimData;
pstPimData = (struct tagPIM*)puc;
memset(puc,0,4);
pstPimData->ucPim1 = 2;
pstPimData->ucData0 = 3;
pstPimData->ucData1 = 4;
pstPimData->ucData2 = 5;
printf("%02x %02x %02x %02x\n",puc[0], puc[1], puc[2], puc[3]);
return 0;
}
打印结果为 02 29 00 00
二.枚举
枚举顾名思义就是一一列举,把可能的取值一一列举。
enum Day
{
Mon,
Tues,
Wed,
Thur,
Fri,
Sat,
Sun
};
enum Sex
{
MALE,
FEMALE
};
enum Color
{
RED=1,
GREEN=2,
BLUE=4
};
以上定义的 enum Day , enum Sex , enum Color 都是枚举类型。 {}中的内容是枚举类型的可能取值,也叫枚举常量。
这些可能取值都是有值的,默认从0开始,一次递增1,当然在定义的时候也可以赋初值。
三.联合体
(1).联合体介绍
联合也是一种特殊的自定义类型, 这种类型定义的变量也包含一系列的成员,特征是这些成员公用同一块空间(所以 联合也叫共用体)。
//联合类型的声明
union Un
{
char c;
int i;
};
//联合变量的定义
union Un un;
union Un
{
int i;
char c;
};
int main()
{
union Un un;
// 下面输出的结果是一样的吗?
printf("%p\n", &(un.i));
printf("%p\n", &(un.c));
//下面输出的结果是什么?
un.i = 0x11223344;
un.c = 0x55;
printf("%x\n", un.i);
}
因为联合体的成员公用一块空间,所以地址相同,改变 un.c 也在改变 un.i ,根据小端存储模式可知,打印结果为 11223355
在前面讲解数据在内存中的存储时,已经讲解过一种判断大小端的方法,今天我们还可以使用联合体的方式去判断
#include<stdio.h>
union Un
{
int i;
char c;
};
int main()
{
union Un un;
un.i = 1;
if (un.c == 1)
{
printf("小端\n");
}
else
{
printf("大端\n");
}
}
(2).联合体的计算
(1). 联合体的成员是共用同一块内存空间的,这样一个联合变量的大小,至少是最大成员的大小(因为联合至少得有 能力保存最大的那个成员)
(2).当最大成员大小不是最大对齐数的整数倍的时候,就要对齐到最大对齐数的整数倍。
union Un1
{
char c[5]; // 对齐数为 1 5个字节
int i; // 对齐数为 4 4个字节
};
union Un2
{
short c[7]; // 对齐数为 2 14个字节
int i; // 对齐数为 4 4个字节
};
//下面输出的结果是什么?
printf("%d\n", sizeof(union Un1));
printf("%d\n", sizeof(union Un2));
Un1最大成员的大小为 5 个字节,不是最大对齐数 4 的整数倍,因此应为 8 个字节
Un2最大成员的大小为 14 个字节,不是最大对齐数 4 的整数倍,因此应为 16 个字节
// 输出结果是什么?
#include<stdio.h>
int main()
{
union
{
short k;
char i[2];
}*s, a;
s = &a;
s->i[0] = 0x39;
s->i[1] = 0x38;
printf(“%x\n”,a.k);
return 0;
}
改变 char 数组的值就是在改变 k 的值,因此 k 的值以 %x 的形式打印出来为 3839