0基础C保姆自学 第二节——初步认识C语言的全部知识框架

简介: 在栈区的变量用完之后,系统会自动收回,不需要程序员手动释放,但在堆区则恰恰相反:我们需要用free函数来将我们创建的动态空间释放。否则将会造成内存泄漏。

0基础C保姆自学 第二节——初步认识C语言的全部知识框架


注:上述带*的内容可以不用在本节掌握,读者可以在这里当作一种视野拓展,以满足读者的好奇心。


C语言除了以上内容,笔者还会给大家介绍


文件操作   和   编译链接。


但这两部分比较复杂,我们本节不提。如果想看,欢迎订阅专栏,关注@jxwd,这样就能第一时间看到我更新的文章啦。


(虽然是万字长文,而且有二三十张配图,但我敢说没几个人能认认真真从头看到尾😀)


话不多说,我们开始。


还记得我们在上一节输出了第一个C程序,那么,读者应该掌握C语言的基本程序框架。


当然,如果不记得了,我们可以在这里再次重复一下。用一张图片来表示就行,如下图:

image.png



1、 数据类型

数据类型是固定大小内存的别名


常见的数据类型有哪些呢?


char                字符型


int                    整形


long (int)         长整型


long long (int)  更长的整形


short (int)        短整型


float                单精度浮点型


double            双精度浮点型


注明一下:在这里,可以通俗的理解:


①字符型为键盘上能敲出的任意一个单个字符。


②整形为正数,浮点型为小数。


那这些类型是干什么的,怎么用呢?(建议初学者讲笔者所有的代码在自己的编译器上自行操作一遍。实操很重要!)


1-1 数据的创建


像这样,我们分别将上述每一个类型都创建了一下,并且将其初始化了。所以,像上述的创建方式,可以总结为:    数据类型名+ 自定义类型名,给它赋了一个值(这里的10.2f仍然是一个数,我们下面会讲)意为给它初始化了一下。

image.png


1-2 数据的打印

我们来看下面一段代码:(为了使读者得到更多的练习,笔者在此尽量以截图的形式来呈现代码)


代码敲完后,按ctrl+(fn)+f5

如图,我们将上图的六个数据打印出来,得到了相对应的六个数据。


我们发现这样几个规律:


①在printf函数(即打印函数)中,都是以类似于(“%() ”,  );打印出来的。


②每一行的类型不同,打印时%后面跟的字母也不相同。


③每一个printf中都含有一个'\n',它表示换行。(否则,打印出的形式将会是如下的形式:)

很挫是不是。。。。。。。


我们提一下%后面的字母是什么意思:


在C语言中,打印类型需要遵守一定的规则。就是在打印前需要先告诉编译器你打印的是什么类型。%d代表我后面要打印整形;%hd表示我后面要打印short类型;%f表示我后面要打印float类型....其他类推。


在前面告诉完编译器要打的类型后,还要再在后面写入要打印的变量名字(就是说要打印谁的值)


附:部分%后面跟着不同字母的不同含义:(如果读者对部分不理解可以先放一放)

%c    打印char (字符类型)
%d    打印int(整形)
%f  打印float(单精度浮点型)
%lf  打印double(双精度浮点型)
%p  打印地址
%s  打印字符串
%u  无符号的十进制形式打印整形
%x       以十六进制形式打印
%o      以八进制形式打印


1-3 数据的大小

每一种类型的数据的大小又是多少呢?


我们用一个C语言中的关键字:sizeof来实现这个功能。其中sizeof的含义是求某个数据类型所占空间的大小,单位是字节。比如sizeof(int)就是指int 的数据所占的内存大小。


我们来看下面一段代码:


如图,我们可以看到,char类型的大小是1个字节;short类型的是2个字节;int 类型的是占4个字节;float也是4个字节....(注:long在有的编译环境下不是4个字节,而是8个字节)


在这里,我们需要补充一个硬知识:


计算机存储数据示意二进制的形式存储,每一个0或者1占据一个比特位。


一个字节=8个比特位;


一个kb=1024个字节;


一个MB=1024个kb;


一个GB=1024个MB;


一个TB=1024个GB;.......


而在我们上文中所说,int占4个的是字节,char占1个的是字节....


这样说,可能对于字节的大小理解得更加深刻点吧。。。。。。


好,关于数据类型就先给大家介绍到这里,大家只要会创建、会打印,知道它们占据空间的大小就行。


2、常量和变量

第一个问题还是要问:


什么叫变量?


·  变量,顾名思义就是可以改变的量。


像我们上面所说的创建的变量,即a,b,c,d,e,ch,它们都称之为变量。


因为它们所表示的值都可以改变。比如,我可以让a+1,也可以让b-1,让c等于50等等。这个并不难理解。


2-1 变量的分类、作用域和生命周期

变量分为:局部变量和全局变量。


2-1-1局部变量:

定义于局部的变量。


这里的局部指的是在函数的内部中声明的。例如:

image.png



左上图中, 这里的a定义于main函数的内部,可以认为,它是一个局部变量。


同样的道理,在由上图中,我们的add是一个函数(函数具体的细节我们后面讨论,现在知道它是一个函数)中的b也就是一个局部变量。


2-1-2 全局变量:

作用于整个项目的变量。


例如:(如下图)

这里的c就是全局变量。    在全局范围内定义的变量。


局部变量和全局变量类似于整体和局部的关系。


当局部变量和全局变量的名称冲突时,局部优先。(相当于局部又重新定义了一次)


例如:


看,打印出来的就是20。


因为在printf这个地方,局部的a是优先于全局的。


2-1-3 变量的作用域和生命周期

1、对于全局变量来说,变量的作用域为整个项目工程,生命周期与项目的执行周期相同。


2、对于局部变量来说,其作用域为其所在的整个函数,生命周期为其所定义之时起,至函数运行结束后终止。(函数运行结束后,该变量所会被销毁,所占用的空间会还给内存)


我们通俗的说一下,全局变量在整个项目运行的过程中一直存在,并且一直可以用。但是全局变量在出了所定义的它的函数之后便销毁,便不再存在。


2-1-4 变量的使用

我们以输入输出来学习变量的使用。


我们来完成这样一个任务:


输入两个数,并打印这两个数。


我们把样例代码给出,读者可以自行去打着试一下:


读者可以看到,我们在这里引入了一个新的函数scanf。


scanf函数是一个输入函数,用于输入数据。它的用法与printf相似,类似于(“%() ”,  );但是有的读者已经注意到,我们在变量的前面加了一个&。它在这里的意思是取地址,原因是我们输入是要对变量a,b所在的地址进行输入。如果不加上这个&,编译会报错。


打完代码后,我们按住ctrl+(fn)+f5,让代码运行。


我们在控制台输入3 2(注意两个数字间有一个空格)


然后我们可以看到,在屏幕上输出了两个数字3 2。(输出中间之所以有空格,是因为第一个%d后面我们加了一个空格)


这就是标准输入输出!


关于scanf会报错(注意是报错不是报警告)如何解决,推荐大家看一个视频介绍,看完后就知道该如何解决了。


【C语言学习问题】VS2013编译器对函数不安全报错的解决办法_哔哩哔哩_bilibili


那什么叫常量呢?


2-2 常量

常量,是指在给出值以后,不可以再被修改的量。(注意:这里我们所说的值是一个广泛的概念)


2-2-1 常量的类型

在目前的阶段,我们接触到的常量有如下四种类型。


1、直接定义的数值常量。


2、const 修饰的常变量。


3、#define定义的常量。


4、枚举类型常量。


我们接下来举几个栗子:

第一种:直接定义的数值常量。


像这样,10表示一个数值常量;而字符‘a'表示一个字符常量。它们都是我们直接定义的数值常量。


第二种:const 修饰的常变量。来看:


像这样,前面由const修饰的变量a,就变成了常(变)量。常变量保留的变量的某些特征,但是它具有的常量的性质,其值不可以被修改。


在普通情况下,我们均将其当作一个常量。比如,我让a+1,然后重新复制给a,这个时候,编译器就会报错:(如下图所示)


第三种:#difine定义的常量:(就是我们所说的宏常量)


例如:(如下图)


在这里,MAX就是一个常量,它的值为10。如果打印出来,可以看到,控制台上显示的为10:(如下图)

同样的道理,如果将其值修改,我们会看到编译器会报错。


第四种:枚举类型常量。


对于枚举类型,我们要用到一个关键字enum,即通过enum来将常量一一列举出来。来举一个栗子:

从上面我们可以看出,我们在枚举类型中给出所要给的量的名称(我们这里列举的是男,女,保密,当然,你想列举什么就列举什么),那么它们就将成为常量;


*(它们的值默认从0开始,以此往下递增。(提一嘴,因为它们代表的值是整形,所以我们可以用%d来去打印))    


注:标记*表示在这里可以不用了解,看不懂没有关系,我们在接下来的内容中会详细介绍的。


我们接着往下说:


3、数组

数组就是把类型相同的元素放在一起,形成一个的集合


3-1 数组的声明

数组的声明方式是这样的:

image.png

例如,我们可以这样声明一个数组:(如下图)


1、这里的int的意思是数组中元素的类型全部都是int;


2、array是数组名,你想起什么都可以


3、[9]表示数组中有9个元素


4、{1,2.....,9 }表示数组的元素(你想给什么都可以,但前提是元素都要是你前面所声明的类型)


3-2 数组的初始化

像我们上面那样,在声明的时候,给它的元素全部列举了出来,就是数组初始化的一种形式。


这种把数组的元素一一列举出来的初始化方式,称为数组的完全初始化。


另外一种数组初始化的方式,称为不完全初始化。


比如:

image.png


这表示什么呢?


表示数组的第一个元素为1,剩下的8个元素都是0。


3-3 数组的访问

那我怎么样才可以拿到(访问)数组的某个元素呢?


我们以上面的   int array[9]={1,2,3,4,5,6,7,8,9};   为例:


首先,数组的每一个元素都是有下标的。而且是从零开始,然后直至最后一个元素,依次递增。如图:

image.png

那么,我们在访问的时候,是以数组元素下标的形式来访问的。


比如,我想访问数字2,那么我就可以用array[1]来访问。


那现在,我们把它打印出来:

image.png


其它的元素访问规则以此类推即可。


注意:在这里要强调一下,数组中的元素类型一定要相同。


4、字符串

字符串,字面意思,就是指字符和字符在一起形成一个串


字符串一般会用双引号引起来,而不是单引号。


比如   “abcde”   就是一组字符串。


在C语言中,没有单独的专门用来声明字符串的类型。所以我们把它放在字符数组中去声明。举个栗子:(如下图)

image.png



这便是一个字符串。


我们来分析一下:

image.png


我们可以把字符串理解为一个特殊的数组,或者就是一种字符数组。


注意:


在每个字符串的后面,其实还隐藏这一个'\0'。


它不算到字符串的长度中,但是却占据一个单位的空间。


比如,上文提到的字符串"abcdef",它的字符串长度是多少呢?(如下图)

image.png



(如上图)


也就是说,这个字符串长度为6。


我们可以来验证一下。


验证时,需要引入一个库函数:strlen,它用来计算字符串的长度。计算的原理是从字符串的第一个字符开始,一个一个向后遍历,直至遇到'\0'为止,计算'\0'前面的字符个数。用它需要引头文件


看下面的栗子:

image.png



可以看出,该字符串的长度为6。而字符'\0'是不计入字符串长度中去的。


5、转义字符

我们输入这样一行代码:

image.png

我们让代码运行,来看一看会输出个什么样的东西

image.png


一堆乱码?


其实不是。


那为什么会输出这么个鬼东西?


这就与转移字符有关系了。


在这里,编译器在遇到’\t‘后将其转义,不再是原本含义,而是转变为水平制表符,相当于一个tab键缩进的长度,大概相当于4个空格(多少个空格可以自己改)。


而\102也是一个转义字符。它是102是八进制数字,它输出的为八进制数 102 在系统中所代表的ASCII码值。


\n也是一个转义字符,它的意思是换行,我们之前提到过的。


在这里我们提一下ASCII码,它的全称为美国信息交换标准码。它实际上是一个字符-数字转换表。即我们在计算机中,给定一个数字,它在内存中其实还对应着一个字符。同样的道理,我们在计算机中输入一个字符,它在内存中其实还对应着一个数字。而其所遵循的转换的规则就是ASCII码表(注意:该数字是有范围的)。现我们把ASCII码表给出

微信图片_20221208125851.png



\102转换为十进制就是8^2+2=66,而在上面的表中我们可以看到它所代表的字符为B

image.png

好,我们再回到转义字符上来。

image.png

这就是相关的字符相关的转移。


当编译器遇到\a时,电脑会响一下,\b表示退格,\f表示换页...重点需要注意的时\n; \t; \ddd;\xhh;


\n表示换行;\t表示水平制表符(相当于一个tab);


需要指出的是,\xhh指的是打出\x后再在后面加两个十六进制数所对应的值;而\ddd则表示\后面加三个八进制数所对应的值(我们刚刚提到过)


那么,我们就可以解释一下刚刚为什么打印出来那么一堆乱七八糟的东西了

image.png



其中,转义字符算一个字符。


那好,我们看一下下面的这一串字符的字符长度是多少:


   \1133\138\test\\.c\n


答案是12;你算对了吗?

image.png

为什么是12?


根据我们上面说的,

image.png


需要注意的是,我们没有把\138放在一起。因为\ddd是八进制,数字只能有0~7,是不能有8的。


好,转义字符我们就说到这。


6、代码注释

先行的代码有两种注释方式。


第一种,是C风格的注释方式:用/*   */;


第二种,是C++风格的注释方式,C99后流行,用  \\;(如下图)

image.png


当然,/*   */这种注释方式是有一定的缺点的:即不可以嵌套。请看:(下图)

image.png


我们先把main函数里面的注释掉,再想在外面注释的时候,我们发现,后面的括号并没有被注释掉。


7、函数

函数是C中最重要的一个部分。可以说,一个程序是由若干个函数组成的。那么函数到底是什么?它到底怎么用?我们今天在这里先稍微地提一下,读者只需清楚什么是函数就好,我们今天只是简单的介绍一下,不深入,不发散。


首先,函数有两种,一种是库函数,一种是自定义函数。


我们用的printf;scanf;strlen都是库函数,也叫内置函数。包括我们常常提到的main也是一个函数。可以这么理解,库函数是别人为你写好的,然后打包封装到像这样的头文件中去。你用#include表示包含这个头文件,那么你就可以去用这个头文件中定义的所有函数。


我们再来讲讲自定义函数:


我们想要用一个函数,只需要把函数的名字写出来,然后打上括号,把函数的名字写进去就行。就像这样ADD(a,b)。我们将在下面的实例中给大家演示:


现在,


我们来写一个最简单的函数——相加函数,就是把两个函数加起来。


我们先把主函数(main)写出来:

image.png


ADD(a,b)就是函数调用的意思。也就是说,我要调用这个函数,经过什么过程和操作我暂时不关心,但结果是通过这个函数,我把我传进去的a,b两个数加了起来,得到了它们两个数的和。然后我用sum来接受这个ADD函数返回来的和。(也就是这个值)


上述所描述的就是函数的调用。再说的直白一点,就是用了函数的方法。


那么,我们现在再来看看如何实现这个函数:(也就是得到这个值所经历的过程是什么)


函数实现的基本架构是这个样子的:

image.png



我们仍然以上面的函数为例:


上面的函数我们可以写成:

int ADD(int a,int b)
{
    int c=a+b;
    return c;
}


这样,我们就实现了这个函数。


我们先来讲讲这每一部分都代表这什么:

image.png



好,我们再在整体的代码中来看一遍,将代码怎么执行的说一遍。


image.png

所以,打印出来的值应当为30。


需要说明一下的是,我们最好把要用的函数写在main函数的上面。因为如果写在main函数下面,编译器在调用这个函数之前并没有见过这个函数,这样就会报错或者报警告。除非在前面进行一下声明。就像这样:


微信图片_20221208130313.png


也是可以的。


好,就此打住,函数我们今天就了解到这。 读者只需要了解函数是什么,怎么创建的就可以。


8、选择语句

选择语句,一般来说有switch和if两种比较常见的选择语句的形式。我们今天还是点到为止,只介绍一下if的选择语句。


if选择语句的基本结构是这个样子的。


image.png


这里的else if也不一定要有,如果只是A和B中间的选择,直接用if   else就行。


我们现在来举一个简单的栗子:

image.png

 各位认为打印出来的是haha还是hehe?


我们来把代码运行的流程演示一遍:(如下图)

image.png



如果用流程图表示,好像是这个样子的:

image.png


ps:如果按照标准,输出框应该用平行四边形画,但我的画图板好像找不到平行四边形。不过无所谓,凑合着看吧😀,明白意思就行哈。


那我现在给两个数,然后比较出一个较大的数输出在屏幕上,可以怎么弄?


很简单,可以这样实现:

image.png


这就是选择语句。


9、循环语句

如果我想输出10句hello world,难道我要打10个printf吗?有没有更好的办法呢?


答案是肯定有的。这个时候,就可以借助我们循环语句的功能来实现。


先说一下,循环语句体中,共有三种主要循环方式,分别是for循环;while循环;do  while循环。今天在这里,由本章的定位所决定,我们只是简单了解一下它们,我们只简单的介绍一下while循环(还有一个原因就是如果今天都讲了以后就没有内容写了呜呜呜~~~~~)


while循环的框架时是这样的:

image.png



来看这么一条代码:

image.png


这就是循环语句的功能。这里我们用的就是while循环。while循环的循环过程是这个样子的(我们用上图的例子来分析)

image.png


所以说,循环就是重复的执行一件事情,直至满足了某些条件,才可以跳出循环。


我们关于循环目前就先了解到这里。


10、操作符

什么叫操作符?


它就像预设好的一个操作符号,我用了这个符号,我就能实现某种目的。


听着可能有点抽象。


我们用具体的操作符来解释:


10-1 下标引用、函数调用和结构成员

主要是指  [ ]        ( )        .       ->这四种。


我们先讲一下前两种,后两种我们放在底下的结构体知识里面来说(往下翻,在下面讲到)。


[ ]主要用于数组中;


(  )主要用于函数中。


比如,我们上面在说数组时数组的访问是通过array[2]来实现的,这里的[ ]就是下标引用,可用于数组访问。


同样的道理,我们在说函数的时候讲到写一个函数的名称然后在后面打括号,这里的括号就是我们所说的操作符。


10-2 算术操作符

+    -     *     /     %


这个就是普通的  加减乘除 取模 。提一嘴,%表示取模。就是取余数。


比如5%2=1;需要注意的是,%不可以用于小数的运算,只能用于整数(用专业的话说,只能用于整型)


10-3 移位操作符

<<         >>


我们之前介绍过,数据使用二进制存储,一个0或者1占据着一个比特位。而且一个整型有4个字节(32个比特位)。那么, << 和 >> 是在二进制上进行的操作,其中<<代表左移,>>代表右移。


我们举个栗子来说明一下:

image.png


这是怎么一回事?这操作符到底怎么用?我们来详细介绍一下:

image.png


同理,>>表示向右移位。<<  或  >>  后面跟上几,表示移动几位。


有关其它的细节,我们以后再聊。读者在此只需知道它们是什么意思就行。


10-4 位操作符

&     ^     |      ~    


它们仍然是对数字的二进制位上进行操作的。


其中&位按位与;|表示按位或;^表示按位异或 ;  ~表示按位取反。 (前面都是对两个数操作得到一个新的数,而 ~ 是对于一个数进行操作的)


什么意思呢?


我们以1表示真,0表示假,那么:


&的规则:两个数的某一位都为真,得到的这一位才是真。


|的规则:两个数的某一位只要有一个为真,得到的这一位就是真。


^的规则:两个数的某一位相同则得到的这一位为假,相异则得到的这一位真。


~的规则:一位一位看,真变假,假变真。


是不是很抽象不易理解?


那好,我们来分别举个栗子:

image.png


我们现在来分析一下为什么这样的数:


首先分析1&2:(如下图)

image.png

同样的逻辑分析1|2:(如下图)

image.png



那么1^2呢?


我们还是以刚刚说的数为例:

image.png



转换为10进制后就是3


最后一个,~呢?


这个其实很简单的,不用画图都能说明白。


还是上面那个例子:


2的二进制数为  00000000 00000000 00000000 00000010;


对每一位取反为11111111  11111111  11111111  11111101;实际上,这个数就是-3。至于它为什么是-3,我们以后再讲。


读者在此只要知道上面4种操作符的运算规则就可以了。


10-5 逻辑操作符

&&   ||


&&表示并且


我们举个例子:

image.png


&&的左右两边都有一个条件判断。


两边的条件必须都为真,if判断才为真,否则一假即假。这就和我们高中所学的这个很像(我不会打,就画了一个。。。。别介意,,哈哈)

image.png

那么我们上面的条件判断的最终结果就为假喽。因为1是不大于2的;1>2该语句判断为假,那么整条语句的判断都为假。


| | 表示或者,就和我们高中所学的这个很像(我又画了一个,哈哈哈)。和&&一样的道理,我在这里就不赘述了,以免有人嫌我太罗嗦。。。。。

image.png

10-6 赋值操作符

=          +=          -=            *=            /=            &=          ^=          |=           >>=         <<=


具体什么意思?


=的意思不是等于,而是赋值。像a=3;这样一条语句,是说把3的值赋给a;


+=的意思就是自己加上一个数,然后再把得到的值赋给自己。


-=同理,自己先减去一个数,然后再把得到的值赋给自己。


其余的都是一样的道理。读者只需类比就行。


我们来通过例子的方式再说明一下:

微信图片_20221208131711.png


a一开始是10,那么+=9相当于自增9,所以这时候打印a,得到的值就是19。


其他的同理了,笔者就不在此一一列举了,读者可以选择性的在编译器中尝试一下。


10-7 关系操作符

>     <       >=       <=         ==            !=


它们都是用于判断两个数之间的关系的。


前面四个没什么好说的,分别为大于、小于、大于等于、小于等于。


我们强调一下后面两个:


==用来判断是否等于,请看下面的代码:

image.png


这里的sum==30就是判断是否相等的语句。意为:如果sum等于30,那么输出hehe;否则,输出haha。


在这里,应该输出hehe。因为sum就是等于30。


切忌!判断是否相等时两个=,即==,一个=表示赋值,不是判断是否相等!


那么我们以同样的逻辑来理解!=就比较简单了。!=意为不等于。(如下图)

image.png


如图,这是,我们输出的应当就为haha了。因为sum就是30,判断语句sum!=30为假,那么进入else语句中,打印haha。


10-8 条件操作符

exp1?exp2:exp3(注:每个exp代表一条语句)


它的意思是:


exp1为真吗?如果为真,则返回exp2的值;如果为假,则返回exp3的值。


我们仍然通过一个简单的栗子来说明:

image.png



显然,上面的语句返回的应当是b的值,所以打印出来的应当是b的值,也就是30。


10-9 单目操作符(只对一个数或语句操作的叫单目操作符)

!          逻辑反操作  


-          负值


+          正值


&          取地址


sizeof     操作数的类型长度(以字节为单位)


~          对一个数的二进制按位取反


--  前置、后置--


++ 前置、后置++


*          间接访问操作符(解引用操作符)


(类型)      强制类型转换


我们挑几个来说:


10-9-1 !     逻辑反操作  (很简单,就是如果语句为真,那前面加上!语句就变成了假)

注:在计算机中,默认0为假,非0为真。


举个栗子:

image.png



请问其能不能输出haha?


答案是否定的。


因为a为非0,为真。那么!a就变成了假,if里面的就进不去啦,所以就不能输出haha了。


10-9-2  -          负值

           +          正值

需要注意一下,这两个可以作为单目操作符,就是在一个数前面加个+  -  。比如,3我可以写成+3(不过得到的结果一样的效果)。我也可以写成-3(就变成了一个负数)。很好理解的。


10-9-3 &    取地址

我们刚刚在scanf中用到过。我们会在下文中介绍指针的时候再详细说。


10-9-4 sizeof  

这与上文所说到的strlen很像,但又不完全相同(后面的章节会具体说到二者的联系和区别)。它是一个运算符,其作用是取得一个对象(数据类型或者数据对象)的长度(即占用内存的大小,以byte为单位)。我们在前面计算数据类型的大小的时候用到了它。


10-9-5 --  前置、后置--

          ++ 前置、后置++

这四个可以看成一组。


++的意思是自增一,--的意思是自减一。


比如,我原本i是5,我现在i++;那么i就变成了6;同理,如果i--就变成了4(i原本是5的情况下)。


那前置和后置又有什么区别呢?


在这里,读者记住一点即可:前置++,先加加在运算。后置++,先运算再++。 --同理。


我们用例子的方式理解一下:

image.png


如上图所示:


在打印第一个a的时候,先将a自增,再打印出来。所以,打印到屏幕上的数为10+1=11。


在打印b的时候(看第二个printf),++为后缀,那么遵循先使用,后加加的原则。先使用则打印出来,打印出来的数就是20;然后再自增。我们这个时候再次打印b的值,发现它已经变成了21。这是因为其在上面打印后++的缘故。


10-9-6 * 定义指针(解引用)

操作符我们在下面的指针详细介绍;


10-9-7(类型)   强制类型转换。

就是在一个类型的前面加上一个括号后,把原来的数据类型强制转换为括号里面的数据类型。


我们来举个例子吧:


比如:int a=10;


好,我现在想把它转换为float类型,那么就只需要这样:


(float) a;


除了单目操作符,还有双目(比如*;/等等)、三目操作符(exp1?exp2:exp3),只不过这些操作符我们都在上文提过了。然后还有就是关于运算符的优先级的问题,算术运算符 > 关系运算符 > && > || > 条件操作符 > 赋值运算符,进一步详细地介绍我们会放在后面的操作符详解的章节中继续为大家介绍。关注我的文章更新哈~~~~具体我们以后再提。


读者在这个时候了解这么多已经够了,因为本章我们是初始C语言。


11、常见关键字

要说到C语言的关键字,那真不少。


auto    break  continue   switch   case default const   char       do   double    else    enum    extern     float     for      goto      if        int        long        register      return     short     signed    sizeof  static    struct       typedef    union      unsigned     void      volatile      while


上面的都是的,而且有很大一部分都是非常常见的。不过大家不用担心,常见就说明用的多,用的多了自然就会了。


我们挑几个来说说(所有的我们都会讲到,只不过有的我们已经在上文提到,有的将会在下文讲解时提到,现在我们挑几个来专门说说)


11-1 auto

它可以根据给它赋值的类型,自动为其创建变量的类型。


比如:int a=30;   auto b=a;用法比较简单。


11-2 break与continue

它们可以认为是一对。


break是用于终止所在的循环。而continue是指跳过当前的一次循环。


我们来举个例子:

微信图片_20221208132204.png



我们可以看到,屏幕上打印了6个hehe。不是10个hehe。原因?


我们来分析一下:

微信图片_20221208132251.png


那么如果我把continue改成break呢?


那就直接跳出循环体。根本不会再跳到上面继续循环了。


就是说,遇到break,该层循环整个就到此结束了。就是这个意思。


11-3 extern

它用于外部声明的时候使用。


这里简单了解一下。


举个简单的例子:

微信图片_20221208132312.png



我创建了两个源文件(当然,源文件的名字最好是用英文,不要学我哟~~~~~)


image.png


我在一个文件中写了add函数的具体实现,在另外一个文件中如果直接调用add,肯定会报错。如果,我们加了一个extern,就不会报错了。


也就是说,用了extern,我可以使用外部的函数。


11-4 static

它意为静态的


它可以用来修饰函数、全局变量和局部变量


11-4-1 修饰函数

我们还是刚刚上面的那个例子:


如果我在add.c文件中加上一个static,编译器就会报错:如图:


image.png

运行:

image.png


根据报错,意思是add无法解析,编译器不知道它是什么。说明我们在add.c中的函数不管用了。这是因为static修饰的缘故,使得add.c中的函数add无法作为另一个文件的外部链接来使用。也就是说,我加了static,我的函数只能在我的这个文件中用。别的文件想用,就不行了。


11-4-2 修饰全局变量

和修饰函数其实一样的道理,就是让其无法在外部被使用,实际上是在链接这一步的时候断了。


(但读者在此可以不用知道这么多,简单了解一下其可以实现的作用即可)


11-4-3 修饰局部变量

来看:

微信图片_20221208132742.png



请问,这段代码会最后打印出来什么?


答案是三个2。image.png


那我在int a 的前面加上一个static,看看会怎样?


微信图片_20221208132812.png


输出的就是2 3 4。


为什么?


因为当a被static修饰后,再次进入print函数的时候,便不会再次创建。可以通俗的理解为:成为了一个静态的量。


所以,a++会被一直执行,但是int a=1却会跳过了。它只会被创建一次。


11-5 typedef

它意为重命名。


比如:(举个小李子)

image.png



11-6 define

它意为定义。


它可以这样用:


这是#define的数字宏定义,至于函数宏,我们以后再讲。

image.png


至于其他的关键字,我们会陆陆续续讲到,读者只需要在我们讲到这些东西的时候,知道它是关键字就可以了。


12、指针

指针其实并没有想象中的那么复杂,它是在内存上的一个操作符。


既然是在内存上的操作符,那么了解指针之前,我们就有必要来了解一下什么是内存。


首先,内存可以理解为一块一块连续的区域,用于存放数据使用。


image.png


像上图那样,可以理解为当我在创建的变量时,我需要向操作系统申请一定大小的空间,


而地址就是为这些内存进行了编号。


我们创建一个变量后,按f11调试,然后选中

image.png

在上面的地址框内输入&a,回车;再按两下f11;可以看到a的数据为   0a 00 00 00   因为是16进制,0a刚刚好就是表示16(后面的0表示高位,相当于十进制中的百位千位等,这个我们以后再说),再这里,我们以肉眼可见的方式看到了a的地址:


0x00AFF7C8(就在0a 00 00 00的左边,灰色的字)

image.png


那好,说完了内存和地址,我们就该谈谈指针是什么了。


指针,其实某种意义就是地址。


我们还是以上面的例子继续说:


a的地址现在我们已经拿到了,是0x00AFF7C8,那么,这个地址的就是指针,它存储着a这个变量的值。也就是说,0x00AFF7C8就是变量a的指针。


那指针变量是个什么东西?通俗的来说,就是用来存放指针的。就是存放0x00AFF7C8这个地址的。我们用 * 来表示


那具体来说是怎么使用的呢?来看下图:

image.png



我们在写代码的时候,可以这样来写:

image.png



int* p = & a;       可以这么理解(如上图(代码图上面那个))


创建一个变量,变量名是p,类型是int*(指针类型),&a(注意,这个时候取地址的作用就显现出来了,这里就是意为取出a的地址),即取出a的地址,然后用刚刚创建的变量p来接收。


这就是指针的创建即初始化。


那我现在想要访问指针变量里的地址所存储的数据,我应该怎么办呢?那就应该用解引用操作符。解引用操作符依然是 *  。它是这么用的。

image.png


这里int b=*p; 它的含义可以这样理解:


①*p表示对指针变量p进行解引用。就是说用我手里存放的指针变量p,找到指针变量p所存的地址,通过这个地址来访问其存储的数据的值。


②把这个值赋给b。


③当我们打印b时,结果输出10。(如上图)


我们暂且就先介绍这么多,我们这次先来简单认识一下指针,读者在此只需要知道指针是什么,怎么创建、怎么解引用就可以。


到后面的指针详解这一章节的时候,笔者会为大家详细的介绍有关指针的所有内容。


13、结构体

我们在说数组的时候,明确强调过,必须时相同的类型才可以放在数组里。那么不同类型呢?可以这样理解,对于不同类型,我们就放在结构体里面。


说到结构体,我们会用到一个关键字(struct)


结构体(struct)是由一系列具有相同类型或不同类型的数据构成的数据集合,也叫结构。


有了结构体,C语言便拥有了描述复杂类型的能力。


13-1 结构体的创建

那结构体时怎么创建的呢?


我们用关键字struct来完成


请看下面一段代码:

微信图片_20221208133125.png

这里的struct pep就是结构体,它是一种类型。类比于int。


在这个结构体中,我们用大括号把要创建的类型集中起来,尾部要打上分号。


然后,在大括号里面创建上我们想要创建的类型。在这个大括号里面,可以创建不同的类型,比如int     char   等。


那么,我们在接下来如何创建一个结构体变量呢?

image.png



看,像这样,我们就创建了一个结构体变量a,可以理解它的类型就是struct pep。


在这么一个结构体中,我们对其中的四种类型分别进行了初始化。(注意要对应)也就是说,变量a里面的20就相当于age类型。2121212就相当于是char类型的数组,存放在以数组名为tel的数组中。其它同理。


13-2 结构体成员的访问

我创建好了,该如何访问呢?


这个时候,就用到了我们之前跳过去没有说的两个操作符:  .   和  ->。(二者用一个就行)


比如,我像打印age变量里的值,该怎么做?


很简单,像下面这样:

image.png



还有一种方式:(我用这种方式示范打印tel)

image.png



如上图,这就是结构体成员访问的两种方式。


14、*动态内存分配初步

对于这一块,我们现在只需要知道什么叫动态内存分配。因为如果讲的过于深入一些初学者可能接受不了。于是,我们就在这里提一下。


为什么要有动态分配?


很简单,因为我们在创建数组的时候,它是一个静态的,不能根据我的需求来去开辟内存空间的大小。开辟的多了或者少了都不好。因此,就有了都太开辟空间的概念,通过动态开辟,向内存申请一块连续的空间,并且保证了开辟空间的大小不会浪费,也不会不够用。


动态内存分配,我们用到函数malloc(当然还有calloc  realloc,大同小异,我们今天不讲),它的运用规则是malloc(size)(size表示空间大小)


它返回的是一个指针,类型为void*


所以,我们可以通过这样创建一个动态数组:


int* ps = ( int * ) malloc ( 100 );


这样,我就创建了一个内存大小为100的动态数组。


那么这个空间又是在哪个地方来开辟的呢?答案是在堆区。


(如下图)

微信图片_20221208133326.png



如图所示,我们的局部变量的创建是在栈区,动态创建的变量是在堆区,我们上面所提到的全局变量的创建以及static修饰的变量的创建都是在静态区。


在栈区的变量用完之后,系统会自动收回,不需要程序员手动释放,但在堆区则恰恰相反:我们需要用free函数来将我们创建的动态空间释放。否则将会造成内存泄漏。


用free释放完之后,还记得要把首元素的指针指向空指针,因为free是无法将指针本身释放掉的。


好了,关于动态内存分配我们暂时了解到这。



目录
相关文章
|
6月前
|
存储 人工智能 算法
【C语言】自学终极笔记
【C语言】自学终极笔记
106 0
|
19天前
|
C语言 C++
保姆式教学C语言——数组
保姆式教学C语言——数组
15 0
保姆式教学C语言——数组
|
19天前
|
机器学习/深度学习 编译器 C语言
C语言刷题(中)(保姆式详解)
C语言刷题(中)(保姆式详解)
12 0
|
19天前
|
机器学习/深度学习 C语言
C语言必刷题上(保姆式详解)
C语言必刷题上(保姆式详解)
13 0
|
2月前
|
安全 程序员 编译器
C语言框架的优缺点如下
C语言框架的优缺点如下
52 1
|
5月前
|
C语言
C语言结构体教程:从入门到实践(保姆式教学)
C语言结构体教程:从入门到实践(保姆式教学)
57 0
|
5月前
|
C语言
C语言中求x的n次方:从入门到实践(保姆式教学)
C语言中求x的n次方:从入门到实践(保姆式教学)
311 0
|
6月前
|
C语言
C语言实战演练之游戏框架
C语言实战演练之游戏框架
|
6月前
|
C语言 索引
基于C语言的函数指针应用-消息命令处理框架
基于C语言的函数指针应用-消息命令处理框架
36 0
|
6月前
|
安全 C语言
C语言设计扫雷(保姆级教学)
C语言设计扫雷(保姆级教学)
C语言设计扫雷(保姆级教学)