结构体深度剖析(内存对齐,对齐参数,偏移量)

一、了解结构体
1
在C语言中,除了最常见用数据类型,字符类型(char)、整数类型(short、int、long )、实型(float、double),,,,,,最常见也是最经典的还有一种数据类型,那就是结构体。 
二、结构体经典面试题: 
(1)、什么是结构体? 
(2)、一般在什么情况下用到结构体? 
(3)、什么是结构体内存对齐?为什么要对齐?怎样对齐? 
(4)、对齐参数如何设置?可以设置为按照任意字节数对齐吗? 
(5)、如何知道结构体某个成员相对于结构体起始位置的偏移量? 
三、 下面,我们围绕以上提出的6个问题来解释结构体。 
(1)、什么是结构体? 
定义:结构体是一系列数据的集合这些数据可能描述了一个物体,也可能是对一个问题的抽象。举个栗子,简单的说,对于人,人有名字,性别,年龄,身高,体重等个人信息,那么,我们在定义这种个体的时候,就不能说它能用一个字符或整型变量来定义。 这时候,就需要结构体闪亮登场了。 
for example 1:

          struct people
                {
                    char name[20];
                    int age;
                    char gender[3];
                    float height;
                         };

(2)、一般在什么情况下用到结构体? 
a、一般当内置内存无法满足用户需要,没有合适类型对应对象时,需要封装特定的类型 
b、当函数有多个参数时,返回值过多,需要封装特定类型,将参数打包返回。 
(3)、什么是结构体内存对齐?为什么要对齐?怎样对齐? 
结构体内存对齐:元素是按照定义顺序一个一个放到内存中去的,但并不是紧密排列的。从结构体存储的首地址开始,每个元素放置到内存中时,它都会认为内存是按照自己的大小来划分的,因此元素放置的位置一定会在自己宽度的整数倍上开始。 
for example2:

 struct   A
            {
               int a;
               char b;
               double c;
               char d;
            };

解析: 
在windows系统32位平台上: 
int占4个字节 
char占1个字节 
float占4个字节 
double占8个字节 
int a从0偏移开始,占四个字节,即占用0,1,2,3,现在可用偏移为4偏移,接下来存char b; 由于4是1的倍数,故而,b占用4偏移,接下来可用偏移为5偏移,接下来该存double c; 由于5不是8的倍数,所以向后偏移5,6,7,都不是8的倍数,偏移到8时,8是8的倍数,故而c从8处开始存储,占用8,9,10,11,12,13,14,15偏移,现在可用偏移为16偏移,最后该存char d ;因为16是1的倍数,故d占用16偏移,接下来在整体向后偏移一位,现处于17偏移,min(默认对齐参数,类型最大字节数)=8;因为17不是8的倍数,所以继续向后偏移18…23都不是8的倍数,到24偏移处时,24为8的整数倍,故而,该结构体大小为24个字节。 

方法总结: 
a、从零偏移处开始,按字节大小计算,判断此偏移地址是否为该成员变量和对齐参数两者之间的最小值,即min(对齐参数,sizeof()成员); 
b、若是,则从此处开始占用内存,大小为该类型所占字节数值,若不是,则内存向后偏移到最小值整数倍处,再开始占用空间。 
c、按a、b、两步骤算出结构体实际所占内存时,为了方便后面类型的存储,再向后偏移一位,然后判断该地址是否是默认对齐数与该结构体中最大类型所占字节数的最小值 ,即:min(默认对齐参数,类型最大字节数)的整数倍,若是,则当前偏移地址的字节数便是结构体大小,若不是,继续向后偏移,直至为最小值整数倍为止。 
(4)、对齐参数如何设置?可以设置为按照任意字节数对齐吗? 
解析:在windows中,VS编译器下,默认对齐数为8; 
在Linux中,默认对齐数为4 
设置对齐参数可在结构体struct之前加上#pragma pack(对齐数),在struct之后加上#pragma pack;便可以设置对齐参数。 
for example3:

#pragma pack(4)
struct   A
            {
               int a;
               char b;
               double c;
               char d;
            };
#pragma pack;

对齐参数不能任意设置,只能是内置类型已有的字节数,如:char(1)、short(2),int(4),double(8)…不能是3,5…任意数。 
(5)、如何知道结构体某个成员相对于结构体起始位置的偏移量? 
使用offsetof宏来判断结构体中成员的偏移地址。使用offsetof宏需要包含stddef.h头文件,该宏定义如下:

   #define offsetof(type,menber) (size_t)&(((type*)0)->member)
巧妙之处在于将地址0强制转换为type类型的指针,从而定位到member在结构体中偏移位置,编译器认为0是一个有效的地址,从而认为0是type指针的起始地址。 
到了这里,本文已接近尾声了,下篇博客将更新位段、联合以及大小端的相关知识详尽剖析,敬请期待。
 

转:https://blog.csdn.net/dai_wen/article/details/78304568

猜你喜欢

转载自blog.csdn.net/eric_e/article/details/84722517