问小白 wenxiaobai
资讯
历史
科技
环境与自然
成长
游戏
财经
文学与艺术
美食
健康
家居
文化
情感
汽车
三农
军事
旅行
运动
教育
生活
星座命理

结构体在内存中的存储

创作时间:
作者:
@小白创作中心

结构体在内存中的存储

引用
CSDN
1.
https://blog.csdn.net/djdjiejsn/article/details/136825013

本文详细探讨了C语言中结构体在内存中的存储方式,包括结构体成员的存储连续性、内存对齐规则、以及结构体的传参方式等。通过具体的代码示例和详细解释,帮助读者深入理解结构体在内存中的存储机制。

一.结构体的成员在内存中的存储是连续的吗?

先来看看下面代码:

#include<stdio.h>
struct stu
{
    char a;
    int b;
};
int main()
{
    printf("%zd\n", sizeof(struct stu));
    return 0;
}  

如果直接连续存储,char 占用1个字节,int占用4个字节,结果打印5。

但是结果这的是如此吗?

所以结构体的数据不是连续储存的。

二.结构体内存对齐规则:(4个规则)

  1. 结构体的第一个成员对齐到和结构体变量起始位置偏移量为0的地址处。
  2. 其他成员变量要对齐到某个数字(对齐数)的整数倍(0,1,2...)的地址处。
    每一个成员都有一个对齐数
    ---VS中的默认对齐数是8
    ---Linus中gcc没有默认对齐数,对齐数是成员自身的大小
    用默认对其数和成员变量的大小进行比较,较小的就是对齐数。
    例如:
    int的对齐数大小是4字节,默认对齐数是8,所以较小的4。
    则int的对齐数4,int只能从结构体起始位置偏移量为4的整倍数开始存放(4,8,12)。
  3. 结构体总大小为最大对齐数(结构体中每个成员变量都有一个对齐数,所有对齐数中最大的)的整数倍。
    例如:如果结构题的最后一个成员存放完以后的结束偏移量是11,但是结构体里的最大对齐是4,那么结构体的总大小为12。
  4. 如果嵌套了结构体的情况,嵌套的结构体成员对齐到自己的成员中最大对齐数的整数倍处,结构体总大小是所有最大对齐数(含嵌套结构体中成员的对齐数)的整数倍。

三.分析最开始的代码:

根据对齐规则1,结构体的第一个成员对齐到结构体起始位置偏移量为0的地址处,char类型占一个字节。
此时偏移量来到1,下一步是要进行int。根据对齐规则2,int类型不能从偏移量为1(不是int对齐数的整倍数)的地方开始存放。所以int从偏移量为4的位置开始存放,大小是4个字节。
此时偏移量来到8,根据对齐规则3,结构体的总大小必须要是最大成员偏移量的整倍数。根据上面可知,char的偏移量是1,int的偏移量是4。现在偏移量是8,8是4的整倍数,所以struct stu的总大小是8个字节。

3.1训练一:

#include<stdio.h>
struct stu
{
    char a;
    int b;
    char c;
};
int main()
{
    printf("%zd\n", sizeof(struct stu));
    return 0;
}  

char a从0到1,int 从4到8,char c从8到9,结构体总大小要是最大结构体成员对齐数的整数倍。9不是4的倍数,所以总大小是12;

3.2训练二:

char c[3]相当与三个char;

3.3训练三:

1,double a ( 08);
2,char b (8
9);
3,int c (12~16);

3.4训练四:

对齐规则4: 如果嵌套了结构体的情况,嵌套的结构体成员对齐到自己的成员中最大对齐数的整数倍处,结构体总大小是所有最大对齐数(含嵌套结构体中成员的对齐数)的整数倍。

#include<stdio.h>
struct stu1
{
    char a;
    int b;
};
struct stu2
{
    char a;
    struct stu1 s;
    int c;
};
int main()
{
    printf("%zd\n", sizeof(struct stu2));
    return 0;
}  

stu1的总大小是8;最大对齐数是4;
分析stu2:
char a(01),struct stu1 s(412);int c(12~16);
struct的最大对齐数是4,嵌套的结构体成员对齐到自己的成员中最大对齐数的整数倍处,这里也就是对齐4;

四.为什么结构体不连续存储

  1. 平台原因 (移植原因):
    不是所有的硬件平台都能访问任意地址上的任意数据的;某些硬件平台只能在某些地址处取某些特定类型的数据,否则抛出硬件异常。
  2. 性能原因:
    数据结构(尤其是栈)应该尽可能地在自然边界上对齐。原因在于,为了访问未对齐的内存,处理器需要作两次内存访问;而对齐的内存访问仅需要⼀次访问。假设一个处理器总是从内存中取8个字节,则地址必须是8的倍数。如果我们能保证将所有的double类型的数据的地址都对齐成8的倍数,那么就可以用一个内存操作来读或者写值了。否则,我们可能需要执行两次内存访问,因为对象可能被分放在两个8字节内存块中。
    为了节省空间,我们可以尽可能把小类型(char)的放一起。

五.修改编译器的默认对齐数

#include<stdio.h>
#pragma pack(1)        //修改编译器默认对齐数为1;
struct stu
{
    char a;
    int b;
};
#pragma pack()         //取消默认对齐数为1;
int main()
{
    printf("%zd\n", sizeof(struct stu));
    return 0;
}  

六.结构体传参

6.1传值:

str是s的临时拷贝,但是arr[100],形参会浪费大量空间。

6.2传址:

6.3访问结构体里的内容:

© 2023 北京元石科技有限公司 ◎ 京公网安备 11010802042949号