结构体大小、内存对齐方式、联合

各编译器下sizeof()值: 

32位编译器: 32位系统下指针占用4字节 

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位编译器:64位系统下指针占用8字节 

char :1个字节 
char*(即指针变量): 8个字节 
short int : 2个字节 
int: 4个字节 
unsigned int : 4个字节 
float: 4个字节 
double: 8个字节 
long: 8个字节 
long long: 8个字节 
unsigned long: 8个字节

内存对齐方式

以16位CPU为例,假设我们同时声明两个变量: char a;  short b; 


用&(取地址符号)观察变量a, b的地址的话,我们会发现, 如果a的地址是0x0000,那么b的地址将会是0x0002或者是0x0004。 

那么就出现这样一个问题:0x0001这个地址没有被使用,那它干什么去了? 答案就是它确实没被使用。 因为CPU每次都是从以2字节(16位CPU)或是4字节(32位CPU)的整数倍的内存地址中读进数据的。

如果变量b的地址是0x0001的话,那么CPU就需要先从0x0000中读取一个short,取它的高8位放入b的低8位,然后再从0x0002中读取下一个short,取它的低8位放入b的高8位中,这样的话,为了获得b的值,CPU需要进行了两次读操作。


但是如果b的地址为0x0002, 

那么CPU只需一次读操作(只需从0x0000中读取一个short,取它的高8位放入b的低8位就可以了)就可以获得b的值了。

所以编译器为了优化代码,往往会根据变量的大小,将其指定到合适的位置,即称为内存对齐(对变量b做内存对齐,a、b之间的内存被浪费,a并未多占内存 

为了使CPU能够对变量进行快速的访问,变量的起始地址应该具有某些特性,即所谓的”对齐”. 比如4字节的int型,其起始地址应该位于4字节的边界上,即起始地址能够被4整除.

数据对齐的好处:以空间换时间的方式提高CPU速率

对齐规则: 
(1)结构体中每个成员分别按自己的对齐字节数和PPB(指定的对齐字节数,32位机默认为4)两个字节数最小的那个对齐,这样可以最小化长度。如在32bit的机器上,int的大小为4,因此int存储的位置都是4的整数倍的位置开始存储。 
(2) 复杂类型(如结构)的默认对齐方式是它最长的成员的对齐方式,这样在成员是复杂类型时,结构体数组的时候,可以最小化长度。看下面例子。 
(3)结构体对齐后的长度必须是成员中最大的对齐参数(PPB)的整数倍,这样在处理数组时可以保证每一项都边界对齐。 

(4)结构体作为数据成员的对齐规则:在一个struct中包含另一个struct,内部struct应该以它的最大数据成员大小的整数倍开始存储。如 struct A 中包含 struct B, struct B 中包含数据成员 char, int, double,则 struct B 应该以sizeof(double)=8的整数倍为起始地址。

举例说明

例1

struct test
{
char x1;
short x2;
float x3;
char x4;
};

由于编译器默认情况下会对这个struct作自然边界(有人说“自然对界”我觉得边界更顺口)对齐,结构的第一个成员x1,其偏移地址为0,占据了第1个字节。第二个成员x2为short类型,其起始地址必须2字节对界,因此,编译器在x2和x1之间填充了一个空字节。结构的第三个成员x3和第四个成员x4恰好落在其自然边界地址上,在它们前面不需要额外的填充字节。在test结构中,成员x3要求4字节对界,是该结构所有成员中要求的最大边界单元,因而test结构的自然对界条件为4字节,编译器在成员x4后面填充了3个空字节。整个结构所占据空间为12字节。

#include<iostream>
 
using namespace std;
 
//windows 64 位默认 结构体对齐系数为8,32位 结构体对齐系数为4
 
//测试系统对齐系数
// #pragma pack(8)  my_struct_1 为16字节
// #pragma pack(4)  my_struct_1 为12字节
// 不加#pragma pack(8)  my_struct_1 为16字节
//顾系统默认对齐系数为8
 
struct my_struct_1
{
	char a;     //1
	double b;   //之前补7 +8     8/8==1
};
 
#pragma pack(4)
struct my_struct_2
{
	char a;    //1
	double b;  //3+8
	int c;     //4     16/4=4
};
#pragma pack()
 
#pragma pack(2)
struct my_struct_3
{
	char a;    //1
	double b;  //1+8
	int c;     //4     14/2
};
#pragma pack()
 
#pragma pack(4)
struct my_struct_4
{
	char a[5];  //5
	double b;   //3+8   16/4
};
#pragma pack()
 
#pragma pack(2)
struct my_struct_5
{
	char a[5];  //5
	double b;   //1+8   14/2
};
#pragma pack()
 
#pragma pack(4)
struct my_struct_6
{
	char a;    //1
	char b[3]; //3
	char c;    //1   1+3+1
};
#pragma pack()
 
#pragma pack(4)
struct my_struct_7
{
	char a;    //1
	char b[3]; //3
	char c;    //1   
	int d;     //补齐 3 +4 
};
#pragma pack()
 
#pragma pack(4)
struct test
{
char x1;   //1
short x2;  //补齐1+ 2
float x3;  //4
char x4;   //1 补齐+3  
};
#pragma pack()

异质的数据结构

C语言提供了两种将不同类型的对象组合到一起创建数据类型的机制 

1、结构,用关键字struct声明,将多个对象集合到一个单位中

2、联合,用关键字union声明,允许用几种不同的类型来引用一个对象

结构

 结构的所有组成部分都存放在内存中一段连续的区域内,而指向结构的指针就是结构第一个字节的地址
struct  rect{

long x;

char a;

};

联合

用不同的字段引用相同的内存块

union u3{

char c;

int i[2];

double v;

};

对于union u3的指针p,p->c、p->i[0]、p->v引用的都是数据结构的起始位置。

注意:一个联合的总的大小等于它最大字段的大小


  1. 联合和大小端 
  1. 大端模式:内存地址从低地址开始读, 
    小端模式:内存地址从高地址开始读。 
    数组地址:地址从低地址开始。
    #include<iostream>
    using namespace std;
    union X
    {
    	int32_t a;
    	struct
    	{
    		int16_t b;
    		int16_t c;
    	};
    };
    int main() {
    	X x;
    	x.a = 0x20160237;
    	cout << &(x.a) << endl;
    	cout << &(x.b) << " " << &(x.c) << endl;
    	system("pause");
    	return 0;
    }


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值