【再识C进阶4】详细介绍自定义类型——结构体、枚举和联合

学习目标:

       在上一篇博客中,我们已经详细地学习了字符分类函数、字符转换函数和内存函数。那这一篇博客和上一篇博客的关系不是那么相连。

       这一篇博客主要介绍一下自定义类型,因为在解决实际问题时,由于世界上的因素有很多,我们需要建立不同的数据类型来描述这些变量,但是C语言本身创立的类型不是很多,所以需要我们用户自己根据需求进行创建,于是就有了这一篇博客!


学习内容:

通过上面的学习目标,我们可以列出要学习的内容:

  1. 结构体的相关知识
  2. 结构体的内存对齐
  3. 结构体实现位段
  4. 枚举的相关知识
  5. 联合的相关知识

一、结构体的相关知识

1.1 结构的基本知识

       通俗来讲,结构是一些值的集合,这些值称为结构成员读者们看到这句话是不是似曾相识啊,在前面的数组这一章,我们讲过数组的概念——是一些相同类型的值的集合。小编在这里提一嘴,是因为数组存放的值和今天我们讲述结构体存放的值是不一样的。结构体的成员可以是不同类型的变量,一定要区分清楚!

       因为结构体中,我们也可以放置相同类型的值的集合;而数组只能放置相同类型的值的集合。我们要好好利用结构体来解决实际生活中的问题。

1.2 结构体的声明

1.2.1 结构体的内容介绍

       在上图中,我们介绍了结构体类型中的各个位置的含义和概念,下面,我们来自己设计一个学生的结构体类型:

struct Student {char name[20];int age;char sex[5];double score;
};

1.2.2 结构体是如何创建变量 

       定义完结构体类型后,这就相当于又创建了一个数据类型,我们可以根据这个数据类型来创建一个或多个结构体变量,同时也可以创建一个结构体数组,这和基本数据类型大差不差。接下来,我们就来创建结构体变量:

1.2.3 利用typedef简化类型名字

       你们会不会因为结构体类型的名字比较长,而觉得很不方便,我们可以利用 typedef 来进行对类型的重命名:下面,我们来看一个例子:

1.3 特殊的声明

1.3.1 匿名结构体的概念和代码

       这个声明就像标题所说的一样,是有点特殊的,也是不常用的。我们在进行编写代码时,在设计结构体时,可以进行不完全的设计声明。因为编写结构体时没有给出相应的名字,所以这种设计的结构体叫做匿名结构体。具体例子我们看下面:

//匿名结构体类型
struct{int a;char b;float c;
}b;

1.3.2 匿名结构体和普通结构体的区别 

为了便于理解,我们在进行匿名结构体类型与普通结构体类型之间的区别(如下图)

1.3.3 有关匿名结构体的一道题目 

在了解完上面匿名结构体的概念后,我们来看这么一道题:

//匿名结构体类型
struct {int a;char b;float c;
}b;
struct {int a;char b;float c;
} *p;

上面两个结构体在声明的时候省略掉了结构体标签tag。那么请问:

//在上面的代码基础上,下面的代码合法吗?
p = &b;

答案: 

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

1.3.4 匿名结构体的使用场景

       说实话,这个匿名结构体的使用次数应该要很少,他的使用场景只有在使用一次之后就不在使用,之后永远不在使用。 

1.4 结构体的自引用(错误)

1.4.1 介绍结构体自引用

       在讲结构体之前,我们来了解一下数据结构。数据结构有:线性表、栈和队列、串(KMP)、数与二叉树、图、查找、排序。(在之后的笔记中,我也会详细地写出数据结构)。

       在数据结构中,我们线性表中的链表与结构体的自引用有一定的关系。正如标题所示那样,结构体的自引用是错误的,而真正正确的是链表的写法。

错误的写法:

正确的写法:
       在链表中,我们的数据不同于数组一样是连续的放在一起,而链表是将数据不连续的放在内存空间中,我们怎么找到下一个结点呢?在内存中,每一个数据都有一个自己的地址,我们如果将下一个结点的地址存在这一个结点中,就能找到下一个结点

1.4.2 结构体自引用的一些问题

问题:

typedef struct{int a;Node* n;
}Node;
//这样写代码,可以吗?

答案:

       显然是错误的,这是一个先有鸡还是先有蛋的问题。原因在于这个结构体在创立的时候,还没有来得及给其重命名,就已经有了重命名后的指针,所以是错误的。

改进方法:

typedef struct Node {int a;struct Node* next;
}Node;

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

       在标题为1.2.2中,我们讲述了如何创建结构体的全局变量和局部变量。那么接下来,我们来简单介绍一下初始化。说起初始化,想必大家都不陌生,在之前的课程中,我们就会对基本结构数据类型进行初始化。

第一种写法:

struct Point
{int x;int y;
}p1 = { 1,2 }; //声明类型的同时定义变量p1,并初始化p1

第二种写法:

//初始化:定义变量的同时赋初值。
int x = 20;
int y = 40;
struct Point p3 = { x, y };

第三种写法:

struct Stu        //类型声明
{char name[15];//名字int age;      //年龄
};
//我们可以使用下面这一种方法,不用按照顺序进行初始化
struct Stu s1 = { .age = 19, .name = "hskod" };

下面,我们来介绍一下结构体的嵌套使用

struct Node
{int data;struct Point p;struct Node* next;
}n1 = { 10, {4,5}, NULL }; //结构体嵌套初始化
struct Node n2 = { 20, {5, 6}, NULL };//结构体嵌套初始化

1.6 结构体内存对齐

1.6.1 引出offsetof

       在这之前,我们先来写一段前言用于激发读者思考!我们来看下面这两个结构体大小的对比,先来预告一波:结构体的内存对齐与结构体时候如何计算内存大小是有关。看下图:

struct Stu1 {int a;char c1;char c2;
};struct Stu2 {char c1;int a;char c2;
};

       在这两个结构体中,所有的变量都是一样的,只有变量的顺序是不一样的,这种顺序会造成结构体的内存大小是不同。为什么呢?这就和下面要介绍的内存对齐有关!

       为了便于更好地理解这一现象,我们来引出一个宏:offsetof。这个宏的作用是:此具有函数形式的宏返回数据结构或联合类型类型中成员成员的偏移值(以字节为单位)返回的值是类型为 size_t 的无符号整数值,具有指定成员与其结构开头之间的字节数

举个例子:

1.6.2 结构体的内存对齐讲解

首先得掌握结构体的对齐规则:
1. 第一个成员在与结构体变量偏移量为 0 的地址处。
2. 其他成员变量要对齐到某个数字(对齐数)的整数倍的地址处。
对齐数 = 编译器默认的一个对齐数 与 该成员大小的 较小值
VS 中默认的值为 8
3. 结构体总大小为最大对齐数(每个成员变量都有一个对齐数)的整数倍。
4. 如果嵌套了结构体的情况,嵌套的结构体对齐到自己的最大对齐数的整数倍处,结构体的整
体大小就是所有最大对齐数(含嵌套结构体的对齐数)的整数倍。
为什么存在内存对齐 ?
大部分的参考资料都是如是说的:
1. 平台原因 ( 移植原因 )
不是所有的硬件平台都能访问任意地址上的任意数据的;某些硬件平台只能在某些地址处取某些特
定类型的数据,否则抛出硬件异常。
2. 性能原因
数据结构 ( 尤其是栈 ) 应该尽可能地在自然边界上对齐。
原因在于,为了访问未对齐的内存,处理器需要作两次内存访问;而对齐的内存访问仅需要一次访
问。
总体来说:
结构体的内存对齐是拿 空间 来换取 时间 的做法。
那在设计结构体的时候,我们既要满足对齐,又要节省空间,如何做到:
让占用空间小的成员尽量集中在一起

1.7 修改默认对齐数

1.8 结构体传参


学习产出:

  1. 结构体的相关知识
  2. 结构体的内存对齐
  3. 结构体实现位段
  4. 枚举的相关知识
  5. 联合的相关知识

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

karmada v1.7.0安装指导

前言 安装心得 经过多种方式操作,发现二进制方法安装太复杂,证书生成及其手工操作太多了,没有安装成功;helm方式的安装,v1.7.0的chart包执行安装会报错,手工修复了报错并修改了镜像地址,还是各…

一文拿捏Spring事务之、ACID、隔离级别、失效场景

1.🌟Spring事务 1.编程式事务 事务管理代码嵌入嵌入到业务代码中,来控制事务的提交和回滚,例如TransactionManager 2.声明式事务 使用aop对方法前后进行拦截,然后在目标方法开始之前创建或者加入一个事务,执行完目…

一键AI高清换脸——基于InsightFace、CodeFormer实现高清换脸与验证换脸后效果能否通过人脸比对、人脸识别算法

前言 AI换脸是指利用基于深度学习和计算机视觉来替换或合成图像或视频中的人脸。可以将一个人的脸替换为另一个人的脸,或者将一个人的表情合成到另一个人的照片或视频中。算法常常被用在娱乐目上,例如在社交媒体上创建有趣的照片或视频,也有用于电影制作、特效制作、人脸编…

MySQL5.7版本与8.0版本在CentOS系统安装

目录 前置要求 1. MySQL5.7版本在CentOS系统安装 1.1 安装 1.1.1 配置yum仓库 1.1.2 使用yum安装MySQL 1.1.3 安装完成后,启动MySQL并配置开机自启动 1.1.4 检查MySQL的运行状态 1.2 配置 1.2.1 获取MySQL的初始密码 1.2.2 登陆MySQL数据库系统 …

《Secure Analytics-Federated Learning and Secure Aggregation》论文阅读

背景 机器学习模型对数据的分析具有很大的优势,很多敏感数据分布在用户各自的终端。若大规模收集用户的敏感数据具有泄露的风险。 对于安全分析的一般背景就是认为有n方有敏感数据,并且不愿意分享他们的数据,但可以分享聚合计算后的结果。 联…

Python无废话-办公自动化Excel图表制作

openpyxl 支持用Excel工作表中单元格的数据,创建条形图、折线图、散点图和饼图等。 图表制作步骤 在openpyxl模块中创建图表,步骤如下: ①选择一个单元格区域,创建Reference 对象,作为图形数据a)(Value)。 ②创建一个Chart对象…

简单查找重复文本文件

声明这是最初 我的提问给个文本分类清单input查找文件夹下 .py .txt .excel .word 一模一样的文本不是找文件名 找相同格式下的文件文本是否一样 文件单独复制到文件夹下两个文件全部复制到文件夹下 print 打印相同文本文件的名字 比如查找到了3.py与4.5.是.py文件中的文本文件…

AtCoder Beginner Contest 233 (A-Ex)

A.根据题意模拟即可 B.根据题意模拟即可 C.直接用map 进行dp即可 D.用前缀和进行模拟,用map统计前缀和,每次计算当前前缀和-k的个数就是以当前点为右端点答案。 E - Σ[k0..10^100]floor(X/10^k) (atcoder.jp) (1)…

blender光照系统设置

0)Viewport Shading设置里面的Lighting下面的参数: Scene Lights,Scene World - Scene Lights是指在渲染模式下是否使用场景中的灯光对象来照亮物体。 - Scene World是指在渲染模式下是否使用场景中的世界设置来作为背景和环境光。如果关闭该选项&#…

分类预测 | MATLAB实现NGO-CNN北方苍鹰算法优化卷积神经网络数据分类预测

分类预测 | MATLAB实现NGO-CNN北方苍鹰算法优化卷积神经网络数据分类预测 目录 分类预测 | MATLAB实现NGO-CNN北方苍鹰算法优化卷积神经网络数据分类预测分类效果基本描述程序设计参考资料 分类效果 基本描述 1.Matlab实现NGO-CNN北方苍鹰算法优化卷积神经网络数据分类预测&…

springmvc中DispatcherServlet关键对象

以下代码为 spring boot 2.7.15 中自带的 spring 5.3.29 RequestMappingInfo 请求方法相关信息封装,对应的信息解析在 RequestMappingHandlerMapping 的 createRequestMappingInfo() 中实现。 对于 RequestMapping 赋值的相关信息进行解析 protected RequestMappi…

【AI视野·今日NLP 自然语言处理论文速览 第四十六期】Tue, 3 Oct 2023

AI视野今日CS.NLP 自然语言处理论文速览 Tue, 3 Oct 2023 (showing first 100 of 110 entries) Totally 100 papers 👉上期速览✈更多精彩请移步主页 Daily Computation and Language Papers Its MBR All the Way Down: Modern Generation Techniques Through the …

竞赛选题 机器视觉的试卷批改系统 - opencv python 视觉识别

文章目录 0 简介1 项目背景2 项目目的3 系统设计3.1 目标对象3.2 系统架构3.3 软件设计方案 4 图像预处理4.1 灰度二值化4.2 形态学处理4.3 算式提取4.4 倾斜校正4.5 字符分割 5 字符识别5.1 支持向量机原理5.2 基于SVM的字符识别5.3 SVM算法实现 6 算法测试7 系统实现8 最后 0…

Ubuntu22.04 交叉编译gcc9.5 for arm

一、准备 环境:ubuntu22.04为刚刚安装,未安装gcc等包 vi ~/.bashrc输入 export PATH$PATH:/opt/gcc-arm-8.3-2019.03-x86_64-arm-linux-gnueabihf/bin 保存,reboot 安装: sudo apt install cmake sudo apt install gawk sudo apt instal…

STM32复习笔记(五):FSMC连接外部SRAM

目录 Preface: (一)原理相关 (二)CUBEMX配置 (三)轮询方式读写 (四)DMA方式读写 Preface: STM32F4有一个FSMC(Flexible Static Memory Contr…

mysql面试题11:讲一讲MySQL主从复制模式

该文章专注于面试,面试只要回答关键点即可,不需要对框架有非常深入的回答,如果你想应付面试,是足够了,抓住关键点 面试官:讲一讲MySQL主从复制模式? MySQL主从复制的配置步骤如下: 在主服务器上配置: 打开主服务器的配置文件my.cnf,启用二进制日志(binary log)功…

ElementUI - 主页面--动态树右侧内容管理

一.左侧动态树 1.定义组件 ①样式&数据处理 <template><el-menu class"el-menu-vertical-demo" background-color"#334157"text-color"#fff" active-text-color"#ffd04b" :collapse"collapsed" router :def…

以太网基础学习(一)——以太网概述

一、以太网概述 以太网(Ethernet)指的是由 Xerox公司创建并由Xerox、Intel和 DEC公司联合开发的基带局域网规范&#xff0c;通用的以太网标准于1980年9月30日出台&#xff0c;是当今现有局域网采用的最通用的通信协议标准&#xff08;是局域网的一种&#xff09;。 以太网是一种…

XSS详解

XSS一些学习记录 XXS短标签、属性、事件、方法短标签属性事件函数弹窗函数一些对于绕过有用的函数一些函数使用payload收集 浏览器编码问题XML实体编码URL编码JS编码混合编码 一些绕过方法利用constructor原型污染链构造弹框空格绕过圆括号过滤绕过其他的一些绕过 参考 XXS短标…

mysql面试题14:讲一讲MySQL中什么是全同步复制?底层实现?

该文章专注于面试,面试只要回答关键点即可,不需要对框架有非常深入的回答,如果你想应付面试,是足够了,抓住关键点 面试官:讲一讲mysql中什么是全同步复制?底层实现? MySQL中的全同步复制(Synchronous Replication)是一种复制模式,主服务器在写操作完成后,必须等待…