1.结构体对齐规则:
(1)第一个成员在与结构体变量偏移量为0的地址处。
(2)其他成员变量要对齐到对齐数的整数倍的地址处。
对齐数=编译器默认对齐数与该成员大小的较小值。
(3)结构体总大小为最大对齐数的整数倍
(4)如果嵌套了结构体的情况,嵌套的结构体对齐到自己最大对齐数的整数倍处,
结构体的整体大小就是所有最大对齐数(含嵌套结构体的对齐数)的整数倍。
(5)visual studio 中的默认对齐数=8,可通过#pragma pack(x)修改默认对齐数的大小,
也可通过#pragma pack()取消对默认对齐数的修改,若想取消默认对齐数可写成#pragme pack(1)。
(6)Eg:
#pragma pack(4)
int main(int argc, char* argv[])
{
struct tagTest1
{
short a;
char d;
long b;
long c;
};
struct tagTest2
{
long b;
short c;
char d;
long a;
};
struct tagTest3
{
short c;
long b;
char d;
long a;
};
struct tagTest1 stT1;
struct tagTest2 stT2;
struct tagTest3 stT3;
printf("%d %d %d", sizeof(stT1), sizeof(stT2), sizeof(stT3));//12 12 16
return 0;
}
#pragma pack()
2.为什么会存在内存对齐:
(1)平台原因(移植原因):不是所有的硬件平台都能访问任意数据的,有些硬件平台只能在某些地址处取某些特定类型的数据,否者抛出硬件异常。
(2)性能原因:数据结构(尤其是栈)应该尽可能地在自然边界上对齐,原因在于,为了访问未对齐的内存,处理器需要作两次内存访问,而对齐的内存访问仅需要一次访问。
(3)总体来说:结构体内存对齐是拿空间来换取时间。
(4)在设计结构体时,尽量让占空间小的集中在一起,这样既可以满足对齐,又可以节省空间
3.位段
(1)位段的成员必须是int ,unsigned int ,或singed int。
(2)位段的成员名后面有一个冒号和一个数字,例:
int lin : 1 ;
表示:lin这个成员只占1个bit位
(3)位段的跨平台问题:int位段被当成有符号还是无符号是不确定的,
位段中最大位数目在16位机器中最大是16,32位机器最大是32,
位段中的成员在内存中从左向右分配,还是从右向左分配尚未定义,、
当一个结构体包含两个位段,第二个位段成员比较大时,无法容纳于第一个位段剩余的位时,
是舍弃剩余位置还是利用,还不确定
(4)总结:跟结构体相比较,位段可以达到相同的效果,可以很好的节省空间,却存在跨平台的问题。
(5)Eg:
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 %02xn",puc[0], puc[1], puc[2], puc[3]);//02 29 00 00
return 0;
}
注释:puc是一个char数组,每次跳转一个字节,结构体不是,它只有第一个元素单独享用一字节,其他三个元素一起共用一字节,所以puc被结构体填充后,本身只有两个字节会被写入,后两个字节肯定是0,然后第一个字节是2就是2了,第二个字节比较麻烦,首先ucData0给了3其实是越界了,1位的数字只能是0或1,所以11截断后只有1,同理ucData1给的4也是越界的,100截断后是00,只有5的101是正常的。填充序列是类似小端的低地址在低位,所以排列顺序是00 101 00 1。也就是0010 1001,即十六进制表示0x29
4.枚举:
(1)什么是枚举:一个集的枚举是列出某些有穷序列集的所有成员的过程,或者是一种特定类型对象的计数,这两种类型经常(但不总是)重叠,是一个被命名的整型常数的集合。
形式:
enum 枚举名
{
成员1[=整型常数];
成员2[=整型常数];
...
成员n[=整型常数];
}枚举变量;
(2)如果枚举没有初始化,从第一个元素开始依次赋值0,1,2,......。当枚举中某个元素成员赋值以后,其后的成员按依次加1的规则确定其值。
(3)枚举类型所占空间的大小:当枚举成员小于4个字节时,所占空间为4个字节,
当枚举成员的值大于4个字节时,所占空间为8个字节(最大范围不能超过long long类型)
(4)枚举的优点:增加代码的可读性和维护性,和#define定义的标识符比较枚举有类型检查,
更加严谨,防止了命名污染(封装),便于调试,使用方便,一次可定义多个变量。
(5)Eg:
enum ENUM_A
{
X1,
Y1,
Z1 = 255,
A1,
B1,
}lin;
enum ENUM_A enumA = Y1;
enum ENUM_A enumB = B1;
printf("%dn",sizeof(lin));//4
printf("%d %dn", enumA, enumB);//1 257
注释:枚举成员小于4个字节,所占空间的大小为4个字节,枚举默认从0开始,所以X1是0,故Y1是1,给了数字后会根据数字向后推,那么Z1是255,A1是256,所以B1是257。
5.联合体(共用体)
(1)联合体的定义:联合体是一种特殊的自定义类型,这种类型定义的变量也包含一系列的成员,特征是这些成员共用同一块空间。
形式:
union lin
{
类型名 成员名1;
类型名 成员名2;
......
类型名 成员名n;
};
(2)联合的特点:联合的成员是共用同一块空间,联合的大小,至少是最大成员的大小。
(3)Eg:运用联合体判断大小端
#includeint lin() { union ln { char c; int i; }u; u.i = 1; return u.c; } int main() { if (lin() == 1) { printf("小端"); } else { printf("大端"); } return 0; }
(4)联合体也可以存在内存对齐
#includeunion Un { short s[7]; int n; }; int main() { printf("%dn", sizeof(union Un));//16 return 0; }
注释:结构体向int对齐,7个short一共是14字节,对齐后是16字节。n是单独的4字节,由于是union,所以n与s共用空间,只取最长的元素,故占用16字节。



