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

news2024/12/23 18:16:28

学习目标:

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

       这一篇博客主要介绍一下自定义类型,因为在解决实际问题时,由于世界上的因素有很多,我们需要建立不同的数据类型来描述这些变量,但是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.coloradmin.cn/o/1060380.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

01.爬虫基础

1、Python爬虫介绍 爬虫的实战性要求很强。爬虫经常需要爬取商业网站或政府网站的内容,而这些网站随时可能进行更新,另外网络原因和网站反爬虫机制也会对爬虫代码演示造成干扰。 1、1 爬虫的用处 网络爬虫:按照一定的规则,自动…

【Java 进阶篇】JDBC 管理事务详解

在数据库操作中,事务是一个非常重要的概念。事务可以确保一系列的数据库操作要么全部成功执行,要么全部失败回滚,以保持数据库的一致性和完整性。在 Java 中,我们可以使用 JDBC 来管理事务。本文将详细介绍 JDBC 管理事务的方法和…

【Java 进阶篇】JDBC 数据库连接池详解

数据库连接池是数据库连接的管理和复用工具,它可以有效地降低数据库连接和断开连接的开销,提高了数据库访问的性能和效率。在 Java 中,JDBC 数据库连接池是一个常见的实现方式,本文将详细介绍 JDBC 数据库连接池的使用和原理。 1…

vs2015 函数声明、定义与引用

10.VS-函数声明、定义和引用 - 简书 简言之,函数先在头文件中被声明,然后在对应cpp文件中实现(定义),最后被不同文件的代码调用(引用)。

集合原理简记

HashMap 无论在构造函数是否指定数组长度&#xff0c;进行的都是延迟初始化 构造函数作用&#xff1a; 阈值&#xff1a;threshold&#xff0c;每次<<1 &#xff0c;数组长度 负载因子 无参构造&#xff1a;设置默认的负载因子 有参&#xff1a;可以指定初始容量或…

ES6中对象的扩展

1. 属性的简洁表示法 可以直接写入变量和函数作为对象的属性和方法。在对象中只写属性名&#xff0c;不写属性值&#xff0c;代表属性值等于和属性名相同的的变量的值。 属性的简写 let foo bar; let baz {foo}; // { foo: bar } // 等同于 let baz { foo: foo}方法的简写…

力扣 -- 377. 组合总和 Ⅳ

解题步骤&#xff1a; 参考代码&#xff1a; class Solution { public:int combinationSum4(vector<int>& nums, int target) {int nnums.size();vector<double> dp(target1);//初始化dp[0]1;//填表for(int i1;i<target;i){for(int j0;j<n;j){//填表if(…

Windows下启动freeRDP并自适应远端桌面大小

几个二进制文件 xfreerdp # Linux下的&#xff0c;an X11 Remote Desktop Protocol (RDP) client which is part of the FreeRDP project wfreerdp.exe # Windows下的&#xff0c;freerdp2.0 主程序&#xff0c;freerdp3.0将废弃 sdl-freerdp.exe # Windows下的&…

Linux系统及Docker安装RabbitMq

目录 一、linux系统安装 1、上传文件 2、在线安装依赖环境 3、安装Erlang 4、安装RabbitMQ 5、开启管理界面及配置 6、启动 7、删除mq 二、docker安装 1、上传mq.tar包或使用命令拉取镜像 2、启动并运行 3、访问mq 一、linux系统安装 1、上传文件 2、在线安装依赖环…

B2主题优化:WordPress文章每次访问随机增加访问量

老站长都知道&#xff0c;一个新站刚开始创建&#xff0c;内容也不多的时候&#xff0c;用户进来看到文章浏览量要么是0&#xff0c;要么是 个位数&#xff0c;非常影响体验&#xff0c;就会有一种“这个网站没人气&#xff0c;看来不行”的感觉。 即使你的内容做的很好&#x…

5.Vectors Transformation Rules

在上节&#xff0c;有个问题&#xff1a;向量分量的转换方式 与 新旧基底的转换方式相反 用例子来感受一下&#xff0c; 空间中一向量V&#xff0c;即该空间的一个基底&#xff1a;e1、e2 v e1 e2 现把基底 e1 、 e2 放大两倍。变成 基向量放大了两倍&#xff0c; 但对于…

Day-05 CentOS7.5 安装docker

参考 &#xff1a; Install Docker Engine on CentOS | Docker DocsLearn how to install Docker Engine on CentOS. These instructions cover the different installation methods, how to uninstall, and next steps.https://docs.docker.com/engine/install/centos/ Doc…

「专题速递」RTC云网端联合优化、弱网对抗策略、QUIC协议的能力和实践

随着互联网日益增长的加速需求、复杂的网络环境和多样化的视频业务&#xff0c;音视频技术领域的专家们正在不断探索如何实现准确和极低延迟的网络传输能力。他们在应用层流控、传输层协议设计以及跨层优化等方面积极努力&#xff0c;以改善用户的网络体验。 在当今数字化时代&…

Spacewalk

Spacewalk Spacewalk是一种开源的系统管理工具&#xff0c;提供了集中管理多个Linux服务器的功能。以下是一些Spacewalk用例&#xff1a; Spacewalk是基于Substrate的parachains和Stellar之间的桥梁&#xff0c;可以实现与Stellar的资产转移。该拨款申请用于开发太空行走协议…

Java 基于 SpringBoot 的学生考勤系统

1 简介 本文讲解的是 Java基于 SpringBoot 的学生考勤系统。学生考勤管理系统能做到的不仅是大大简化管理员的信息管理工作&#xff0c;在提高学生考勤管理效率的同时还能缩减开支&#xff0c;更能在数字化的平面网络上将学生考勤管理最好的一面展示给客户和潜在客户&#xff…

MyBatisCodeHelper Pro3.x新版本插件自由

1效果图 我的版本为3.2.2 2.资源链接 码云地址点这里 3.使用说明 将我修改好后的MyBatisCodeHelper-Pro-obfuss.jar替换MybatisCodeHelperNew-3.x.x.zip&#xff08;原版本插件&#xff09;\MyBatisCodeHelper-Pro\lib中的MyBatisCodeHelper-Pro-obfuss.jar 4.实现与感谢…

腾讯云南京服务器性能如何?南京服务器测速IP地址

腾讯云服务器南京地域怎么样&#xff1f;南京地域很不错&#xff0c;正好处于中间的位置&#xff0c;南方北方用户均可以选择&#xff0c;网络延迟更低速度更快&#xff0c;并且目前南京地域有活动&#xff0c;南京地域可用区可选南京一区、南京二区和南京三区&#xff0c;腾讯…

c语言实现玫瑰花

浅浅跟波风 1.效果图 2.代码实现 #define _CRT_SECURE_NO_WARNINGS #include <stdio.h> #include <math.h>const int max_iterations 128; const float stop_threshold 0.01f; const float grad_step 0.01f; const float clip_far 10.0f;const float PI 3.1…

录屏软件——Vizard

Vizard&#xff0c;美且实用的网页端录屏软件&#xff0c;轻巧不占内存。Windows/Mac OS均适用。 可以录电脑操作、录软件教程、录网课、录bug、录工作汇报、录创作素材&#xff08;游戏&#xff09;……几乎能想到的一切录屏场景。 除了完全免费的在线录屏&#xff0c;Vizar…

腾讯云服务器选购指南:如何选择一台合适的云服务器配置?

腾讯云服务器配置如何选择&#xff1f;CPU内存、带宽和系统盘怎么选择合适&#xff1f;个人用户可以选择轻量应用服务器&#xff0c;企业用户可以选择云服务器CVM&#xff0c;2核2G3M带宽轻量服务器95元一年、2核4G5M服务器168元一年&#xff0c;企业用户可以选择标准型S5云服务…