C语言之--结构体内存对齐

18 篇文章 0 订阅
一、聚合数据类型:
数组和结构体。数组是相同数据类型的集合,它的每个数据元素可以通过下标或者是指针间接访问,而结构体的不同点是它是不同元素类型的数据的集合。结构体不能通过下标来访问原因是它的每个成员(元素)的类型不同,长度不同。它们可以通过名字来访问。
结构体的声明:
struct tag
{   int a;
   short b;
  char c;
}x;
tag标签可以结构体起名字,{ }里的成员列表表示是每个元素的类型,和名字。x表示是结构体变量它包含三个成员整形a,短整形b,字符型c。
二、结构体元素地址分配规则:
既然和数组一样都是聚合类型,是否地址也是从低到高呢?下面通过例子来验证下:
typedef struct A
{
	int a;
	float b;
	char c;
}obj;
int main()
{
	obj x;
	x.a = 10;
	x.b = 3.14;
	x.c = 'c';
	printf("%p\n%p\n%p\n", &x.a, &x.b, &x.c);
	system("pause");
	return 0;
}

通过结果可以看出结构体成员的地址分配规律也是从低到高的。
三、结构体成员的访问:
1、直接访问:结构体名加点操作符.的形式:点操作符接受两个操作数,左边表示结构体名,右边表示要访问的成员。点操作符的访问顺序是从左到右。
typedef struct A
{
	int a;
	float b;
	char c;
}obj;
int main()
{
	obj x;
	x.a = 10;
	x.b = 3.14;
	x.c = 'c';
	system("pause");
	return 0;
}
使用typedef定义一个新类型,此时obj不是变量名而是类型名。
2.也可以间接访问,用->操作符来访问其成员。
struct A
{
	int a;
	float b;
	char c;
	int arr[5];
}x = { 10,
       3.14,
	   'c',
	   {1,2,3,4,5},
};
int main()
{
	struct A *p = &x;
	printf("%d\n", p->a);
	printf("%f\n", p->b);
	printf("%c\n", p->c);
	printf("%d\n", p->arr[3]);
	system("pause");
	return 0;
}
&x表示是整个结构体的地址。3.访问嵌套的成员:如:
typedef struct A
{
	int arr[5];
}obj;
struct B
{
	int a;
	float b;
	char c;
	obj d;
} x = {
			1,
			3.14,
			'c',
			{1,2,3,4,5}
		};
int main()
{
	struct B *px = &x;
	px->a;
	px->b;
	px->d.arr[1];
	system("pause");
	return 0;
}

四、结构体的内存对齐:
struct A
{
	int a;
	char b;
	float c;
};
int main()
{
	printf("%d\n", sizeof(struct A));
	system("pause");
	return 0;
}
结果是多少呢?

首先明白对齐原因:
1:平台原因:不是所有的硬件平台都能访问任意地址的任意数据,某些平台只能在某些地址取某些特定类型的数据,否则抛出硬件异常
2.性能原因:
数据结构(尤其是栈)应该尽肯能地自然边界对齐,原因在于,为了访问未对齐的的内存,处理器需要做两次内存访问,两次对齐的内存访问仅需要一次访问。
结构体内存对齐的规则:
1:第一个成员在与及结构体变量偏移量为0的地址处;
2:其他成员变量要对齐到对齐数的整数倍的地址处;
(对齐数=编译器默认一个对齐数与该成员大小的较小值)
编译器默认对齐数:
vs默认对齐数是8;
Linus默认对齐数是4
3:结构体的总大小为最大对齐数(每个成员除第一个成员都有一个对齐数)的整数倍;
4:如果嵌套结构体的情况,嵌套结构体对齐到自己最大对齐数的整数倍处,结构体的整体大小就是所有最大对齐数(含嵌套结构体的对齐数)的整数倍。
接下来我们按照规则来分析:当前是在vs环境下,结构体第一个成员int 型4个字节没有对齐数,到char型自身大小是1个字节,与编辑器默认的对齐数是8的较小值是1,所以char型的对齐数1对齐到前面4个字节, 此时偏移量5,float型自身4个字节,与默认值8的较小值是4,4 的整数倍是8但前面的只有5个字节,系统再开辟3个字节刚好是8对齐到对齐数4的整数倍处。此时内存的大小 12,12 是结构体成员最大(第一个除外)对齐数4的整数。
嵌套结构体:
struct B
{
	int x;// 4不用对齐;
	double y;
	char c;
	//
}obj;
//24
struct A
{
	int a;//4
	float b;//4
	char c;//+1
	struct B obj[2];//结构体数组;//48  +16
	int *p;//4 (68)+4
};

第一个按照规则很容易得到其内存的大小是24,那么嵌套该怎么理解对结构体B分析:int 型4个字节,没有对齐数,float4个字节与默认值8的较小值是4,对起到其对齐数整数倍4处,此时内存大小是8;char型1个字节,与默认值的的较小值是1,对齐到8的位置,此时有9个字节,结构体数组B的大小是48;由前面分析可知结构体B对齐数的最大对齐数是4,对起到对齐数的整数倍前面为9再开辟3个字节到12此时为60;最后指针4个字节,与默认最小值为4;对齐到对齐数整数倍(60是4的整数倍)。此时大小为72。
当然我还可以通过修改默认值来改变其大小
可以通过宏指令##pragma pack (1)此时1表示编辑器的默认值是1
五、位段
位段,C语言允许在一个结构体中以位为单位来指定其成员所占内存长度,这种以位为单位的成员称为“位段”或称“位域”(利用位段能够用较少的位数存储数据。
位段的声明和结构体类似区别在于,位段的成员声明必须是int,unsinged int ,signed int 型,成员的后面是一个冒号和一个整数,这个整数是该位段所占字节的数目;如:
struct A
{
	int a : 4;
	int b : 4;
	int c : 4;
}obj;
int  main()
{
        obj.a = 10;
	obj.b = 5;
	obj.c = 7;
	printf("%d\n%d\n%d\n",obj.a,obj.b,obj.c);
	printf("%d\n", sizeof(struct A));
	system("pause");
	return 0;
}
把位段声明为整形,它究竟是被解释为有符号的数还是无符号的数由编辑器决定。




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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值