位段的介绍,存储方式,计算大小
枚举的介绍,使用举例
联合的介绍,存储方式,大小计算
一、位段
位段:度娘给出的解释是,C语言允许在一个结构体中以位为单位来指定其成员所占内存长度,这种以位为单位的成员称为“位段”或称“位域”( bit field) 。利用位段能够用较少的位数存储数据。
位段的声明和结构体很相似,但不同的是:
①位段的成员可以是int,unsigned int,signed int或者是char(属于整形家
族)类型
②位段的成员名后边有一个冒号和一个数字,这个数字表示位段所占的二进制位数。
如:
struct A
{
int _a:3;
int _b:4;
int _c:5;
int _d:6;
}a;
那么位段A多大呢?在计算之前,先说下位段怎么存储
简单说就是看变量后面的数字。先看第一个变量类型,开辟对应类型大小的字节,然后数字是多少就占多少bit,剩余bit留给下一个变量用。不够的话再开辟对应类型大小字节数。
细说的话,对于位段结构,编译器会自动进行存储空间的优化,要遵循这些原则原则:
1)如果一个位段存储单元能够存储得下位段结构中的所有成员,那么位段结构中的所有成员只能放在一个位段存储单元中,不能放在两个位段存储单元中;如果一个位段存储单元不能容纳下位段结构中的所有成员,那么从剩余的位段从下一个位段存储单元开始存放。(在VC中位段存储单元的大小是4字节).
2)如果一个位段结构中只有一个占有0位的无名位段,则只占1或0字节的空间(C语言中是占0字节,而C++中占1字节);否则其他任何情况下,一个位段结构所占的空间至少是一个位段存储单元的大小;
再看上面的例子,int类型,开辟四字节大小的空间,对于_a,_b,_c,_d,分别依次按顺序占3,4,5,6bit,最后一共占了18bit,但开辟了4字节,因此用sizeof对该结构体求大小的话是4字节。
使用位段需注意以下几点:
1)位段的类型只能是int,unsigned int,signed int三种类型,不能是char型或者浮点型;
2)位段占的二进制位数不能超过该基本类型所能表示的最大位数,比如在VC中int是占4个字节,那么最多只能是32位;
3)无名位段不能被访问,但是会占据空间;
4)不能对位段进行取地址操作;
5)若位段占的二进制位数为0,则这个位段必须是无名位段,下一个位段从下一个位段存储单元(这里的位段存储单元经测试在VC环境下是4个字节)开始存放;
6)若位段出现在表达式中,则会自动进行整型升级,自动转换为int型或者unsigned int。
7)对位段赋值时,最好不要超过位段所能表示的最大范围,否则可能会造成意想不到的结果。
8)位段不能出现数组的形式。
二、枚举
就是一一列举出相类似的东西,如星期,班级,商品等。
enum Day // 星期
{ Mon,
Tues,
Wed,
Thur,
Fri,
Sat,
Sun
}D1,D2;
enum Class//班级
{
c1,
c2,
c3,
c4
}C1,C2;
enum Day, enum Class都是枚举类型,{}中的是枚举常量,不是变量,不能在程序中用赋值语句再对它赋值。枚举常量它们如果没有初始化的话,从第一个默认是0,后面的依次加1。如果初始化了的话,被初始化的枚举常量之前的不变,其他的根据枚举常量的值依次加1。{}后面的D,C类似结构体,是枚举变量。
需要注意的是,可以把枚举常量赋给变量,但不能把枚举常量的数值赋给枚举变量。如D1=Mon,D2=Tues;是正确的,而D1=0,D2=1;是错误的。如果非要把数值赋给枚举变量的话需要通过强制类型转换,D1=(enum Day)0;
#include
int main()
{
int i, j;
int mon[32];
enum Day // 星期
{
Mon = 1,
Tues,
Wed,
Thur,
Fri,
Sat,
Sun
};
j = Mon;
for (i = 1; i < 32; i++)
{
mon[i] = j;
j++;
if (j > Sun)
{
j = Mon;
}
}
for (i = 1; i < 32; i++)//打印31天内的周一到周日
{
switch (mon[i])
{
case 1: printf(" %2d", mon[i]); break;
case 2: printf(" %2d", mon[i]); break;
case 3: printf(" %2d", mon[i]); break;
case 4: printf(" %2d", mon[i]); break;
case 5: printf(" %2d", mon[i]); break;
case 6: printf(" %2d", mon[i]); break;
case 7: printf(" %2d", mon[i]); break;
default:break;
}
if (0 == i % 7)
{
printf("\n");
}
}
printf("\n");
}
三、联合
联合是一种特殊的自定义类型,这种类型定义的变量包含一系列的成员,特征是这些成员公用同一块空间(所以联合也叫共用体)。
union Un
{
int a;
char b;
short c;
};
因为联合的成员是共用同一块内存空间的,所以一个联合变量的大小,至少是最大成员的大小(因为联合至少得有能力保存最大的那个成员)。
当最大成员大小不是最大对齐数的整数倍的时候,就要对齐到最大对齐数的整数倍。
int main()
{
union Un
{
int i;
char c;
};
union Un un;
//因为两个成员占用相同内存,所以地址相同
printf("%p\n", &(un.i));
printf("%p\n", &(un.c));
un.i = 0x11111111;
//因为char类型是1字节,16进制表示,char类型只能用2个数字,如果多于2个数字会发生类型截断
un.c = 0x22;
//输出286331170,因为共用一块内存且小端存储,所以本应0x11111111变为0x11111122,十进制表示286331170
printf("%d\n", un.i);
}
根据上个程序可知,联合可以判断当前计算机的大小端存储。如果计算机是大端存储,则0x11111111会变为0x22111111,输出结果是571543825。
自己仿照上例写个简单的
int main()
{
union Un
{
int a;
char b;
}u;
u.a = 0x11;
u.b = 0x01;
//小端存储,结果是1;如果是大端存储则是16
printf("%d\n", u.a);
}
计算联合结构的大小
int main()
{
union Un1
{
char a[5];
int b;
};
union Un2
{
short a[7];
int b;
};
//下面输出的结果是什么?
printf("%d\n", sizeof(union Un1));//8
printf("%d\n", sizeof(union Un2));//16
}
再算一题
int main()
{
union ip_addr
{
unsigned long addr;
struct
{
unsigned char c1;
unsigned char c2;
unsigned char c3;
unsigned char c4;
}ip;
};
union ip_addr my_ip;
// 176238749转换为16进制0xA 81 30 9D,因为是联合结构
//add和结构体变量ip中的成员共用一块内存,则c1=9D,c2=30,c3=81,c4=A
//十进制表示为10 129 48 157
my_ip.addr = 176238749;
printf("%d.%d.%d.%d\n", my_ip.ip.c4, my_ip.ip.c3, my_ip.ip.c2, my_ip.ip.c1);//10.129.48.157
}
最后,还是那句,欢迎大家在评论区进行提问,提建议给我,谢谢