• C语言自定义类型-结构体


    一,结构体声明

    C语言中为我们准备了许多现成的数据类型例如:
    int
    short
    float
    double
    char
    long
    long long

    等等. . . . . .
    但是我们描述一些复杂的事物,光靠上述的数据类型是描述不清的,例如,我们描述一个大学生,可以描述他的身高,年龄,学号等等。

    那么结构体类型是如何生命的呢?

    struct Stu
    {
    	int hight;
    	char num[20];
    	int age;
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    注意: 大括号后面的分号不能丢哦。

    二,特殊结构体声明

    结构体在声明的时候,可以不完全声明,像下面一样。

    struct 
    {
    	int a;
    	int b;
    	char c;
    }x;
    struct
    {
    	int a;
    	int b;
    	char c;
    }b,*p;
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    但是,当不完全声明的时候,我们可以将x变量的地址,存到p指针当中吗?

    p = &x;
    
    • 1

    在这里插入图片描述
    可以看到,是不可以的,虽然两个结构体变量的各个成员相同,但是编译器仍然认为这是两个不同的结构体。

    并且不完全声明的结构体,不能再用来定义新的变量例如下面

    struct
    {
    	int a;
    	int b;
    	char c;
    };
    int main()
    {
    	
    	struct x;
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    在这里插入图片描述
    不完全声明的结构体如果想定义变量,只能在声明的同时定义变量例如下面

    struct 
    {
    	int a;
    	int b;
    	char c;
    }x;
    struct
    {
    	int a;
    	int b;
    	char c;
    }b,*p;
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    三,结构体自引用

    我们先看几行代码

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

    这样的代码,可行吗?

    如果可行的话?那sizeof(struct Node)是多少呢?

    我们可以看出结构体在不断的嵌套你中有我,我中有你,不断地这样下去,想必肯定是错误的。
    实现结构体自引用的正确的方法如下:

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

    四,结构体变量的定义与初始化

    1,在结构体声明的同时初始化

    struct stu
    {
    	int age;
    	int hight;
    	char num[20];
    }ss1;
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    2,在结构体声明后定义

    int main()
    {
    	struct stu ss2;
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5

    3,按照结构体成员顺序初始化

    int main()
    {
    	struct stu ss2 = { 19,182,"47385782" };
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5

    4,不按照成员顺序初始化

    int main()
    {
    	
    	struct stu ss3 = { .hight = 183,.num = "42345235",.age = 22 };
    
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    五,结构体内存对齐

    我们先看一段代码

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

    你想一下,答案是什么呢?

    在这里插入图片描述

    可能有人会想,这两个的答案不应该都是6吗?
    首先,我们先介绍一个宏:offsetof
    在这里插入图片描述
    他的返回值是,结构体成员相对于结构体变量的偏移量
    例如:

    struct a
    {
    	char c1;
    	int i;
    	char c2;
    };
    #include
    int main()
    {
    	printf("%d\n", offsetof(struct a, c1));
    	printf("%d\n", offsetof(struct a,i));
    	printf("%d\n", offsetof(struct a, c2));
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    在这里插入图片描述
    在这里插入图片描述

    可以看到根据offsetof的返回值,画出的图是上述那样的,结构体变量的大小应该是9个字节大小啊。
    下面我们就来介绍一下,结构体内存对齐的规则

    规则:
    (1)结构体变量的第一个成员永远放在相对于结构体变量偏移量为0 的位置。
    (2)其他成员放在偏移量为其对齐数整数倍的位置处
    对齐数:为默认对齐数和结构体成员大小的较小值
    vs:默认对齐数 8
    Linux:不设置默认对齐数
    (3)结构体变量的总大小为最大对齐数的整数倍
    (4)如果嵌套了结构体的情况,嵌套的结构体对齐到自己最大对齐数的整数倍处,
    结构体的总大小为最大对齐数的整数倍(包括嵌套结构体中的对齐数)

    下面,针对第四条写一段代码

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

    那么这个结构体的总大小是多少呢?
    在这里插入图片描述
    答案是24,你想对了吗?

    六,修改默认对齐数

    可以用#pragma pack()来改变默认对齐数

    #pragma pack(4)//改变默认对齐数为4
     
    #pragma pack()//回复默认对齐数
    
    • 1
    • 2
    • 3

    七,结构体传参

    传参有两种方式
    (1)传值

    struct STU
    {
    	int age;
    	int hight;
    	char num[20];
    };
    void print1(struct STU s)
    {
    	printf("%d\n", s.age);
    	printf("%d\n", s.hight);
    	printf("%s\n", s.num);
    
    }
    int main()
    {
    	struct STU ss1 = { 19,182,"154235265" };
    	print1(ss1);
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    (2)传址

    void print2(struct STU* s)
    {
    	printf("%d\n", s->age);
    	printf("%d\n", s->hight);
    	printf("%s\n", s->num);
    }
    int main()
    {
    	struct STU ss1 = { 19,182,"154235265" };
    	print1(ss1);
    	print2(&ss1);
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    我们知道形参是实参的一份临时拷贝,会开辟新的空间,如果当我们的结构体较大的时候,利用传值的传参方式会很浪费空间,所以结构体传参的时候采用传址调用比较好。

  • 相关阅读:
    Allure使用手册
    (项目笔记)OpenCV目标检测程序
    【ElasticSearch】基于Docker 部署 ElasticSearch 和 Kibana,使用 Kibana 操作索引库,以及实现对文档的增删改查
    云端golang开发,无需本地配置,能上网就能开发和运行
    go简洁架构实践
    没用的知识增加了,尝试用文心实现褒义词贬义词快速分类
    arduino离线安装esp32开发板支持包
    SQL Server全套教程(基于SQL语句----预览版)
    在线客服系统源码 聊天记录实时保存 附带完整的搭建教程
    @Slf4j打印异常信息打印出堆栈信息
  • 原文地址:https://blog.csdn.net/Djsnxbjans/article/details/126911152