结构体中的位字段
有些信息在存储时并不需要占用一个完整的字节,有时只需要占用一个或者几个二进制位,为了节省存储空间并使得处理简便,C语言提供了一种数据结构,成为“位域”或者“位段”。
C与C++允许指定占用特定位数的结构成员,字段的类型应为整型或者枚举型 ,接下来是冒号:,然后后面跟一个数字,它指定了使用的位数,且可以使用没有名字的字段来提供间距。每个成员都被称为位字段(bit field)。例:
1 struct reg 2 { 3 unsigned int SN:4; 4 unsigned int :4; 5 bool good: 4; 6 };
union
union与结构体的存放顺序是所有成员都从地地址开始存放。
union占用的内存大小要根据union的成员中占用内存最大的一项,还要考虑对齐问题。
大小端问题
大端存储格式:数据的高字节存放在低地址中,低字节存放在高地址中。
小端存储格式:数据的高字节存放在高地址中,低字节存放在低地址中。
printf函数在输出时候,要输出的变量用栈来保存,最后输出的先入栈,最先要输出的变量最后入栈,因此,按顺序入栈之后,再按照要求的格式按顺序输出。
printf函数是最右侧的元素先入栈。若入栈元素为char,short等小于4字节的类型,入栈时也是占4个字节,故printf("%d%d",‘a‘,‘b‘);时才不会发生输出错误(若入栈不是4个字节,出栈时是输出4个字节,就会发生错误);大于4个字节时,如long long ,按实际字节数入栈。字符串参数入栈的是只想字符串的指针。
一个关键点是:char,short等类型入栈时,高位补0还是补1。
当数是无符号类型时(如unsigned short)高位总是补1,当数是有符号类型时,如short,高位补符号位。
大小端也会影响到位字段的存放。定义的数据结构中如果包含bit位域,将以以下规则存放:
1)低字节都存放在低地址;
2)大端模式首先为字段的高bit位分配空间,小端模式首先为字段的低bit位分配空间;
3)大端模式首先存放地址的高bit位,小端模式首先存放地址的低bit位。
枚举
C++的enum 工具提供了另一种创建符号常量的方式,这种方式可以代替const。使用enum的句法与使用结构体的相似。
枚举是一种用户自定义的类型,定义的基本格式为:
enum 枚举类型名 {枚举常量1[=整型常数],枚举常量2[=整型常数],枚举常量3[=整型常数].......} [变量名列表]
花括号中的内容成为枚举表,其中的每一项成为枚举常量,[]中的整型数值是给枚举常量赋初值,可以省略,如果不赋初值,编译器会自动为每一个枚举常量赋一个不同的整型值,当枚举表中某个常量赋值后,则其后的成员按依次加1 的规则赋初值。
sizeof
sizeof是一个单目运算符,不是函数,sizeof操作符以字节形式给出了其操作数在内存中的存储大小。操作数可以是一个表达式或者括在括号里内的类型名,且会忽略在括号内的各种运算。如sizeof(a++) 中的++ 不会执行。
sizeof("\0") = 2.
汉字在内存中所占的字节数跟系统有关,windows下一般是占2个字节,但是在linux环境下,若系统使用UTF-8编码,则一个汉字占3个字节。
当数组做形参时,数组名当做指针使用,此时,sizeof(数组名)=sizeof(指针);32位系统下为4 。
struct 的空间计算
struct 空间计算比较复杂,一般遵循两个原则:
1)整体空间是占用空间最大的成员(的类型)所占的字节数的整数倍,但是在32位linux+gcc环境下,若最大成员类型 所占的字节数超过了4,如double是8,则整体空间是4的整数倍即可。
2)数据对齐原则-----内存按结构体成员的先后顺序排列,当排列到该成员变量时,其前面已经摆放的空间大小必须是该成员类型大小的整数倍,如果不够则补齐,一次向后类推。但在linux+gcc环境下,若某个类型所占的字节数超过了4,如double为8,则前面已经摆放的空间大小是4的整数倍即可,不够则补齐。
当结构体中嵌套有其他结构体时,则上面两个原则要修改为以下两条:
1)整体空间是子结构体与父结构体中占用空间最大的成员(的类型)所占字节数的整数倍,但在linux+gcc环境下,若最大成员类型所占的字节数超过了4,如double是8,则整体空间是4的整数倍即可。
2) 数据对齐原则------父结构体内按照结构体成员的先后顺序排列,当排列到子结构体时,其前面已经排好的空间大小必须是子结构体成员中最大类型大小的整数倍,如果不够,则补齐,依次类推。但是在linux+gcc环境下,若某成员类型占据的字节数大于4,如double是8,则前面已经摆放的空间大小是4的整数倍即可,不够则补齐。
含数组的结构体的空间计算
结构体中,数组是按照单个变量一个一个进行摆放 的,而不是视为一个整体。
含位域结构体的空间计算
位域成员不能单独被sizeof取值,但是可以用sizeof获取含有位域的结构体大小。
使用位域的主要目的是压缩存储,大致规则为:
1)如果相邻位域字段的类型相同,且其位宽之和小于类型的sizeof大小,则后面的字段紧邻前一个字段存储,直到不能容纳为止。
vs2010环境下,一个inta:4,如果后面不是位域,则占用4个字节,而在Dev-c++以及linux+gcc环境下,不论位于为何种类型,所占字节数以其时间占用的字节数为准,以方便后续结构体成员的摆放,即int:4,如果后面不是位域,仅占1个字节。
2)如果相邻位域字段的类型相同,但其位宽之和大于类型的sizeof大小,则其后面的字段将从新的存储单元开始,其偏移量为其类型大小的整数倍。
3)如果相邻位域的字段类型不同,则各编译器的具体实现有差异,VC6采取不压缩,Dev-c++与linux+gcc采取压缩方式。
4)如果位域字段之间穿插着非位域字段,则不进行压缩。
5)整个结构体的总大小为最宽基本类型成员大小的整数倍。
使用"#pragma pack "时结构体空间的计算
#pragma pack(n) 编译器将按照n个字节对齐
#pragma pack() 取消自定义字节对齐方式。
注意,如果结构体成员中最大的成员类型大小都小于n,则n失效,即按照结构体中最大类型成员的大小进行对齐,反之,则以n对齐。
offsetof(item) = min( n , sizeof( item ) )
#pragma pack(push) 将当前pack设置压栈保存
#pragma pack(pop) 恢复先前的pack设置
空结构体的大小为 1
enum枚举的空间计算
enum只是定义了一个常量集合,里面没有“元素",而枚举类型是当做int类型存储的,故枚举类型的sizeof值都为4.
enum day {morning , moon , aftermoon} today;
sizeof (day) = 4 ,sizeof (today ) = 4;
string name;
name.size() 计算的是长度,不包含里面的‘\n‘;