结构体的对齐访问一:
为什么我们要学习结构体的对齐方式呢?话不多说我们先上代码
#include <stdio.h>
struct MyStruct
{
char s1;
int s2;
};
int main (void)
{
MyStruct MyStruct;
MyStruct.s1 = 't';
MyStruct.s2 = 12;
char* p = (char*)(&MyStruct);
printf("*p = %c\n", *p);
int* p1 = (int*)((int)&MyStruct + 1);
printf("*p = %d\n", *p1);
return 0;
}
#include <stdio.h>
struct MyStruct
{
char s1;
int s2;
};
int main (void)
{
MyStruct MyStruct;
MyStruct.s1 = 't';
MyStruct.s2 = 12;
char* p = (char*)(&MyStruct);
printf("*p = %c\n", *p);
int* p1 = (int*)((int)&MyStruct + 4);
printf("*p = %d\n", *p1);
return 0;
}
总结:通过对上面两段代码的测试和结果的分析,明显程序2才是对的,char类型在32位中是只占用到一个字节的内存的,为什么通过指针方式访问,加1是错误的,而加4确是对的呢,这里面的知识就涉及到结构体的对齐访问了。
1. 通过上一节课,我们可以知道在结构体中对元素的访问其实在本质上也是一个指针方式,结合中国元素在整个结构体中的偏移量和中国元素的类型来进行访问。
2.但是在实际上结构体的元素的偏移量比我们想的要复杂的多,因为结构体要考虑到元素的对齐访问。所以每个元素实际上占用的字节数和自己本身的类型所占的字节数不一定一样。(比如在上面的例子中,我们没有进行测试之前我们会认为char是占用一个字节的,但是实际上char是占用4个字节的)
3.一般来说,我们用 . 的方式访问结构体元素时,我们是不需要考虑结构体的对齐的,因为编译器和帮我们处理这个细节问题,但是因为C语言本身就是很底层的语言,而且在嵌入式的开发中我们经常需要从内存的角度通过指针的方式来处理结构体以及其中的元素,因此我们还是需要掌握结构体对齐规则的
结构体为什么要对齐访问:
我们都知道,内存本身是一个物理器件,本身会存在一定的局限性,如果内存每次的访问时按照4字节的对齐访问,那么效率是最高的,但是这样做的结果就是你的有些内存会被浪费掉。所以实际上结构体的对齐就是用内存空间换取效率。
结构体对齐访问二:
#include <stdio.h>
/*分析过程:
首先是整个结构体,整个结构体变量4字节对齐是编译器保证的,我们不用操心。
然后是第一个元素s1,a的开始地址就是整个结构体的开始地址,所以自然是4字节对齐的。但是a的结束地址要由下一个元素说了算
然后是第二个元素s2,因为上一个元素是int类型的,本身就占用4个字节,本身对齐,所以留给S2的开始地址也是4字节对齐地址,所以
b可以直接放,b的起始地址定了之后,结束地址就看下一个元素S3,然后是第三个元素S3,short类型需要2字节对齐(short类型元素必须
放在类似0,2,4,8这样的地址处。
当然,当整结构体的元素对齐存放后,还没有结束,因为整个结构体大还要是4的整数倍
*/
struct MyStruct_1 //不对齐(就是一字节对齐) //4字节对齐(编译器默认对齐方式)
{
int s1; // 4 //4
char s2; // 1 //2
short s3; // 2 //2
};
struct MyStruct_2 //不对齐(就是一字节对齐) //4字节对齐(编译器默认对齐方式)
{
char s1; // 1 //(1+3)
int s2; // 4 //4
short s3; // 2 //(2+2)
};
struct MyStruct_3 //不对齐(就是一字节对齐) //8字节对齐
{
char s1; // 1 //4
int s2; // 4 //4
short s3; // 2 //2+(6)
double s4; // 8 //8
};
int main(void)
{
printf("sizeof(MyStruct_1)) = %d\r\n", sizeof(MyStruct_1));
printf("sizeof(MyStruct_2)) = %d\r\n", sizeof(MyStruct_2));
printf("sizeof(MyStruct_3)) = %d\r\n", sizeof(MyStruct_3));
return 0;
}
总结:
1.结构体整体本身必须安置在4字节对齐处,结构体对齐后在大小必须是4的倍数(编译器设置为4字节对齐时,如果编译器设置为8字节对齐,则这里的4是8)
2.结构体中每个元素本身必须对齐存放,而每个元素本身都有自己的对齐规则
3.编译器考虑结构体存放时,已满足以上2点要的至少内存需要存放来计算
结构体对齐访问的函数:
在我们写程序的过程过程中我们,我们常用来设置结构体对齐方式的命令的有2种:
第一种:#pragma pack()
pragma pack(),就是我们要设置编译器的对齐为一个字节的对齐,也可以理解为取消编译器的对齐方式;pragma pack(N),N就是我们要设置多少字节对齐的参数的输入
第二种:在gcc中对齐指令_attribute_((packed)) 、attribute((aligned(n)))
attribute((packed))使用是直接放到要进行结构体内存对齐的类型定义的后面,然后起作用范围只用加了这个函数的这个结构体起作用。它的作用是让整个结构体变量整体进行n字节对齐(注意是结构体变量整体n字节对齐,而不是结构体内各个元素也要n字节对齐)
、