C语言之结构体基础
在C语言中,结构体是不同数据类型的元素的集合。该结构用于创建用户定义的数据类型。该结构也被称为“ C语言自定义类型”。换句话说,结构体是不同类型数据的集合。这种数据类型的名字是由用户自主定义的。通常结构体用于将不同数据类型的元素组合成一个组。结构体中定义的元素称为结构成员。在前面我们学习过基础的数据类型int float char 等,都只能用来表示基础的数据类型,那么要怎么来表示复杂的数据类型呢?比如下信息:
定义5个数组,然后通过数组下标一致性原则去描述上述表格数据是否可行? 当然没得问题,如下代码:
看起来还不错,实际上很繁琐,在排序需要交换两者数据的时候极其繁琐,既然学生信息有很多,那么能不能定义一个学生类型呢?如果能,直接通过学生访问该学生的所有信息就很方便了!
为了定义结构体,您必须使用 struct 语句。struct 语句定义了一个包含多个成员的新的数据类型,struct 语句的格式如下:
- 结构体名自己起,struct 结构体名组成新的数据类型,C语言中struct不可缺少。
- 多个成员之间用分号分隔,C语言中不允许无数据成员的结构体定义
- 末尾的分号不可缺少。
那么对于上面的学生的信息,就可以用如下结构体表示学生结构体类型:
结构体中所有数据成员组成一个整体,形成一个新的数据类型,而不同变量则是零散内存,毫无关联,如下图:
结构体中的数据必须要通过结构体变量访问,访问方式有以下两种:
- 普通结构体变量: 变量.成员
- 结构体指针:指针->成员 或者 (*指针).成员
结构体变量的创建
结构体类型已经声明,如何使用结构体类型定义结构体变量呢?有以下方法(typedef别名创建后续再讲):
- 先声明结构体类型再定义结构体变量
- 在声明结构体类型的同时定义变量
如下测试代码:
结构体变量的初始化
在定义结构体变量的同时通过{}的方式为每一个成员变量进行赋初值,赋初值主要有以下几种方式:
- 全部初始化
- 部分初始化:未初始化部分自动初始化为0
- 全部初始化为0
- 初始化指定的成员(可以初始化任意成员,不必遵循定义顺序)
- 用另一个结构体变量初始化
如下测试代码:
结构体数组
一个结构体变量可以存放一个学生的一组信息,可是如果有 10 个学生呢?难道要定义 10 个结构体变量吗?难道上面的程序要复制和粘贴 10 次吗?很明显不可能,这时就要使用数组。结构体中也有数组,称为结构体数组。它与前面讲的数值型数组几乎是一模一样的,只不过需要注意的是,结构体数组的每一个元素都是一个结构体类型的变量,都包含结构体中所有的成员项。
struct Student stus[10]; 这就定义了一个结构体数组,共有 10 个元素,每个元素都是一个结构体变量,都包含所有的结构体成员。
示例程序| 从键盘输入 5 个学生的基本信息,如学号、姓名、年龄、性别,将年龄最大的学生的基本信息输出到屏幕
程序测试结果如下:
当然对于这种表格数据操作有很多,例如排序,查找,文件保存等。详细参见结构体数组写管理系统。
结构体指针
当一个指针变量指向结构体时,我们就称它为结构体指针。C语言结构体指针的定义形式一般为:struct 结构体名 *变量名;
如下测试代码:
程序测试结果如下:
基本上普通指针能做的,结构体指针一样的。只是在访问数据的时候需要剥洋葱(通过->访问每个数据)。当然也可以当做函数参数和返回值。
C语言允许在一个结构体中以位为单位来指定其成员所占内存长度,这种以位为单位的成员称为位段。利用位段能够用较少的位数存储数据。基本语法如下:
C语言标准规定,只有有限的几种数据类型可以用于位段。
- 所有整数类型
- char类型
- bool类型
如下测试代码:
程序测试结果如下:
- 位段的内存分配:位段占的二进制位数不能超过该基本类型所能表示的最大位数,即位段不能跨字节存储,比如char是占1个字节,那么最多只能是8位;
- 位域的存储:C语言标准并没有规定位域的具体存储方式,不同的编译器有不同的实现,但它们都尽量压缩存储空间。
- 禁止对位段取地址:地址是字节(Byte)的编号,而不是位(Bit)的编号。
- 无名位段:位域成员可以没有名称,只给出数据类型和位宽,无名位域一般用来作填充或者调整成员位置。因为没有名称,无名位域不能使用。
在一个结构体内包含另一个结构体作为其成员,当然一般嵌套可以理解为一类数据的封装。访问的话逐步剥洋葱即可,定义方式有两种写法。
- 以结构体变量当做数据成员方式嵌套
- 直接把结构体定义在另一个结构体内
示例程序以结构体变量当做数据成员方式嵌套 | 给学生增加一个出生日期,包含年月日
示例程序直接把结构体定义在另一个结构体内| 给学生增加一个出生日期,包含年月日
什么是内存对齐
从理论上讲,对于任何变量的访问都可以从任何地址开始访问,但是事实上不是如此,实际问特定类型的变量只能在特定的地址访问,这就需要各个变量在空间上按一定的规则排列,而不是简单地顺序排列,这就是内存对齐。通俗点讲就是厕所建坑位需要合理排布对齐,不然可能会存在只有半个坑的情况。
为什么要内存对齐
- 某些平台只能在特定的地址处访问特定类型的数据;
- 提高存取数据的速度。比如有的平台每次都是从偶地址处读取数据,对于一个int型的变量,若从偶地址单元处存放,则只需一个读取周期即可读取该变量;但是若从奇地址单元处存放,则需要2个读取周期读取该变量。
当然我们会不会算内存对齐其实并不重要,因为对于一个结构体占用内存一个sizeof即可搞定,重要的是大家要知道如何设计代码可以让内存更小,毕竟在特殊开发场景,内存占用是非常值得关注的。例如,网络传输,嵌入式等。
内存对齐规则
C语言标准并没有规定内存对齐的细节,而是交给具体的编译器去实现,但是对齐的基本原则是一样的。
- 结构体变量的首地址能够被其最宽基本类型成员的大小所整除;
- 结构体每个成员相对于结构体首地址的偏移量都是成员大小的整数倍,如有需要编译器会在成员之间加上填充字节;
- 结构体的总大小为结构体最宽基本类型成员大小的整数倍,如有需要编译器会在最末一个成员之后加上填充字节。
如下测试代码:
如果阁下正好在学习C/C++,看文章比较无聊,不妨关注下关注下小编的视频教程,通俗易懂,深入浅出,一个视频只讲一个知识点。视频不深奥,不需要钻研,在公交、在地铁、在厕所都可以观看,随时随地涨姿势。
经验分享丨零基础第一次接触C语言,应该怎么上手?
工欲善其事,必先利其器。相信我,选择VS不会错(推荐版本2013~2019)。
①为什么不选VC6.0?这个编译器是98年的软件,老旧且兼容性差。
②为什么不选Devc++?这个软件十多年前就停止更新了,敲出的代码不美观,不利于好的代码风格的形成
③为什么不选CodeBlock?不是主流,需要配置,不适合初学者
④为什么不选VSCode?需要配置环境,不适合初学者
VS提供的错误检查和格式缩进对新手很友好
视频肯定比博文讲得清楚,实践性也更强
VS2019安装教程VS2013安装教程
作者当然没有资格对这一问题下定断言,只是提供作者这两个月以来的学习经验,帮助新手少走弯路
上课篇:如果是选择视频学习的,不要死揪着不懂的地方,要跟上老师的上课思路。刚开始学肯定都会有很多疑问,但之后肯定都会讲到。所以上课要先试着接受老师所讲的知识
笔记篇:如果说老师讲的知识都在课件上或书本上的话,上课不要急于记笔记,而是在草稿本上记下上课的思路。如果能照着上课思路写成博文那说明掌握的很好,所以也可以说把CSDN当成笔记本来用 。同时推荐一些做笔记的软件:有道云笔记Xmind印象笔记
实战篇:1.代码一定要敲(敲重点啦),体验自己成功写出代码的快乐。2.看待代码有三个层次——看代码是代码(小白),看代码是内存(高手),看代码是代码(大佬)。所以在学习时要主动加深对内存的理解。3.遇到问题要学会调试(之后会专门提到)——程序员的必修课。
至此文件已经创建好了。
(解决方案资源管理器在这里)
[相信你对上面提到的细节有些疑惑,但还是那句话,先接受现有的知识,其他的以后都会懂 ]
按下ctrl+fn+f5(或者Ctrl+f5)可以在屏幕上打印hello world。现在我们从三个方面解释这段代码:
特点:main函数为函数入口,有且 只有一个 ,即在一个工程下即使有多个文件也只可以有一个main函数,试想一个文件如果有多个入口那到底从哪里进去呢?
规范性:
1.这里int不写虽然系统默认返回类型为int,但是作为优秀的程序员们,我们要力求规范
2.return不写虽然也不会报错,但还是上面提到的规范规范问题
是什么:库函数是 C语言提供的,使用的时候只需要引用即可。引用的方式是#include< xx.h >这里的printf所对应的库函数位于stdio头文件下,引用这个头文件就可以使用printf函数了
接下来我们认识一下学习到的第二个函数printf
printf函数功能是是将特定格式的数据输出到屏幕上,没有加格式就原封不动地打印
我们实践一下加深理解:具体结果请大家实践操作
这个int是什么东西,有什么用呢?
:first_quarter_moon_with_face:是什么:int表示是一种数据类型
前面说过main是一个函数,函数前面加类型表示这个函数的返回值(函数的知识之后会讲)
这里重点讲一下数据类型
【short=short int long =long int (在这里int被省略了没写而已)】
①为什么有这类型
试想一下我们描述超市里的一件商品,总得有名字吧,也总得有编号吧,价格肯定也不能少吧,所以我们怎么可能用一种类型就描绘出所有的内容呢。不同类型代表不同的含义,他们向内存申请的空间可是不同的,这很好理解。现在我们分析一下不同类型的大小。
②类型的大小
我们首先介绍一个关键字sizeof(他也是一个运算符。敲重点!!!sizeof不是函数,也不能认为关键字和运算符一样),他的作用是返回某个类型的大小。
认识了sizeof之后我们用他来求各个类型的大小(单为字节B)
<关于long的大小等于int的理解>C标准只规定了long的大小>=int即可,具体的大小由编译器决定。
:rainbow:之前提到,不同的类型决定了向计算机申请的内存大小,那内存的单位是怎么理解的?
一个b可以看成是一个小格子,一个B则是八个小格子组成。计算机中采用二进制存储,也就是说一个格子可以表达两种信息(0 或者 1,很好理解,计算机是由很多“开关”组成的,开关要么开要么关,于是就有了0 1),那么一个B可以表示的信息则有2^8种。
③类型的意义
根据前面提到的我们可以算出1个int大小可以表达的信息有2^32(看成是有序的),而一个short表示的信息有2^16,差别还是很大的。假设我们要存储一个人的年龄,总不会超过四位数吧,用int表示的话岂不是大材小用。虽然short也大很多,但浪费的内存更少。所以从中我们可以看出类型的意义在于合理的向内存分配空间。
希望对大家有帮助!
为了帮助大家,轻松,高效学习C语言/C++,给大家分享我收集的资源,从最零基础开始的,帮助大家在学习C语言的道路上披荆斩棘!
编程学习书籍分享:
编程学习视频分享:
整理分享(多年学习的源码、项目实战视频、项目笔记,基础入门教程)
欢迎转行和学习编程的伙伴,利用更多的资料学习成长比自己琢磨更快哦!大家也要把握住大学的时光,抓住成长的每一次机会哦~
对于C/C++感兴趣可以关注小编在后台私信我:【编程交流】一起来学习哦!可以领取一些C/C++的项目学习视频资料哦!已经设置好了关键词自动回复,自动领取就好了!
本文作者及来源:Renderbus瑞云渲染农场https://www.renderbus.com
文章为作者独立观点不代本网立场,未经允许不得转载。