• C语言《认识结构体》---重点解析内存对齐


    结构体

    一、结构体概念

    1、结构体是由一批数据组合而成的结构型数据。而这些数据又是成员变量,这些变量可以是不同的类型。
    2、结构体通常用来表示类型不同但是又相关的若干数据。
    3、结构体类型不是又系统定义好的,需要我们自己去定义。C语言只是提供了关键字struct来标识所定义的结构体类型。

    二、结构体声明

    我们定义一个结构体类型,描述一个书的基本信息的时候,有编号,书名,作者,价格等。这些视为成员变量如下:

    struct Book
    {
    int code;//编号
    char name[20];//书名
    char author[20];//作者
    double price;//价格
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    或者描述一个人,有名字,身份证,性别,年龄如下:

    struct Pepole
    {
    char name[20];//名字
    int ID;//身份证号
    char sex[10];//性别
    int age;//年龄
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    注意括号最后的分号不能少些。

    (1)、特殊声明

    特殊声明就是在声明结构的时候,可以做不完全声明,可以把名字省略掉,即匿名结构体类型。看如下:

    struct 
    {
    int code;//编号
    char name[20];//书名
    char author[20];//作者
    double price;//价格
    }B;
    struct 
    {
    char name[20];//名字
    int ID;//身份证号
    char sex[10];//性别
    int age;//年龄
    }*p;
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    注意不能这样写,*p=&x,把x的地址放在指针变量p中,编译器会报警告,它会把上面的两个声明当成完全不同的两个类型。

    三、结构体变量的定义和初始化

    (1)、定义初始化例子1

    struct Grade
    {
        int a;
        int b;
    }x1;
    struct Grade x2;
    struct Grade x3={a,b};
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    1、如上,先定义一个成绩结构类型,x1表示声明类型的同时定义变量x1。
    2、x2表示定义结构体变量x2。
    3、x3表示,初始化,定义变量的同时赋初值。

    (2)、定义初始化例子2:

    struct Pepole//类型声明
    {
    char name[20];//名字
    char sex[10];//性别
    int age;//年龄
    };
    struct Stu s = {"lihua","男""17"};//初始化赋初值
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    (3)、结构嵌套

    在结构体里面再套上一个结构体类型。

    struct Book
    {
    char author[20];//作者
    double price;//价格
    };
    struct Pepole
    {
    char name[20];//名字
    char sex[10];//性别
    int age;//年龄
    }p;
    对上面进行嵌套如下:
    struct Book
    {
    char name[20];//书名
    struct Pepole p;
    double price;//价格
    }n={"C语言"{"lihua",{"男",17},66};//结构体嵌套初始化赋初值
    struct Book m={"C语言"{"lihua",{"男",17},66};//另一种结构体嵌套初始化赋初值
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    以上的变量都是全局变量。也可以放在main 函数里面创建局部变量,更加安全。

    四、结构体自引用

    (1)、 结构体自引用概念

    结构体自引用是在结构体里面,创建一个指向自身类型结构体的指针

    (2)、不使用typedef时

    struct Grade
    {
    int a;
    struct Grade next;
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5

    以上声明是非法的,它是一个无限循环,成员next是一个结构体,内部还会有成员是结构体,构成一个死循环,当然长度也就无法判断。
    需要做出更正:

    struct Grade
    {
    int a;
    struct Grade *next;
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5

    让它变为指针,因为指针长度是确定的,它是一个地址,机器能判断它是4或8字节。因此编译器能判断它的长度。

    (3)、使用typedef时

    typedef struct
    {
    int data;
    Node* next;
    }Node;
    
    • 1
    • 2
    • 3
    • 4
    • 5

    以上是错误的,因为next虽然是一个指针,但这里的Node并没有定义。
    typedef是为结构体创建一个别名NODE,可是类型名的作用域是从语句的结尾开始的,在里面是无法使用的,因为没有定义。
    我们有三种解决方法:

    typedef struct Node//第一种
    {
       int data;
       struct Node* next;
    }Node;
    
    struct NODE;//第二种
    typedef struct NODE Node;
    struct NODE
    {
       int data:
       Node *next;
    }
    
    struct NODE
    {
         int data;
         struct NODE* next;
    }
    typedef struct NODE Node;
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21

    五、结构体内存对齐

    (1)、结构体内存对齐概念

    结构体内存对齐是指我们创建一个结构体变量时,会向内存申请所需的空间,用来存储结构体成员的内容。

    (2)、结构体内存对齐规则

    1、结构体的一个成员直接对齐到相对于结构体变量起始位置为0的偏移处。
    2、从第二个成员开始,要对齐到某个对齐数的整数倍的偏移处。
    3、对齐数:结构体成员自身大小和默认对齐数的较小值。
    4、默认对齐数大小:VS默认是8、linux环境上不设对齐数,也就是结构体成员自身大小。
    5、最后一步,结构体的总大小,必须是成员里面中最大对齐数的整数倍。

    (3)、普通结构体实战练习

    struct S2
    {
    	char c1;
    	int  c2;
    	short i;
    };
    int main()
    {
    	printf("%d\n", sizeof(struct S2));
    	return 0;
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    如果不按规则,我们会想当然会认为这个结构体的大小是7。看下结果:
    在这里插入图片描述

    然而并不是。如下看图解析:
    在这里插入图片描述
    1、我们先求出各个成员的对齐数,char是1,int是4,short是2(和VS对齐数8比取最小值),最大对齐数是4。
    2、按规则,第一个成员是char类型,对齐到相对于结构体变量起始位置为0的偏移处。
    2、第二个成员是int类型占4个字节,但下一个地址偏移处是1,不是它的整数倍,继续往下找,找到4,就从4开始填4个字节,直到7处停止,最后一个是short类型而下一个地址是从8开始,是2的倍数,填充到9停止。
    3、最后一步,此时已经有了10个字节,判断它是否是最大对齐数的整数倍,10不是4的倍数,就往下找到12,所以结构体的大小是12。

    (4)、结构体嵌套

    (一)、结构体嵌套概念及规则

    在结构体里面嵌套一个结构体,求此结构体的大小。
    规则:
    除上面规则外,加上一条:嵌套的结构体要对齐到自己最大的对齐数的整数倍处,结构体的整体大小就是所有最大对齐数(含嵌套结构体的对齐数)的整数倍。

    (二)、嵌套结构体实战练习

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

    如下看图解析:
    在这里插入图片描述

    1、求S4中各个成员的对齐数,char类型是1,因为是嵌套要求它的最大对齐数是4,d是8,所有最大对齐数是8。
    2、为了方便起见 我们从1开始(也可以把它想象成从0开始),char占1字节,直接填。
    3、第二个成员是嵌套结构体,要对齐到自己最大对齐数的整数倍处,从开始填12个字节,直到15停止,最后一个是double类型对齐数是8,并占8个字节,16符合条件,从它开始填到24。
    4、最后一步是判断填完之后是否是所有最大对齐数的整数倍,24是8的倍数符合条件结束。
    结果如下:
    在这里插入图片描述

    (5)、修改默认对齐数

    1、因结构体的字节对齐方式在不同的编译器中不一样,我们可以用预处理命令#pragma pack(n),可以将结构体的变量强制对到某个值为n的字节,这样也可以让我们节省空间。
    2、在用完这个命令之后,要在定义这个结构体后加一个取消自定义对齐的命令:#pragma pack()。
    如下例子:

    #include 
    #pragma pack(1)//设置默认对齐数为1
    struct S1
    {
    	char c1;
    	int m;
    	short i;
    };
    #pragma pack()//取消设置的默认对齐数,还原为默认
    int main()
    {
    	printf("%d\n", sizeof(struct S1));
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    上面按默认对齐算的是12,修改对齐数得到应是1。
    结果:
    在这里插入图片描述

  • 相关阅读:
    es中的写入速度优化
    移远通信EM060K系列LTE-A Cat 6模组完成全球认证覆盖
    数据库的简介
    改进Yolov5目标检测与单目测距 yolo速度测量-pyqt界面-yolo添加注意力机制
    Java集合知识点速查(JDK8)
    深入了解“注意力”和“变形金刚” -第1部分
    奶茶店冬天怎么提升销量 | 奶茶技术培训
    perspectiveTransform warpPerspective getPerspectiveTransform findHomography
    游戏中交通工具的设计
    【c++提高1】单调栈
  • 原文地址:https://blog.csdn.net/m0_59292239/article/details/126913987