• 自定义类型:结构体,声明,变量初始化,结构体内存对齐。


    <1>主页:C语言的前男友

    📃<2>知识讲解:结构体,声明,变量初始化,结构体内存对齐

    🔥<3>创作者:C语言的前男友

    ☂️<4>开发环境:Visual Studio 2022

    💬<5>前言:关于结构体我们已经不陌生了,今天我们就来系统的聊一聊结构体的声明,初始化,结构体占的内存大小。

    目录

    🍂一.结构体

    🍂二.特殊的结构体声明

    🍂三.结构体自引用

    🍂四.注意:

    🍂五.结构体的定义和初始化

    🍂六.结构体内存对齐

    🍃(1)例一:

    🍃(2)例二:

    🌕1.题目:

    🌕2.图解:

    🌕​3.运行效果:

    🍃(3)例三:

    🌕1.题目:

    🌕2.图解:

     🍂七、为什么存在内存对齐

    🍃(1) 平台原因(移植原因):

    🍃(2) 性能原因:

    🍃总体来说:

    🍂.修改默认对齐数

    🍃例一:

     🍃例二:

    🍂最后:

    不操千曲而后晓声,观千剑而后识器。


    一.结构体

    1. //结构体声明
    2. struct Stu
    3. {
    4. char name[20];//名字
    5. int age;//年龄
    6. char sex[5];//性别
    7. char id[20];//学号
    8. };

    二.特殊的结构体声明

      在声明结构的时候,可以不完全的声明——匿名结构体类型。

    1. //匿名结构体类型
    2. struct
    3. {
    4. int a;
    5. char b;
    6. float c;
    7. }x;
    8. struct
    9. {
    10. int a;
    11. char b;
    12. float c;
    13. }a[20], *p;

     上面的两个结构在声明的时候省略掉了结构体标签。

    那么问题来了?在上面代码的基础上,下面的代码合法吗?

    p = &x;

     注意:编译器会把上面的两个声明当成完全不同的两个类型。所以是非法的。

    三.结构体自引用

      在结构中包含一个类型为该结构本身的成员是否可以呢?

    1. struct Node
    2. {
    3. int data;
    4. struct Node next;
    5. };
    6. 可行否?如果可以,那sizeof(struct Node)是多少?

    这里显然是不行的。但是是不是就意味着就不行了呢?还是有办法实现自引用的。

    1. 正确的结构体自引用方式
    2. struct Node
    3. {
    4. int data;
    5. struct Node* next;
    6. };

    四.注意:

    1. 如果加上 typedef 是不是就可以这样实现结构体自引用呢?
    2. typedef struct
    3. {
    4. int data;
    5. Node* next;
    6. }Node;
    7. 这样写代码是不行的
    8. 解决方案:
    9. typedef struct Node
    10. {
    11. int data;
    12. struct Node* next;
    13. }Node;

    五.结构体的定义和初始化

    1. struct Point
    2. {
    3. int x;
    4. int y;
    5. }p1; //声明类型的同时定义变量p1
    6. struct Point p2; //定义结构体变量p2
    7. //初始化:定义变量的同时赋初值。
    8. struct Point p3 = {x, y};
    9. struct Stu     //类型声明
    10. {
    11. char name[15];//名字
    12. int age;    //年龄
    13. };
    14. struct Stu s = {"zhangsan", 20};//初始化
    15. struct Node
    16. {
    17. int data;
    18. struct Point p;
    19. struct Node* next;
    20. }n1 = {10, {4,5}, NULL}; //结构体嵌套初始化
    21. struct Node n2 = {20, {5, 6}, NULL};//结构体嵌套初始化
    1. struct Stu
    2. {
    3. char name[20];
    4. int age;
    5. char ID[20];
    6. };
    7. int main()
    8. {
    9. 按照结构体顺序初始化变量
    10. struct Stu stu2 = { "ikun",20,"JNTM123" };
    11. 自定义顺序初始化变量
    12. struct Stu stu1 = { .ID = "CZU123",.age = 19,.name = "张三" };
    13. return 0;
    14. }

    六.结构体内存对齐

    我们已经掌握了结构体的基本使用了,那么一个结构体到底时多大呢?到底怎么计算呢?

    1. struct S1
    2. {
    3. char c1;
    4. int i;
    5. char c2;
    6. };
    7. struct S2
    8. {
    9. char c1;
    10. char c2;
    11. int i;
    12. };
    13. int main()
    14. {
    15. printf("%d\n", sizeof(struct S1));
    16. printf("%d\n", sizeof(struct S2));
    17. return 0;
    18. }

    大家可以猜一下结果

     结果是不是出乎大家的意料了,虽然结构的成员都是一样的,但是结构的大小却不一样。

    到底是为什么造成这样的结果呢?其实结构体的大小可不是简单的结构体成员大小加在一起的。

    而是有一种内存对齐的规则。

    结构体的对齐规则:
    1. 第一个成员在与结构体变量偏移量为0的地址处。
    2. 其他成员变量要对齐到某个数字(对齐数)的整数倍的地址处。
    对齐数 = 编译器默认的一个对齐数 与 该成员大小的较小值。(VS中默认的值为8)

    3. 结构体总大小为最大对齐数(每个成员变量都有一个对齐数)的整数倍。
    4. 如果嵌套了结构体的情况,嵌套的结构体对齐到自己的最大对齐数的整数倍处,结构体的整
    体大小就是所有最大对齐数(含嵌套结构体的对齐数)的整数倍。

    下面我们就对着这个规则来做几个例子:

    (1)例一:

    1. struct S1
    2. {
    3. char c1;
    4. int a;
    5. char c2;
    6. };
    7. 求结构体s1的大小

    运行效果:

    (2)例二:

    1.题目:

    1. struct S2
    2. {
    3. char c1;
    4. char c2;
    5. int i;
    6. };
    7. 求结构体 S2 的大小

    2.图解:

    3.运行效果:

    (3)例三:

    1.题目:

    1. struct S1
    2. {
    3. char c1;
    4. int a;
    5. char c2;
    6. };
    7. struct S2
    8. {
    9. char c1;
    10. struct S1 S;
    11. char c2;
    12. int i;
    13. };
    14. //求结构体S2的大小

    2.图解:

    运行结果:

     七、为什么存在内存对齐

    (1) 平台原因(移植原因):


    不是所有的硬件平台都能访问任意地址上的任意数据的;某些硬件平台只能在某些地址处取某些特
    定类型的数据,否则抛出硬件异常。

    (2) 性能原因:


    数据结构(尤其是栈)应该尽可能地在自然边界上对齐。
    原因在于,为了访问未对齐的内存,处理器需要作两次内存访问;而对齐的内存访问仅需要一次访
    问。

    总体来说:


    结构体的内存对齐是拿空间来换取时间的做法。
    那在设计结构体的时候,我们既要满足对齐,又要节省空间,如何做到:
    让占用空间小的成员尽量集中在一起。

    例如:

    1. struct S1
    2. {
    3. char c1;
    4. int i;
    5. char c2;
    6. };
    7. struct S2
    8. {
    9. char c1;
    10. char c2;
    11. int i;
    12. };

     s2就比s1更节省空间。

    四.修改默认对齐数

    #pragma pack();

    例一:

    1. #include
    2. #pragma pack(8)//设置默认对齐数为8
    3. struct S1
    4. {
    5. char c1;
    6. int i;
    7. char c2;
    8. };
    9. #pragma pack()//取消设置的默认对齐数,还原为默认
    10. #pragma pack(1)//设置默认对齐数为1
    11. struct S2
    12. {
    13. char c1;
    14. int i;
    15. char c2;
    16. };
    17. #pragma pack()//取消设置的默认对齐数,还原为默认
    18. int main()
    19. {
    20. //输出的结果是什么?
    21. printf("%d\n", sizeof(struct S1));
    22. printf("%d\n", sizeof(struct S2));
    23. return 0;
    24. }

    运行结果:

     例二:

    1. #include
    2. #pragma pack(1)//设置默认对齐数为8
    3. struct S1
    4. {
    5. char c1;
    6. int i;
    7. char c2;
    8. };
    9. #pragma pack()//取消设置的默认对齐数,还原为默认
    10. #pragma pack(1)//设置默认对齐数为1
    11. struct S2
    12. {
    13. char c1;
    14. int i;
    15. char c2;
    16. };
    17. #pragma pack()//取消设置的默认对齐数,还原为默认
    18. int main()
    19. {
    20. //输出的结果是什么?
    21. printf("%d\n", sizeof(struct S1));
    22. printf("%d\n", sizeof(struct S2));
    23. return 0;
    24. }

     运行结果:

    最后:

    不操千曲而后晓声,观千剑而后识器。

     

     

  • 相关阅读:
    TechEmpower 21轮Web框架 性能评测 -- C# 的性能 和 Rust、C++并驾齐驱
    Mysql 查询优化 - Explain 详解(下)
    二叉树的详细实现(含递归展开图)
    python爬虫语法
    EVA: Visual Representation Fantasies from BAAI
    初探 Linux Cgroups:资源控制的奇妙世界
    软件测试基本概念
    Pytorch中Conv2d和ConvTranspose2d参数计算公式
    hadoop03--flume集群搭建
    车间作业分析重点分析的内容是哪些?
  • 原文地址:https://blog.csdn.net/qq_63943454/article/details/126918863