结构的大小

来源:互联网 发布:京都念慈庵 知乎 编辑:程序博客网 时间:2024/06/11 04:25

很多同学对一些类型的大小不是很重视,其实这些都是很重要的东西,
尤其是c,因为c的指针使用非常多,不知道类型、结构的大小,怎么控制内存?
当然准确的字节数依赖于机器和编译器。我们今天讲的是我们常用的WIN32
首先我们要搞明白b和B的区别:b是bit(位)。B是Byte(字节)。1B = 8b
char 1B
short 2B
int 4B
long 4B
float 4B
double 8B
指针都是 4B
这些东西背着就行了。
由于不同的平台其大小也不一样,故我们常使用宏定义,或者typedef。
如:
typedef char int8;
typedef unsigned char uint8;
typedef short int16;
#define INT8 char
#define UINT8 unsigned char
#define INT16 short
定义时使用
int8 a;
uint8 b;
INT8 c;
这样在针对不同的平台使用不同的typedef,
如何根据不同的平台使用不同的typedef也可以用宏来实现,这里不再阐述,或者改动少量代码。
下面的结构大小的问题就有的复杂了。
struct s
{
 char c;
 int b;
}
sizeof(s) = ?
初学者一看char 1;int 4;加起来是5。
可能你是对的,但大部分情况下你是错的。
其实sizeof(s) = 8;在这里有个字节对齐的机制。
下面先看一个我的同事在工作中遇到的一个BUG。
func(uint8 *pdata)
{
uint32 tmp = 0xAB;
uint32 *a = (uint32 *)pdata;
 
*a = tmp; //某平台死机,其他平台正常
}
同学们知道可能的原因吗。
这就要用字节对齐来解释了。简单点说就是:
让宽度为1的基本数据类型(char 等)都位于能被1整除的地址上,
让宽度为2的基本数据类型(short等)都位于能被2整除的地址上,
让宽度为4的基本数据类型(int  等)都位于能被4整除的地址上,以此类推。
本来uint8*的pdata是一个被1整除的地址,现在将它赋值给一个应该被4整除的地址。
有的平台可以正常运行,但有的平台就会进行检查,
你在操作一个只能被1整除地址上的宽度为4的数据时就死机了。
然而为什么需要字节对齐?这样有助于加快计算机的取数速度。
关于怎么计算结构体的大小,前人有如下总结:
1) 结构体变量的首地址能够被其最宽基本类型成员的大小所整除;
2) 结构体每个成员相对于结构体首地址的偏移量(offset)都是成员大小的整数倍,如有需要编译器会在成员之间加上填充字节(internal adding);
3) 结构体的总大小为结构体最宽基本类型成员大小的整数倍,如有需要编译器会在最末一个成员之后加上填充字节(trailing padding)。
这样我们看下面的结构:
struct s1
{
 int a;
 char c;
}
sizeof( s1 ) = 8;//根据上面的总结很容易算出来。
了解了这个我们在定义我们的结构的时候就可以注意我们的写法是可以节约空间的。
例如:
struct s1
{
 char a;
 int b;
 char c;
};
就可以写成
struct s1
{
 char a;
 char c;
 int b;
};
节约了4个字节。
下面我们再看这个结构的大小
struct s2
{
 char a;
 s1 s;
 char b;
};//呵呵有的复杂了把。
像这种结构里面套有结构的情况,我们要将s1拆分开来看,s1里面最大是int,a和int对齐,1+3.s1是8.加上b和对齐,1+3.共16.

但这并不是总是这样,有时我们需要改变对齐的方式,这里有两个编译命令#pragma pack(n)和align(n)
#pragma pack(n) //n的合法值包括(1,2,4,8,16)他和编译开关/Zpn是一个意思(如果不知道什么是编译开关可以到网上搜)。
意思就是对齐值取min( n, sizeof( member ) )
举个例子:
#pragma pack(push)//保存之前的pack
#pragma pack(2)//设置当前的pack
struct s1
{
 char a;
 int b;
 char c;
};
#pragma pack(pop)//恢复之前的pack
大小的计算:char型的a和要对齐的min( 2, sizeof(int) ),1+1;int型b是4;同样c和要对齐值共2;此时是sizeof( s1 ) == 8,

align(n)//n的合法值是2的倍数(2,4,8,...)指此结构的大小是max( n, size(member) )的倍数。
__declspec(align(32)) struct s1
{
 char a;
 int b;
 char c;
};
前面还是和上面的一样排序,但到最后一个char型的c时后面要填充满32个字节。sizeof(s1) == 32.
有个问题要注意。空结构的大小不是0.而是1.
struct s
{
};
sizeof( s ) == 1;
当然如果你定义
__declspec(align(32)) struct s1
{
};
大小就是32。
在结构中我们还有一种常用的用法。按位来存取数据。
struct s
{
 char a:3;
 char b:4;
 char c:2;
 char :0;//从新的char开始。
 char d:3;
 int e:4;
 char f:1;
};
我来画个图把,直接明了。
sizeof(s) == 12

|1 a1 |2 a2 |3 a3 |4 b1 |5 b2 |6 b3 |7 b4 |8   |
|1 c1 |2 c2 |3    |4    |5    |6    |7    |8   |
|1 d1 |2 d2 |3 d3 |4    |5    |6    |7    |8   |
|1    |2    |3    |4    |5    |6    |7    |8   |           //对齐int
|1 e1 |2 e2 |3 e3 |4    |5    |6    |7    |8   |…………|31  |32  |
|1 f1 |2    |3    |4    |5    |6    |7    |8   |
|1    |2    |3    |4    |5    |6    |7    |8   |          //下面的都是对齐
|1    |2    |3    |4    |5    |6    |7    |8   |
|1    |2    |3    |4    |5    |6    |7    |8   |

我不多说了,多多揣摩。

原创粉丝点击