常用数据类型占用内存大小:(视编译器不同)一般来说
32位编译器:
char :1个字节
char*(即指针变量): 4个字节 (32位的寻址空间是2^32, 即32个bit,也就是4个字节。同理64位编译器)
short int : 2个字节
int: 4个字节
unsigned int : 4个字节
float: 4个字节
double: 8个字节
long: 4个字节
long long: 8个字节
unsigned long: 4个字节
64位编译器:
char :1个字节
char*(即指针变量): 8个字节
short int : 2个字节
int: 4个字节
unsigned int : 4个字节
float: 4个字节
double: 8个字节
long: 8个字节
long long: 8个字节
unsigned long: 8个字节
结构体:
结构体的容量计算需要进行对齐。即每一个数据在存入时,之前的字节偏移量必须是该数据大小的整数倍(即进行对齐),最后结构体按照其成员中对齐数最大的成员进行对齐。
例如:struct A
{
int a; 0-3
4-7 要填充以保证内存对齐的原则
double b; 8-15
char c[9]; 16-24
};
首先给a分配内存,因为int占四个字节接着给b分配内存,double占8个字节,所以按照8个字节对齐,起始位为8(8%8=0),b为8-15;
最后char占一个字节,所以c为16-24;
因此结构体A的大小为8+8+9 = 25;有因为25不是结构体中最大占用内存类型double类型大小(8)的整数倍,所以A最后的大小是32;
struct B
{
int a;
double b;
double c;
char d;
};
同理,按上面的分析方法确定各个变量在内存中的位置:
a,0-3;
,4-7;
b,8-15;
c,16-23;
d,24;
TOTAL = 25
25 不是8的倍数,所以最后B大小是32;
总结计算结构体的步骤
1.内存对齐与编译器设置有关,首先要搞清编译器这个默认值是多少
2.如果不想编译器默认的话,可以通过#pragma pack(n)来指定按照n对齐
3.每个结构体变量对齐,如果对齐参数n,变量所占字节数(m),内存地址的起始位置%min(n,m)=0。也就是最小化长度规则
4.结构体总大小: 对齐后的长度必须是成员中最大的对齐参数的整数倍。
5.补充:如果结构体A中还要结构体B,那么B的对齐方式是选它里面最长的成员的对齐方式
所以计算结构体大小要走三步,首先确定是当前程序按照几对齐(参照1,2点),接着计算每个结构体变量的大小和偏移(参照3,5),最后计算结构体总大小(参照4)。
联合体
联合体union定义:
在C Programming Language 一书中对于联合体是这么描述的:
当多个数据需要共享内存或者多个数据每次只取其一时,可以利用联合体(union)。由于联合体中的所有成员是共享一段内存的,因此每个成员的存放首地址相对于于联合体变量的基地址的偏移量为0,即所有成员的首地址都是一样的。为了使得所有成员能够共享一段内存,因此该空间必须足够容纳这些成员中最宽的成员。对于这句“对齐方式要适合其中所有的成员”是指其必须符合所有成员的自身对齐方式。(注意对此段对话的理解)
union U1
{
char s[10];
int n;
double d;
};
s占10字节,n占4字节,d占8字节,因此其至少需10字节的空间。然而其实际大小并不是10,用运算符sizeof测试其大小为16.这是因为这里存在字节对齐的问题,10既不能被4整除,也不能被8整除。因此补充字节到16,这样就符合所有成员的自身对齐了。从这里可以看出联合体所占的空间不仅取决于最宽成员,还跟所有成员有关系,即其大小必须满足两个条件:1)大小足够容纳最宽的成员;2)大小能被其包含的所有基本数据类型的大小所整除。
union U2
{
};
s占5字节,n占4字节,d占8字节,因此其至少需8字节的空间.而char型占用一个字节,int占用四个字节,double占用8字节,其最小公倍数为8字节,所以U2也就是占用8字节就可以了。
联合体union和大小端(big-endian、little-endian):
- #include<stdio.h>
- union var{
- char c[4];
- int i;
- };
- int main(){
- union var data;
- data.c[0] = 0x04;//因为是char类型,数字不要太大,算算ascii的范围~
- data.c[1] = 0x03;//写成16进制为了方便直接打印内存中的值对比
- data.c[2] = 0x02;
- data.c[3] = 0x11;
- //数组中下标低的,地址也低,按地址从低到高,内存内容依次为:04,03,02,11。总共四字节!
- //而把四个字节作为一个整体(不分类型,直接打印十六进制),应该从内存高地址到低地址看,0x11020304,低位04放在低地址上。
- printf("%x\n",data.i);
- }
结果:
11020304
证明我的32位linux是小端(little-endian)
大小端存储问题:
小端:低地址存低字节--高地址存高字节
大端:低地址存高字节--高地址存低字节
嵌入式系统开发者应该对Little-endian和Big-endian模式非常了解。采用Little-endian模式的CPU对操作数的存放方式是从低字节到高字节,而Big-endian模式对操作数的存放方式是从高字节到低字节。例如,16bit宽的数0x1234在Little-endian模式CPU内存中的存放方式(假设从地址0x4000开始存放)为:
内存地址 | 0x4000 | 0x4001 |
存放内容 | 0x34 | 0x12 |
而在Big-endian模式CPU内存中的存放方式则为:
内存地址 | 0x4000 | 0x4001 |
存放内容 | 0x12 | 0x34 |
32bit宽的数0x12345678在Little-endian模式CPU内存中的存放方式(假设从地址0x4000开始存放)为:
内存地址 | 0x4000 | 0x4001 | 0x4002 | 0x4003 |
存放内容 | 0x78 | 0x56 | 0x34 | 0x12 |
而在Big-endian模式CPU内存中的存放方式则为:
内存地址 | 0x4000 | 0x4001 | 0x4002 | 0x4003 |
存放内容 | 0x12 | 0x34 | 0x56 | 0x78 |
联合体union的存放顺序是所有成员都从低地址开始存放。
编程测试处理器是大端还是小端:
试题:请写一个C函数,若处理器是Big_endian的,则返回0;若是Little_endian的,则返回1
解答:
int checkCPU( )
{
{
union w
{
int a;
char b;
} c;
c.a = 1;
return(c.b ==1);
}
}