【C语言进阶】参加面试怎能不会结构体?进来学,手把手教会你结构体的原理与使用

news2024/10/3 2:16:07

目录

🤩前言🤩:

🤯正文:结构体🤯:

        1.结构概述🍗:

        2.结构的声明🍔:

        3.特殊声明🍟:

        4.结构的自引用🍣:

        5.结构的定义与初始化🍱:

        6.结构体内存对齐(超重点★★★★★)🧆:

        7.修改默认对齐数🥗:

        8.结构体传参🌮:

🥳总结🥳:


🛰️博客主页:✈️努力学习的銮同学

🛰️欢迎关注:👍点赞🙌收藏✍️留言

🛰️系列专栏:💐【进阶】C语言学习

        家人们更新不易,你们的👍点赞👍和👉关注👈真的对我真重要,各位路过的友友麻烦多多点赞关注,欢迎你们的私信提问,感谢你们的转发!

        关注我,关注我,关注我,你们将会看到更多的优质内容!!!


🏡🏡本文重点 🏡🏡:

🚅结构体声明🚃结构体自引用🚃结构体定义🚏🚏

🚅结构体初始化🚃结构体内存对齐🚃结构体传参🚏🚏

🤩前言🤩:

        上文中我详细全面的为各位小伙伴们整理出了在面试中常用的字符串除了函数,并且为大家讲解了每个字符串处理函数的语法结构和使用方法。而在我们的二面笔试中,还有一个非常重要常考的知识块,那就是我们今天的讲解内容——结构体

🤯正文:结构体🤯:

        1.结构概述🍗:

        C 语言允许用户自己指定这样一种数据结构,它由不同类型的数据组合成一个整体,以便引用,这些组合在一个整体中的数据是互相联系的,这样的数据结构称为结构体,它相当于其它高级语言中记录。结构是一些值的集合,这些值称为成员变量。结构的每个成员可以是不同类型的变量

        2.结构的声明🍔:

        这部分内容在前面初阶结构体已经讲过了,不再做过多的赘述,在此仅以描述 “ 学生 ”为例演示其声明与定义的过程

#define _CRT_SECURE_NO_WARNINGS 1
 
#include<stdio.h>
 
//结构体的声明:
struct student
{
	char name[20];
	int age;
	char sex[5];
	float score;
 
}s1,s2;
//定义结构体变量s1、s2
//此处定义的结构体变量是全局的
 
struct student s3, s4;
//定义结构体变量s3、s4
//此处定义的结构体变量等同于声明时定义,也是全局的
 
int main()
{
	struct student s5, s6;
	//定义结构体变量s5、s6
	//此处定义的结构体变量是局部的
 
	return 0;
}

        3.特殊声明🍟:

        今天我们关于声明部分要补充的,是关于结构体的不完全声明,即匿名结构体类型

#define _CRT_SECURE_NO_WARNINGS 1

#include<stdio.h>

struct
//没有声明结构体标签,即为匿名结构体类型
{
	char name[20];
	int age;
	char sex[5];
	float score;
}student = { "Zhang",21,"Man",91.7 };
//匿名结构体类型必须在生声明的同时进行定义

int main()
{
	printf("%s %d %s %.1f\n", student.name, student.age, student.sex, student.score);

	return 0;
}

        我们把这种在声明时省略掉结构体标签的结构体称为匿名结构体类型,在使用这种方式进行声明时,由于没有声明结构体标签,导致一旦该结构体结束声明,将无法再次进行定义,所以对于该类型的结构体来说,就必须在声明结构体的同时进行定义(可以不初始化)。

        之所以在这里强调这个知识点是因为,在进行完全声明时,例如上面我们的 “ 学生 ”的示例中的s1~s6这六个结构体变量因为声明时声明了结构体标签,所以会被视为同一种类型进行处理和调用

        而我们再来看下面这个例子:

//结构体类型1:
struct
{
	char name[20];
	int age;
	char sex[5];
	float score;
}x;

//结构体类型2:
struct
{
	char name[20];
	int age;
	char sex[5];
	float score;
}*p;

        在这个示例中,虽然两个结构体类型内的结构体成员完全一样,但因为两者都使用了匿名结构体的声明方式,编译器会把上面的两个声明当成完全不同的两个类型,于是在下面的代码中将被视为非法:

p = &x; 
//一种类型的指针指向另一种不同类型,将被视为非法

        4.结构的自引用🍣:

        顾名思义,结构的自引用就是指结构体在自己的声明中引用了自己的一种声明方式。那么我们来看看下面这段代码,判断一下这样的下面这样的引用方式是否正确:

struct Test
{
	int data;
	struct Test n;
};

int main()
{
	struct Test n;

	return 0;
}

        我们说这种引用方式是非法的。这是因为,当我们这样进行引用后,在我们定义结构体变量时,会进行自引用,但在自引用中又嵌套了对自身的引用,如此循环往复,而编译器并不知道该在何时停止自引用

        正确的自引用形式应当是下面这样的形式:

struct Test
{
	int data;
	struct Test* NEXT;
    //使用指针指向确定的引用空间
};

int main()
{
	struct Test n;

	return 0;
}

        当我们在进行结构体变量的定义时同样进行了自引用,不同的是这一次我们使用了一个指针,指向了下一个结构体变量的空间,而在这次指向之后,指针指向的空间被固定,不再指向其它空间,如此就实现了真正的结构体自引用。

        同时,我们还可以结合关键字 typedef 进行使用

typedef struct Test
{
	int data;
	struct Test* NEXT;
	//但在这里必须仍使用struct Test
	//在结构体声明结束后才会进行重命名
}Test;
//使用tepydef关键字,将struct Test类型重命名为Test类型

int main()
{
	Test n;
	//经过重命名,在进行定义时可以直接使用重命名后的类型名进行定义

	return 0;
}

        我们可以结合关键字 typedef 来将我们声明的结构体变量进行重命名,方便我们对结构体变量定义与初始化。但要注意的是,在使用 typedef 时,在结构体声明内部进行自引用时,仍需写成完全形式,这是因为,只有在结构体声明结束后才会对我们声明的结构体类型进行重命名

        5.结构的定义与初始化🍱:

         这部分内容在之前的初阶结构体(点击跳转)的学习中也已经为大家进行过详细的讲解,且这个部分没有需要额外补充的知识点,这里也就不再做过多的赘述,仅以示例来演示相关的使用:

#define _CRT_SECURE_NO_WARNINGS 1
 
#include<stdio.h>
 
struct student
{
	char name[20];
	int age;
	char sex[5];
	float score;
 
}s1 = { "Zhang",21,"Man",98.4 };
//初始化结构体变量s1,此处的结构体变量是全局的
 
struct student s2 = { "Wang",20,"Woman",99.5 };
//初始化结构体变量s2,此处初始化的结构体变量等同于声明时初始化,也是全局的
 
int main()
{
	struct student s3 = { "Jiao",21,"Man",67.2 };
	//初始化结构体变量s3,此处的结构体变量是局部的
 
	printf("%s %d %s %.1lf\n", s1.name, s1.age, s1.sex, s1.score);
	printf("%s %d %s %.1lf\n", s2.name, s2.age, s2.sex, s2.score);
	printf("%s %d %s %.1lf\n", s3.name, s3.age, s3.sex, s3.score);
 
	return 0;
}

        6.结构体内存对齐(超重点★★★★★)🧆:

        经过上面的学习,我们就已经基本掌握了结构体的使用了。接下来我们将要深入研究结构体大小的计算过程,即结构体内存对齐,而这也是近年来许多公司面试与笔试中的热门考点

        我们先来看看下面这段计算结构体变量大小的代码:

#include<stdio.h>

struct test1
{
	char a;
	int b;
	char c;
}test1;

struct test2
{
	char d;
	char e;
	int f;
}test2;

int main()
{
	printf("The size of test1 is %d\n", sizeof(test1));
	printf("The size of test2 is %d\n", sizeof(test2));

	return 0;
}

        各位小伙伴们认为这段代码的计算结果是什么样的呢?

        小伙伴们可能会猜想,TEST1 与 TEST2 两结构体类型中的成员,均是两个占据1个字节的 char 类型变量与一个占据4个字节的 int 类型变量,所以结构体变量 test1 与 test2 的大小应当均为6个字节

        那么小伙伴们的猜想正确吗?我们将其编译运行起来看看结果:

        我们看到,实际的计算结果与我们的猜想大相径庭,那么到底是哪里出现了问题呢?这就是我们在这里需要研究的内容:结构体内存对齐。

        要想弄清楚究竟是如何进行结构体变量大小计算的,我们首先得掌握结构体的对齐规则

1. 第一个成员在与结构体变量偏移量为0的地址处。(偏移量:该成员的存放地址与结构体空间起始地址之间的距离

2. 其他成员变量要对齐到对齐数的整数倍的地址处。

3. 对齐数 = 编译器默认的一个对齐数该成员大小较小值

4. 对齐数在VS中的默认值为8

5. 结构体总大小为最大对齐数的整数倍

6. 如果嵌套了结构体的情况,嵌套的结构体对齐到自己的最大对齐数的整数倍处,结构体的整体大小就是所有最大对齐数的整数倍

        知晓了结构体的对齐规则,我们再回过头来分析上面的结构体变量大小计算过程。

        在结构体变量 test1 中,第一个成员为占据一个字节的 char 类型变量 a,我们按照规则将其放置在偏移量为0,即结构体空间的起始位置

\struct TEST1
偏移量0 1 2 3 4 5 6 7 8 9101112
内容char a

        接着,第二个成员为占据4个字节的 int 类型变量 b,按照规则我们首先要计算它的对齐数,我们将变量 b 的大小4与对齐数默认值8进行比较,得出较小值为4,即对齐数为4,于是我们将它放在对齐数的整数倍处,即最近位置第四字节处

\struct TEST1
偏移量0 1 2 3 4 5 6 7 8 9101112
内容char aint b

        再接下来是第三个结构体成员占据1个字节的 char 类型变量 c,同样按照规则我们计算出它的对齐数为1,并将它放在对齐数的整数倍处,即最近位置第九字节处

\struct TEST1
偏移量0 1 2 3 4 5 6 7 8 9101112
内容char aint bchar c

        最后,根据规则,结构体的总大小为最大对齐数的整数倍,而这三个变量中,对齐数最大的是 int 类型变量的对齐数4,则总大小应当为4的倍数。而既为4的倍数,又要能够容纳所有的结构体成员最小的结构体大小应当为12个字节,即为结构体变量 test1 的大小

        同理各位小伙伴们下去以后可以自己尝试推算结构体变量 test2 的大小并进行验证。

        但是我们发现,这样的方式造成了很大程度上的空间浪费,以 test1 为例,12个字节的大小中有六个字节的空间申请了但却没有被使用。那么为什么还要采用这样的办法呢?主要有以下两个原因:

1. 平台原因(移植原因):不是所有的硬件平台都能访问任意地址上的任意数据的;某些硬件平台只能在某些地址处取某些特定类型的数据,否则抛出硬件异常。

2. 性能原因:数据结构(尤其是栈)应该尽可能地在自然边界上对齐。原因在于,为了访问未对齐的内存,处理器需要作两次内存访问;而对齐的内存访问仅需要一次访问。

        通俗来说结构体的内存对齐就是一种用空间换时间的处理方法。

        而我们能做的,就只有以上面的 test1 与 test2 为例,尽可能的选取 test2 这样,使占用空间小的成员尽可能的集中在一起

        7.修改默认对齐数🥗:

        在我们的代码编写过程中,默认的对齐数可能会不够合适。而当这个时候,我们就可以通过使用下面这个预处理指令修改我们的默认对齐数

#pragma pack(8)
//修改默认对齐数为8

        我们也可以通过该指令在修改过默认对齐数之后,取消设置的默认对齐数,将其还原:

#pragma pack()
//取消设置的默认对齐数,还原为默认

        8.结构体传参🌮:

        结构体传参与函数传参类似,没有什么疑难点,我们直接来看下面的示例:

#include<stdio.h>

struct TEST
{
	int data[1000];
	int num;
};

struct TEST test = { {1,2,3,4}, 1000 };

//结构体传参
void Print1(struct TEST test)
{
	printf("%d\n", test.num);
}

//结构体地址传参
void Print2(struct TEST* p)
{
	printf("%d\n", p->num);
}

int main()
{
	Print1(test);  //传结构体
	Print2(&test); //传地址

	return 0;
}

        而在上面这段代码中,我们一般认为 Print2 函数更为优秀。原因是当函数传参的时候,参数是需要压栈的,在这个过程中就会产生时间和空间上的系统开销。如果传递一个结构体对象时结构体过大,那么将会导致参数压栈的的系统开销较大,最终将会导致程序性能的下降

🥳总结🥳:

        通过今天的学习,相信各位优秀的小伙伴们一定可以熟练的掌握结构体的相关原理与使用,并在将来的面试与笔试中一鸣惊人,让前去面试你的面试官眼前一亮,一眼就从人群中相中你们,为你们提供一份让你们满意的 offer !!!

        🔥🔥天下风云出我辈,一入江湖岁月催。皇图霸业谈笑中,不胜人生一场醉!!!🔥🔥

        更新不易,辛苦各位小伙伴们动动小手,👍三连走一走💕💕 ~ ~ ~  你们的点赞和关注对我真的很重要!最后,本文仍有许多不足之处,欢迎各位认真读完文章的小伙伴们随时私信交流、批评指正!

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

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

相关文章

32位处理器中,通过汇编指令实现64位数据的加减运算

32位处理器一次可以处理的数据是32bit&#xff0c;但如果是64bit的数据&#xff0c;依然可以运算&#xff0c;只是不能一步到位。下面以加法为例。 目录 1、基本思路 2、具体实现 (1) 将数据保存到寄存器 (2) 低32位相加 (3) 高32位相加 3、完整汇编代码 1、基本思路 一…

ODN 2006丨艾美捷CpG ODN系列说明书

艾美捷CpG ODN系列——ODN 2006&#xff1a;具有硫代磷酸酯骨架的CpG寡脱氧核苷酸&#xff08;B型&#xff09;。人和小鼠TLR9&#xff08;Toll样受体9&#xff09;的特异性配体。 艾美捷CpG ODN 丨ODN 2006化学性质&#xff1a; 序列&#xff1a;5-tcgtcttttgtcgttttgtgtcgtt…

非零基础自学Golang 第8章 包管理 8.8 Go语言命名规范 8.9 小结 8.10 知识拓展

非零基础自学Golang 文章目录非零基础自学Golang第8章 包管理8.8 Go语言命名规范8.8.1 驼峰式命名法8.8.2 导出标识符8.9 小结8.10 知识拓展8.10.1 标准包简介第8章 包管理 8.8 Go语言命名规范 对于Go语言命名规范&#xff0c;每一家公司根据自己的实际情况可能都有不同。 一…

仅仅上线一小时,下载量就破10W!阿里内部Java性能优化实战手册

当时看完这&#xff08;Java程序性能优化实战&#xff09;的时候&#xff0c;感到首先就Java的方方面面讲得比较全&#xff0c;但是不乱。而且每个点都讲得比较清楚&#xff0c;读下来也没有什么盲点。干货非常多。国内少有的能写得这么好的。我看了收获很多。所以这会推荐给朋…

HCIP-Cloud+Service+DevOps+Engineer+V2.0第一章华为端到端 DevOps 概览

HCIP-CloudServiceDevOpsEngineerV2.0第一章华为端到端 DevOps 概览 学习总结&#xff0c;思维导图整理&#xff0c;免费分享。侵权删除 本博文为HCIP-Cloud Service DevOps Engineer V2.0培训系列内容&#xff0c;[完整学习路径](https://education.huaweicloud.com/programs…

M.2、PCIe 和 NVMe 的定义和区别

资料来源&#xff1a;维基百科&#xff0c;电商平台等 文章目录结论M.2PCIeNVMe结论 基于阅读的资料&#xff0c;对三者之间的关系&#xff0c;总结为如下层次结构&#xff1a; M.2 M.2定义了计算机内部扩展卡的外观尺寸和电气接口规范。 外观尺寸&#xff1a; M.2模块的外…

艾美捷西妥昔单抗Cetuximab方案及相关研究

西妥昔单抗Cetuximab属于嵌合型IgG1单克隆抗体&#xff0c;分子靶点为表皮生长因子受体&#xff08;EGFR&#xff09;。EGFR信号途径参与控制细胞的存活&#xff0c;增殖、血管生成、细胞运动、细胞的入侵及转移等。 本品可以以高出内源配体约5到10倍的亲和力与EGFR特异结合&am…

BellmanFord算法与SPFA算法

​​​​​​ BellmanFord算法与SPFA算法 展开 Bellman-Ford Bellman-Ford 算法是一种用于计算带权有向图中单源最短路径&#xff08;SSSP&#xff1a;Single-Source Shortest Path&#xff09;的算法。该算法由 Richard Bellman 和 Lester Ford 分别发表于 1958 年和 1956 年…

nodejs+vue校园用车辆校车管理系统

本项目的应用场景描述如下&#xff1a;为减少学生等待校车的时间&#xff0c;合理安排校车调度&#xff0c;设计并开发一个校车预约系统&#xff0c;系统由手机端、服务器端、车载刷卡端三部分组成。学生通过手机应用&#xff08;或微信应用&#xff09;查看校车运行时段&#…

webpack系列之webpack打包图片多生成空白图片且图片不能正常加载的解决方式

文章の目录参考写在最后我用的是webpack的V5.75.0版本&#xff0c;下面是正确的配置方法 module.exports {...// 所有第三方文件模块的匹配规则module: {rules: [{test: /\.jpg|png|gif|bmp|ttf|eot|svg|woff|woff2$/,use: {loader: "url-loader",options: {limit:…

【火热报名中】2022“博客之星”年度总评选重磅启动!

技术人看过来~~2022 这一年&#xff0c;我们遇见了太多的曲折和磨砺&#xff0c;但大家依然保持初心、砥砺向前&#xff0c;用技术人的拳拳之心&#xff0c;抵挡来自时代浪潮的冲击与挑战。为嘉奖勤勉了一年的技术人&#xff0c;也为这一年的种种努力画上圆满的句号&#xff0c…

线性代数之行列式

矩阵的行列式&#xff0c;determinate&#xff08;简称det&#xff09;&#xff0c;是基于矩阵所包含的行列数据计算得到的一个标量。是为求解线性方程组而引入的。 1 行列式的定义 1.1 二阶行列式 对于二阶线性方程组 若b1b2都为0&#xff0c;则称齐次线性方程组&#xff0c;否…

Flutter - PageView(1) 基本用法

如果要实现页面切换和 Tab 布局&#xff0c;我们可以使用 PageView 组件。需要注意&#xff0c;PageView 是一个非常重要的组件&#xff0c;因为在移动端开发中很常用&#xff0c;比如大多数 App 都包含 Tab 换页效果、图片轮动以及抖音上下滑页切换视频功能等等&#xff0c;这…

【10秒在圣诞节做出温馨的圣诞树】

&#x1f935;‍♂️ 个人主页老虎也淘气 个人主页 ✍&#x1f3fb;作者简介&#xff1a;Python学习者 &#x1f40b; 希望大家多多支持我们一起进步&#xff01;&#x1f604; 如果文章对你有帮助的话&#xff0c; 欢迎评论 &#x1f4ac;点赞&#x1f44d;&#x1f3fb; 收藏…

1年就晋升了3级,全靠这份阿里大牛赠送的这份堪称神级架构师手册

又逢“金九银十”&#xff0c;年轻的毕业生们满怀希望与忐忑&#xff0c;去寻找、竞争一个工作机会。已经在职的开发同学&#xff0c;也想通过社会招聘或者内推的时机争取到更好的待遇、更大的平台。 然而&#xff0c;面试人群众多&#xff0c;技术市场却相对冷淡&#xff0c;…

spring教程

spring 1.spring ioc ​ IoC 是 Inversion of Control 的简写&#xff0c;译为“控制反转”&#xff0c;它不是一门技术&#xff0c;而是一种设计思想&#xff0c;是一个重要的面向对象编程法则&#xff0c;能够指导我们如何设计出松耦合、更优良的程序。 ​ Spring 通过 Io…

[附源码]Python计算机毕业设计Django葡萄酒销售管理系统论文

项目运行 环境配置&#xff1a; Pychram社区版 python3.7.7 Mysql5.7 HBuilderXlist pipNavicat11Djangonodejs。 项目技术&#xff1a; django python Vue 等等组成&#xff0c;B/S模式 pychram管理等等。 环境需要 1.运行环境&#xff1a;最好是python3.7.7&#xff0c;…

明道云与智齿科技共推个性化CRM+呼叫中心联合方案

背景介绍 近来&#xff0c;B2B企业用人及获客成本居高不下的问题愈发显现&#xff0c;企业为提高核心竞争力&#xff0c;利用信息技术协调企业与客户在销售、营销和服务上的交互&#xff0c;在优化管理方式的同时&#xff0c;向客户提供个性化交互服务&#xff0c;以达到吸引新…

[激光原理与应用-59]:激光器 - 光学 - 脉冲激光器的参数解析(能量、脉冲、周期、功率)

目录 第1章 光波的基本参数 1.1 光速 1.2 波长与频率 1.3 频率 1.4 电磁波光谱 1.5 光波的能量 第2章 脉冲激光器的参数 2.1 脉冲 2.2 脉冲宽度&#xff1a;单个脉冲作用时间。 2.3 脉冲周期/重复频率 2.4 单脉冲能量 它山之石&#xff1a; 1、激光重复频率&#…

python+Eclipse+pydev环境搭建

本文重点介绍使用Eclipsepydev插件来写Python代码&#xff0c; 以及在Mac上配置EclipsePydev 和Windows配置EclipsePydev 编辑器&#xff1a;Python 自带的 IDLE 简单快捷&#xff0c; 学习Python或者编写小型软件的时候。非常有用。 编辑器: Eclipse pydev插件 1. Eclips…