C语言第三十弹---自定义类型:结构体(上)

个人主页: 熬夜学编程的小林

💗系列专栏: 【C语言详解】 【数据结构详解】

结构体

1、结构体类型的声明

1.1、结构体回顾

1.1.1、结构的声明

1.1.2、结构体变量的创建和初始化

1.2、结构的特殊声明

1.3、结构的自引用

2、结构体内存对齐

2.1、对齐规则

​编辑

总结


1、结构体类型的声明

前面我们在学习操作符的时候,已经学习了结构体的知识,这里稍微复习⼀下。

1.1、结构体回顾

结构是⼀些值的集合,这些值称为成员变量。结构的每个成员可以是不同类型的变量。

1.1.1、结构的声明

struct tag
{
 member-list;//成员列表
}variable-list;//全局变量列表
例如描述⼀个学生:
struct Stu
{
 char name[20];//名字
 int age;//年龄
 char sex[5];//性别
 char id[20];//学号
}; //分号不能丢

1.1.2、结构体变量的创建和初始化

#include <stdio.h>
struct Stu
{
 char name[20];//名字
 int age;//年龄
 char sex[5];//性别
 char id[20];//学号
};
int main()
{
 //按照结构体成员的顺序初始化
 struct Stu s = { "张三", 20, "男", "20230818001" };
 printf("name: %s\n", s.name);
 printf("age : %d\n", s.age);
 printf("sex : %s\n", s.sex);
 printf("id : %s\n", s.id);
 
 //按照指定的顺序初始化
 struct Stu s2 = { .age = 18, .name = "lisi", .id = "20230818002", .sex = "⼥
 printf("name: %s\n", s2.name);
 printf("age : %d\n", s2.age);
 printf("sex : %s\n", s2.sex);
 printf("id : %s\n", s2.id);
 return 0;
}

1.2、结构的特殊声明

在声明结构的时候,可以不完全的声明。
比如:
//匿名结构体类型
struct
{
 int a;
 char b;
 float c;
 }x;
struct
{
 int a;
 char b;
 float c;
}a[20], *p;
上面的两个结构在声明的时候省略掉了结构体标签(tag)。
那么问题来了?
//在上⾯代码的基础上,下⾯的代码合法吗?
p = &x;
警告:
编译器会把上面的两个声明当成完全不同的两个类型,所以是非法的。
匿名的结构体类型,如果没有对结构体类型重命名的话,基本上只能使用⼀次。

1.3、结构的自引用

在结构中包含⼀个类型为该结构本身的成员是否可以呢?
比如,定义⼀个链表的节点
struct Node
{
 int data;
 struct Node next;
};
上述代码正确吗?如果正确,那 sizeof(struct Node) 是多少?
仔细分析,其实是不行的,因为⼀个结构体中再包含⼀个同类型的结构体变量,这样结构体变量的大小就会无穷的大,是不合理的。
正确的自引用方式:
struct Node
{
 int data;
 struct Node* next;
};
在结构体自引用使用的过程中,夹杂了 typedef 对匿名结构体类型重命名,也容易引入问题,看看
下面的代码,可行吗?
typedef struct
{
 int data;
 Node* next;
}Node;
答案是不行的,因为 Node是对前面的匿名结构体类型的重命名产生的,但是在匿名结构体内部提前使用Node类型来创建成员变量,这是不行的。
解决方案如下:定义结构体不要使用匿名结构体了
typedef struct Node
{
 int data;
 struct Node* next;
}Node;

2、结构体内存对齐

我们已经掌握了结构体的基本使用了。
现在我们深入讨论⼀个问题:计算结构体的大小。
这也是⼀个特别热门的考点: 结构体内存对齐

2.1、对齐规则

首先得掌握结构体的对齐规则:
1. 结构体的第⼀个成员对齐到和结构体变量起始位置偏移量为0的地址处
2. 其他成员变量要对齐到某个数字(对齐数)的整数倍的地址处。
对齐数 = 编译器默认的⼀个对齐数 与 该成员变量大小的较小值。
3. 结构体总大小为最大对齐数(结构体中每个成员变量都有⼀个对齐数,所有对齐数中最大的)的整数倍。
4. 如果嵌套了结构体的情况,嵌套的结构体成员对齐到自己的成员中最大对齐数的整数倍处,结构体的整体大小就是所有最大对齐数(含嵌套结构体中成员的对齐数)的整数倍。
- VS 中默认的值为 8
- Linux中 gcc 没有默认对齐数,对齐数就是成员自身的大小
//练习1
struct S1
{
 char c1;
 int i;
 char c2;
};
printf("%d\n", sizeof(struct S1));

1、根据结构体对齐的规则,结构体的第⼀个成员对齐到和结构体变量起始位置偏移量为0的地址处,即上图第一个绿色框。

2、其他成员变量要对齐到某个数字(对齐数)的整数倍的地址处。对齐数 = 编译器默认的⼀个对齐数 与 该成员变量大小的较小值。int 类型对齐数为4,VS默认对齐数为8,因此对齐到4的整数倍,即上图橙色方框。

3、其他成员变量要对齐到某个数字(对齐数)的整数倍的地址处。char 类型对齐数为1,VS默认对齐数为8,因此对齐到1的整数倍,即上图蓝色方框。

4、结构体总大小为最大对齐数(结构体中每个成员变量都有⼀个对齐数,所有对齐数中最大的)的整数倍。最大对齐数为4,此时结构体大小9不是4的倍数,因此会再在多浪费3个字节(上图x是浪费的空间),因此大小为12字节。

//练习2
struct S2
{
 char c1;
 char c2;
 int i;
};
printf("%d\n", sizeof(struct S2));

1、根据结构体对齐的规则,结构体的第⼀个成员对齐到和结构体变量起始位置偏移量为0的地址处,即上图第一个绿色框。

2、其他成员变量要对齐到某个数字(对齐数)的整数倍的地址处。对齐数 = 编译器默认的⼀个对齐数 与 该成员变量大小的较小值。char 类型对齐数为1,VS默认对齐数为8,因此对齐到1的整数倍,即上图橙色方框。

3、其他成员变量要对齐到某个数字(对齐数)的整数倍的地址处。char 类型对齐数为1,VS默认对齐数为8,因此对齐到1的整数倍,即上图蓝色方框。

4、结构体总大小为最大对齐数(结构体中每个成员变量都有⼀个对齐数,所有对齐数中最大的)的整数倍。最大对齐数为4,此时结构体大小8是4的倍数,因此大小为8字节。

//练习3
struct S3
{
 double d;
 char c;
 int i;
};
printf("%d\n", sizeof(struct S3));

1、根据结构体对齐的规则,结构体的第⼀个成员对齐到和结构体变量起始位置偏移量为0的地址处,即上图第一个绿色框。

2、其他成员变量要对齐到某个数字(对齐数)的整数倍的地址处。对齐数 = 编译器默认的⼀个对齐数 与 该成员变量大小的较小值。char 类型对齐数为1,VS默认对齐数为8,因此对齐到1的整数倍,即上图橙色方框。

3、其他成员变量要对齐到某个数字(对齐数)的整数倍的地址处。int 类型对齐数为4,VS默认对齐数为8,因此对齐到4的整数倍,即上图蓝色方框。

4、结构体总大小为最大对齐数(结构体中每个成员变量都有⼀个对齐数,所有对齐数中最大的)的整数倍。最大对齐数为8,此时结构体大小16是8的倍数,因此大小为16字节。

//练习4-结构体嵌套问题
struct S4
{
 char c1;
 struct S3 s3;
 double d;
};
printf("%d\n", sizeof(struct S4));

1、根据结构体对齐的规则,结构体的第⼀个成员对齐到和结构体变量起始位置偏移量为0的地址处,即上图第一个绿色框。

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

其他成员变量要对齐到某个数字(对齐数)的整数倍的地址处。对齐数 = 编译器默认的⼀个对齐数 与 该成员变量大小的较小值。double 类型对齐数为8,VS默认对齐数为8,因此对齐到8的整数倍,即上图橙色方框。

3、其他成员变量要对齐到某个数字(对齐数)的整数倍的地址处。char 类型对齐数为1,VS默认对齐数为8,因此对齐到1的整数倍,即上图蓝色方框。

4、其他成员变量要对齐到某个数字(对齐数)的整数倍的地址处。int 类型对齐数为4,VS默认对齐数为8,因此对齐到4的整数倍,即上图绿色方框。

5、其他成员变量要对齐到某个数字(对齐数)的整数倍的地址处。double 类型对齐数为8,VS默认对齐数为8,因此对齐到8的整数倍,即上图橙色方框。

6、结构体的整体大小就是所有最大对齐数(含嵌套结构体中成员的对齐数)的整数倍。最大对齐数为8,此时结构体大小32是8的倍数,因此大小为32字节。

总结


本篇博客就结束啦,谢谢大家的观看,如果公主少年们有好的建议可以留言喔,谢谢大家啦!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/409166.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

【Java多线程】对线程池的理解并模拟实现线程池

目录 1、池 1.1、线程池 2、ThreadPoolExecutor 线程池类 3、Executors 工厂类 4、模拟实现线程池 1、池 “池”这个概念见到非常多&#xff0c;例如常量池、数据库连接池、线程池、进程池、内存池。 所谓“池”的概念就是&#xff1a;&#xff08;提高效率&#xff09; 1…

acwing算法学习笔记 ------ 双链表

1、定义 这里可以做一个投机取巧&#xff0c;我们不再像单链表去用head去存头和尾&#xff0c;直接让r[0] 1,l[1] 0; idx 2.进行初始化&#xff0c; 解释一下l[N] 和 r[N] l[N]:是表示指向左面下一个节点下标&#xff0c; r[N]:表示指向下一个节点的下标。大家不用担心i…

基础复习(IDA调试器)

1.选择IDA调试后端 在顶部有一个下拉菜单&#xff0c;选择调试器后端位置 很多用户实际上使用的是Windows版本的IDA&#xff0c;该IDA可以直接调试Windows下32bit和64bit的程序 2.本地调试启动方法 载入IDA后&#xff0c;程序实际上在对程序内置的一个字符串进行base64解码…

【03】逆序数组

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 目录 一、逆序函数是什么&#xff1f; 二、逆序函数原码 1.直接逆序 2.创建临时数组逆序 三、结言 &#x1f4a5;一、逆序函数是什么&#xff1f; 示例&#xff1a;输入1 4 …

综合服务 IntServ

目录 综合服务 IntServ IntServ 定义的两类服务 IntServ 的四个组成部分 流 (flow) 资源预留协议 RSVP RSVP 协议的工作原理 IntServ 体系结构在路由器中的实现 综合服务 IntServ 体系结构存在的主要问题 综合服务 IntServ 综合服务 IntServ (Integrated Services) 可…

Linux进程间通信详解

文章目录 进程间通信进程间通信介绍一. 管道1. 管道的基本概念2. 管道的创建①. 匿名管道②. 命名管道匿名与命名管道的区别 3. 删除管道4. 管道的4种特殊情况 二、system V1. 共享内存( shm )shm基本概念shm函数 2. 消息队列( msg )msg基本概念msg函数 3. 信号量sem函数 三、指…

【GameFramework框架内置模块】3、数据表(Data Table)

推荐阅读 CSDN主页GitHub开源地址Unity3D插件分享简书地址 大家好&#xff0c;我是佛系工程师☆恬静的小魔龙☆&#xff0c;不定时更新Unity开发技巧&#xff0c;觉得有用记得一键三连哦。 一、前言 【GameFramework框架】系列教程目录&#xff1a; https://blog.csdn.net/q7…

你要不要搞副业

最近看到了几个网友关于年轻人要不要搞副业的一点讨论&#xff0c;学习到了很多。整理分享如下&#xff1a; plantegg 你要不要搞副业&#xff1f; 最近网上看到很多讨论搞副业和远程工作的&#xff0c;我也说点自己的经验看法 当然这完全是出于个人认知肯定不是完全对的、也…

第十三章 Linux——备份与恢复

第十三章 Linux——备份与恢复 基本介绍安装dump和restore使用dump完成备份dump语法说明dump应用案例1dump应用案例2dump-w查看备份时间文件备份文件或者目录备注 使用restore基本语法基本介绍restore基本语法应用案例1应用案例2应用案例3应用案例4 基本介绍 实体机无法做快照…

wcf 简单实践 数据绑定 数据校验

1.概要 1.1 说明 数据校验&#xff0c;如果数据不合适&#xff0c;有提示。 1.2 要点 class User : IDataErrorInfothis.DataContext user;<Window.Resources><Setter Property"ToolTip" Value"{Binding RelativeSource{RelativeSource Self},Pat…

【电子通识】认识FMEA(失效模式和影响分析)

FMEA是Failure Mode and Effect Analysis的英文缩写&#xff0c;中文名称为失效模式和影响分析。主要应用于航空航天、食品、汽车和核电等行业。 FMEA讨论的是事先策划以及执行措施&#xff0c;预防问题的发生或控制问题的发展&#xff0c;降低设计和过程的风险。由于问题还没…

AI:134-基于深度学习的社交媒体图像内容分析

🚀点击这里跳转到本专栏,可查阅专栏顶置最新的指南宝典~ 🎉🎊🎉 你的技术旅程将在这里启航! 从基础到实践,深入学习。无论你是初学者还是经验丰富的老手,对于本专栏案例和项目实践都有参考学习意义。 ✨✨✨ 每一个案例都附带有在本地跑过的关键代码,详细讲解供…

m估计及其c++简单实现

文章目录 什么是m估计怎么求解m估计呢&#xff1f;Huber函数时的线性m估计 什么是m估计 自20世纪60年代稳健统计建立以来&#xff0c;在国内外众多学者的研究之下&#xff0c;诞生了一系列稳健统计重要理论和成果。其中最主要且广泛使用的稳健统计有以下三类&#xff1a; L-e…

linux之前后端项目部署与发布

目录 前言 简介 一、安装Nginx 二、后端部署 2.1多个tomcat负载均衡 2.2 负载均衡 2.3 后端项目部署 三、前端部署 1.解压前端 2.Nginx配置文件修改 3.IP域名映射 4.重启Nginx服务 前言 上篇博主已经讲解过了单机项目的部署linux之JAVA环境配置JDK&Tomcat&a…

【elasticsearch】搜索结果处理

搜索结果处理 排序 elasticsearch支持对搜索结果排序&#xff0c;默认是根据相关度算分&#xff08;_score&#xff09;来排序。可以排序字段类型有&#xff1a;keyword类型、数值类型、地理坐标类型、日期类型等。 GET /indexName/_search {"query":{"match_a…

java 内存模型

程序计数器 线程私有主要字节码解释器通过读取程序计数器来选取下一条需要执行的指令&#xff0c;比如分支&#xff0c;循环&#xff0c;跳转和异常处理如果执行的是java 方法&#xff0c;那么程序计数器记录的时候虚拟机字节码指令的地址&#xff0c;如果执行的是native 方法…

【FreeRTOS】任务管理

一、任务管理介绍 1.任务状态 1&#xff09;调度器切换任务调度 2&#xff09;任务是一个死循环&#xff0c;如果想要停止这个任务则会调用在函数最后写的delete函数进行自杀 1.就绪态 1&#xff09;已经具备执行的能力&#xff0c;只等待调度器进行调度。 2&#xff09;新创建…

Linux系统前后端分离项目

目录 一.jdk安装 二.tomcat安装 三.MySQL安装 四.nginx安装 五.Nginx负载均衡tomcat 六.前端部署 一.jdk安装 1. 上传jdk安装包 jdk-8u151-linux-x64.tar.gz 进入opt目录&#xff0c;将安装包拖进去 2. 解压安装包 这里需要解压到usr/local目录下&#xff0c;在这里新建一个…

python程序设计基础:异常处理结构与程序调试、测试

第八章&#xff1a;异常处理结构与程序调试、测试 简单地说,异常是指程序运行时引发的错误,引发错误的原因有很多例如除零、下标越界、文件不存在、网络异常、类型错误、名字错误、字典键错误、磁盘空间不足,等等。 如果这些错误得不到正确的处理将会导致程序终止运行,而合理…

HuggingFists系统功能介绍(1)--系统概述

HuggingFists是一款低代码AI应用工具&#xff0c;力图发展为LangChain的低代码平替工具。HuggingFists发起于数由科技的Sengee数据科学计算框架&#xff0c;因此其界面风格继承了数据科学工具的很多特征。有别于完全基于LangChain衍生出的低代码工具Flowise&#xff0c;其风格更…