目录
1、C语言的三种结构
C语言是结构化的程序语言,因为C语言支持3种结构:顺序结构、选择结构、循环结构。我们每个人的生活状态也同样是这三种顺序、选择、循环,比如白天去学校晚上回宿舍以往反复,可以看作循环。我们不管干什么都会导致时间流逝,不能返回,像顺序结构一样,看作顺序。我们有时需要做出选择,比如钱不够,需要做出选择,要买漫画还是买零食这可以看作选择。C语言的三种结构正好可以描述我们生活中的状态,是不是感觉C语言非常的神奇?
2、if语句
2.1 if
if语句的语法如下:
if(表达式)//判断表达式结果非0为真,0为假
语句
if判断括号内的表达式结果是0为假,非0为真,为假if语句不执行,为真便执行。
下面就给一段代码,来让大家更加清楚地认识到if语句:
上面代码就是先创建一个变量num,然后使用scanf函数给num输入一个值,下面就继续执行if语句,先判断if语句括号内的表达式,num%2==1意思是如果num这个值:7%2余1,然后判断1==1,判断结果正确返回1,1为真,为真就执行if里面的语句。然后就打印7为奇数。
if语句下面如果只有一条语句可以不扩大括号,比如:
就算if语句不扩大括号,if语句下面的第一条语句也属于if语句的范围,可以理解为绑定。
2.2 else
else语句的语法如下:
if(表达式)
语句1
else
语句2
else语句和if语句是配套的,可以理解为他们是一套工具,里面有锤子和螺丝刀,锤子解决的了的事情用锤子解决,锤子解决不了的事情用螺丝刀解决。总要有一种方法能够解决事情。这就是if else语句。if判断为假,就直接执行else语句。if判断为真,就走if语句,结束后就不用走else语句了,这就是else语句的存在,比如下面再给一段代码:
是奇数就走if语句,不是奇数就只有偶数这一种可能了,就走else。
2.3 分支中的多条语句
注:不管是if还是else默认只能控制一条语句,如果一个分支要有多条语句就用大括号括起来。
但是生活中的选择有时不仅仅只有两个,有时候有多个选择,那这个时候该怎么办呢?
2.4 嵌套if
在if else语句中,else可以与另一个if语句连用,构成多重判断。
比如:要输入一个整数,判断它是正数、负数还是0,就需要用到嵌套if
if不通过就走else,然后继续在else里判断剩下的两个分支,但是有没有更简洁的写法呢?当然有,看下面代码。
以上两种嵌套if只是两种书写方式,但是功能和性质还是一模一样的。
将else里嵌套if直接写成else if(表达式)去判断,也可以将else if看作另一个分支的本体,不影响,但需要了解他们之前是嵌套关系。
除了上面的嵌套if,if else语句还可以这样嵌套:
2.5 练习:打印出年龄阶段
例如:输入:22
输出:成年
2.6 悬空else问题
来看一下下面这段代码,你们猜它打印"hehe"还是"haha"?
答案是什么都不打印,为什么?首先第一条if语句判断a==1吗?a为0,0 !=1所以为假,为假不是应该走else语句打印"haha"吗?首先,不要看到else 与 if第一条if语句对齐了就以为它们是一对的。else语句规则只与相邻最近的if锁定为一对,因为第一条if语句里的if语句是另一条判断,而且并没有括号括起来来隔绝与外界的联系,所以第二个if和else锁定了,第一个if语句为假,根本就不可能进行下面的判断,所以什么都不打印。
这样看就知道为什么会什么结果都不打印了吧?
如果想要解决这个问题用括号括起来就行:
2、关系操作符
C语言用于比较表达式,称为"关系表达式"(relational expression),里面使用的运算符称为"关系运算符"(relational operator),主要有下面6个。
- >大于运算符
- <小于运算符
- >=大于等于运算符
- <=小于等于运算符
- ==相等运算符
- !=不等运算符
例子:
a > b
a < b
a >= b
a <= b
a == b
a != b
关系表达式通常返回0或1,表示真假。
C语言中,0表示假,所有非零值表示真。比如,20>12返回1,12>20返回假。
常在if语句或while循环判断真假的语句中使用。
比如:
a如果小于b返回1,判断为真,就执行if语句。
注:“==”是相等运算符,在生活中,我们使用=来判断这个数等不等于另一个数,但是在C语言中=号为赋值操作符,为了能够分清楚赋值和判断,C语言规定==为判断。
关于关系操作符的使用需要避免的那些错误:
注意,在使用相等运算符==时,建议将常量例如整数3放到前面,将变量放到后面进行判断,3==a;为什么?如果将变量放到a放到前面,常量3放到后面进行比较并不会有什么问题,a==3;但是如果哪一次不小心将相等运算符写成赋值运算符时a=3进行判断,a被赋值成了3,3为真就进入语句,a不仅被赋值为3,关键是这样编译器也不会报错,认为你这就是正常的给变量赋值再进行判断
如果想要解决这个问题就将常量写在前面进行判断,就算不小心将==写成赋值=,也会报错,因为编译器会认为你是在给常量赋值,常量3就是一个整数,怎么能够给整数赋值呢?这不荒唐嘛。
另一个需要避免的错误是:多个关系操作符不宜连用
a < b < c
因为在判断关系表达式时是从左到右进行判断的,比如先判断a是否小于b,为真返回1,为假返回0,最后和c进行判断的要么就是1要么就是0。
(a < b)< c
比如上面这段代码,是先判断a<b,15<20为真返回1,1和c进行判断,1<18,为真最后返回1打印"hehe"。上面代码中的b并不会和a进行判断后就继续于b进行判断了,而是与a进行判断后返回的1或0再与c进行判断。
但是如果就是想连用让b>a,并且b<c,怎么办?这是就要用到&&或||两个操作符。
a<b && b<c
那这个操作符是什么呢?又有什么作用?
3、逻辑操作符&&,| |,!
逻辑操作符提供逻辑判断功能,用于构建更复杂的表达式,主要有下面三个操作符。
- !:逻辑反操作符 (改变单个表达式真假)
- &&:与操作符,是并且的意思 (两侧表达式都为真则返回1,一侧为假就返回0)
- | |:或操作符,是或者的意思 (只要有一侧表达式为真就返回1,两侧表达式都为假才返回0)
3.1 逻辑取反操作符!
什么是逻辑取反操作符?可以参照以上示图,当a返回非0为真时,!a就取反0为假。当a返回0为假时,!a就取反1为真,这就是!逻辑取反操作符的功能介绍。
来两段代码来让我们更加清楚直观的了解逻辑取反操作符:
可以看到a为非0,a进行if语句判断和!a进行判断的结果不同,这就是!逻辑取反操作符,当a为非0时!a就为0,当a为0时!a就为1。
3.2 与操作符&&
看上图当两个变量或表达式返回的值都为非0,&&就会返回1,但是只要有一个为0那&&后就返回0,全都是0的话就更不用说了。举个简单的例子:比如有一个老师要叫两个比较strong的学生来搬桌子 ,说张三与李四一起过来搬,这张桌子就能搬得动。如果只有李四来了,李四说这个桌子太沉搬不了,如果都不来桌子还是没人能搬。
给一段代码来了解一下&&:
可以看到给month输入3、4、5都可以通过month>=3&&month<=5,因为它们都符合这两个表达式的条件,所以返回值都为1,就通过。给month输入6就不行了,虽然可以通过month>=3这个关系表达式,返回一个1,但是并不能通过month<=5,所以返回0,&&操作符的判断条件就是两边表达式必须都为1,否则就返回0,不通过就执行else语句。
3.3 或操作符| |
| |与操作符两边只要有一个是非0就返回1,除非两个都是0就返回0。举个例子:又是一张桌子,这次的桌子一个人就可以搬得动,老师说张三或者李四来搬一下桌子,一个人就可以搬。如果两个都来了也可以搬,如果两个人都不来那就没法搬。
像上面的代码可以用&&来固定一个数的范围,这个数只要在这个范围就可以执行,比如a>=1&&a<=100,只要是1-100之间的数都可以通过,那如果我想表示冬季的月份呢?12月、1月、2月,就不能用范围来判断了,怎么办?这时就可以用到与操作符。
只要是12、1、2这三个数的一个就可以通过,3不属于规定的这三个数,执行else。或操作符只要有一个为真就返回1,都为0才返回0。
3.4 练习:闰年的判断
输入一个年份year,判断year是否是闰年
闰年的判断规则:
- 能被4整除并且不能被100整除是闰年
- 能被400整除是闰年
代码解析:如果第一个判断(year%4==0&&year%100 !=0)不通过返回0, 0 | |(year%400==0),就判断第二个,如果都没通过那当前输入的整数就不是闰年。
3.5 短路
C语言逻辑运算符还有一个特点,它总是先对左侧的表达式求值,再对右边的表达式求值,这个顺序是保证的。如果左边的表达式满足逻辑运算符的条件,就不再对右边的表达式求值。这种情况称为 "短路"。
比如前面的代码:
if(month>=3 && month<=5)
如果因为是先从左边表达式开始运行的,先判断左边表达式,如果左边表达式为真,就继续判断右边的表达式。如果左边表达式为假就不会再继续判断有边的表达式了,因为如果有一个表达式为0就返回0,因为左边表达式已经是0了,所以计算机就会偷个懒不再判断右边的表达式,这就被称之为短路。
那对于| |运算符是怎么样的呢?结合前面的代码:
if(month==12 || month==1 || month==2)
判断month==12为假就继续向后执行判断,如果为真就不会继续向右判断。
对于| |操作符来说左操作数为真时,右操作数也就不会执行了。
对于这种仅仅根据左操作数的结果就能知道整个表达式的结果,不在对右操作符进行计算的运算符称为短路求值。
4、switch语句
除了if语句外,C语言还提供了一种分支语句叫做switch。这个语句的拼写给人的第一印象就是任天堂的Switch。
,所以要想记住这个语句并不难,只需要记住任天堂Switch拼写和这个语句是一模一样的就可以了。
switch是一种特殊的if ..else结构,用于判断条件有多个结果的情况。它把多重else if改成更易用、可读性更好的形式。
switch(expression){
case value1: statement
case value2: statement
default: statement
}
- switch后的expression必须是整型表达式
- case后的值必须是整形常量表达式
4.1 switch
switch中文被称为转换,就是通过变量或表达式的值来转换成对应的分支,例如:
这就是分支转换,就是根据switch括号里变量或表达式求得的值转换到对应路径,并执行。如果括号里最后的值为0,进去就走case 0:分支,如果为1就走case 1:分支,像上面代码中的m%3,77%3的余数为2,进去就直接走case 2:分支,并执行了这条路径的语句,如果找不到对应的分支就执行default分支。
简单理解就是你给switch的值是多少,switch便会转换成你所给的值对应的那个入口。就好比如,你有一张门票2,你给了检票员switch,switch就会给你开放区域2的大门,你就可以进入区域2,如果你的门票数字没有这里对应的区域,就会进入default,就当做出口吧。
4.2 switch中的break
以上所使用的break是永久退出关键字,适用于分支语句和循环语句,为什么每个分支下都要使用一个break来退出呢?这是因为当switch的一条分支执行结束后并不会自动退出当前switch所属区域,而是自动的向下继续执行其他分支,所以开头对应的值找到对应的分支只是让程序找到这个分支的入口,前面的几个分支程序路过了但是不是对应分支所以没有执行,直到找到这个分支的入口,就执行里面的语句,执行完后可以顺着下面的分支继续执行。由此可见switch的分支是和if else分支是有差异的。
解决方法:为了避免以上的情况请在使用switch分支时让每一条分支结尾都是用break永久退出来跳出switch语句。
4.3 练习:打印对应日期
输入一个1-7的数字,打印出对应的星期几
如果编程题目需求发生改变例如:
- 输入:1-5 输入:6-7
- 输出:工作日 输出:休息日
代码解析:这段代码就是利用switch执行完一个分支会继续执行下一个分支这种特性,来达成编程题目的要求。
4.4 switch中的default
在使用switch语句时我们会遇到一种情况,就是传进去的值没有能够与之对应的switch中的case 语句,如果遇到以上情况要么就不处理,直接跳过switch语句,要么就在switch语句最后加上default是子句,上面的代码基本上都用到了default语句。
注:case语句和default语句的顺序规则,default并不一定就只能在最后定义,可以在开头定义,也可以在中间定义。只不过加在最后让代码有更加好的阅读性。
5、while循环
C语言提供了三种循环分别是:while、for和do while,接下来介绍一下while循环。
5.1 if 和while的对比
首先来看一下while循环和if语句的对比:
if(表达式)
语句;
while(表达式)
语句;
可以看到while类似if语句,因为while也需要进行判断,但是它们的区别在于if语句判断后只执行一次,while判断后就可以循环多次。
注:while循环每次执行结束就需要再进行一次判断,为真则继续执行。
5.2 while的执行流程
但是while可能会受到break或continue的影响,break可以永久退出,如果遇到break是会直接跳出循环。
5.3 while循环的实践
练习:在屏幕上打印1-10的值
注意:需要一个随时变化的循环变量,比如上面的变量i,就是while的循环变量,如果i<=10(等价于i<11)就循环,但是也不能一直循环,所以在while语句里需要 i 不停的变化,每循环一次i 就+1,直到大于10循环结束。
5.4 练习:打印值的每一位
将一个数值里的每一位进行打印
例如:
输入:1234,输出:4 3 2 1
输入:521,输出:1 2 5
代码解析:以 val 作为判断条件,每循环一次取出val当前数值的个位进行打印并除以10,直到 / 成0,循环结束。
6、for循环
for循环是三个循环中最常使用的一种循环。
6.1 语法形式
for(表达式1;表达式2;表达式3)
语句;
- 表达式1 用于循环变量的初始化
- 表达式2 用于循环结束条件判断
- 表达式3 用于循环变量的调整
6.2 for循环的执行流程
首先执行表达式1,负责给循环变量进行初始化,紧接着执行表达式2用循环变量进行判断,如果==0就直接结束,如果!=0则继续执行里面的循环语句,执行完语句就执行表达式3调整循环变量,然后再判断,==0结束,!=0则继续执行,循环往复,直到判断为==0时则结束循环。也可以中途利用break来跳出循环。
整个循环过程中,表达式1初始化只被执行一次,表达式2和表达式3跟着循环而执行。
6.3 for循环的实践
练习:在屏幕上打印1-10的值
6.4 while循环和for循环的对比
for和while都是拥有初始化、判断、调整这三个部分,可以看出for循环是将三个表达式集成一体的循环,便于代码维护,而如果代码较多的时候while循环的三个部分就比较分散,所以从形式上for循环要更优一些。
6.5 练习
练习:计算1-100之间3的倍数的数字之和
7、do-while循环
7.1 语法形式
在循环语句中do while语句使用的最少,它的语法如下:
do{
语句;
}while(表达式)
while和for是先判断后循环,而do while是先进入循环体,语句执行结束后再判断,这也是do while语句的特点。
7.2 do while循环流程
进入do while首先执行语句,执行完后进行判断,==0结束循环,!=0则继续执行下一次语句,中途如果遇到break也可以直接结束循环。
do while循环体至少是要执行一次的,因为是先执行后判断,这是do while比较特殊的地方。
7.3 练习
输入一个正整数,计算这个整数是几位数?
例如:
输入:1234 输出:4
输入:12 输出:2
这里并不一定非要使用do while,只是如果输入的值为0,0也是一位数,但是判断时不能通过,所以这种情况可以使用do while,因为是先执行后判断,所以至少循环一次。
8、break和continue语句
在循环执行的过程中,如果某些状况发生时需要提前终止循环,这是非常常见的现象。C语言中提供了break和continue两个关键字,就是应用到循环中的。
- break的作用是永久的终止循环,只要break被执行,就会直接跳出循环。
- continue的作用是跳过本次循环后面的代码,在for循环和while循环中是有差异的。
8.1 break
可以看到当i判断是否等于5为真时,就终止了循环,只打印了1-4的数值。
8.2 continue
当i==5是执行continue,continue就跳过本次循环后面的代码,所以除了5其他数值都打印了。
9、循环的嵌套
前面学习了三种循环:while、for、do while,有时候这三种循环嵌套在一起才能更好的解决问题,就是我们所说的:循环嵌套,这里我们就看一个例子。
9.1 练习1: 找出100-200之间的素数
找出100-200之间的素数
注:素数又被称之为质数,只能被1和本身整除的数字
以上代码用到了一个新的库函数叫做sqrt开平放,听名字就知道是数学函数所以所包含头文件就是#include <math.h>,比如:sqrt(36)就是求出36的开平方6并返回
以上代码所使用的就是循环嵌套。
9.2 练习2:打印乘法口诀表
10、goto语句
C语言提供了一种非常特别的语法,即使goto语句和跳转标号,goto语句可以实现同一个函数内跳转到设置好的标号处。
goto的用法:
因为break终止循环仅限于当前循环,当遇到嵌套循环时没有办法一次性跳出所有循环,黄色闪光波风goto说:“这些对我来说都是小case(小问题)。”直接扔出一个飞雷神(again标号),一下子就瞬移到了循环外面。
讲到这里相信大家对goto语句也有了清晰的认知
11、猜数字游戏
写一个猜数字游戏
游戏要求:
- 电脑自动生成1-100之间的随机数
- 玩家猜数字,猜数字的过程中,根据猜测数据的大小给出大了还是小了的反馈,直到猜对,游戏结束
11.1 随机数生成
要想完成猜数字游戏就,首先得产生随机数,那怎么产生随机数呢?
11.1.1 rand
C语言提供了一个函数叫rand,这函数是可以生成随机数的,函数原型如下所示:
int rand(void);
rand函数会返回一个伪随机数,这个随机数的范围是0-RAND_MAX之间,这个RAND_MAX的大小是依赖编译器上实现的,大部分编译器上的是32767。
rand函数的使用需要包含一个头文件是:stdlib.h
那我们就测试一下rand函数,这里多调用几次,产生5个随机数:
#include <stdio.h>
#include <stdlib.h>
int main()
{
printf("%d\n", rand());
printf("%d\n", rand());
printf("%d\n", rand());
printf("%d\n", rand());
printf("%d\n", rand());
return 0;
}
我们可以看到虽然一次运行中产生的5个数字是相对随机的,但是下一次运行程序生成的结果和上一次一模一样,这就说明有点问题。
其实rand生成的随机数是伪随机数,伪随机数并不是真正的随机数,是通过某种算法生成的随机数。真正的随机数是无法预测下一个值是多少的。而rand是对一个叫“种子”的基准值进行运算生成的随机数。
之所以前面每次运行的程序产生的随机数序列是一样的,那是因为rand函数生成的随机数的默认种子是1。如果要生成不同的随机数,就要让种子是变化的。
11.1.2 srand
C语言又提供了一个函数叫srand,用来初始化随机数的生成器(种子)的,srand的原型如下:
void srand(unsigned int seed);
程序在调用rand函数之前先调用srand函数,通过srand函数的参数seed来设置rand函数生成随机数时的种子(因为srand就是对一个默认种子1进行运算生成随机数的,如果要生成不同的随机数,就要让种子是变化的,而srand函数就是用来设置种子的,所以当使用srand改变种子(基准值)后,rand才能生成真正意义上的随机数)。只要种子在变化,每次生成的随机数序列也就在变化。
那也就是说给srand的种子如果是随机的,rand就能生成随机数;在生成随机数的时候有需要一个随机数,这就矛盾了。
不一定非要给srand一个随机数才能让rand生成随机数,给srand一个随时变化的值也可以使rand生成随机数。
11.1.3 time
在程序中我们一般使用程序运行的时间作为种子的,因为时间在时刻的发生变化。
在C语言中有一个函数叫time,就可以获得这个时间,需要的包含为#include <time.h>,time函数原型如下:
time_t time(time_t* timer);
time函数的返回值是计算机的起始时间1970年1月1日0分0秒与程序此时运行的时间之间的差值,单位是秒,这个差值迄今为止已经有17亿这么庞大的数值,而且这个差值是每一秒都在变化的,所以这个差值也被称为时间戳。返回的类型是time_t类型的,time_t类型本质上其实就是32位或者64位的整型类型。
time函数的参数:
time函数的参数timer如果是非NULL指针的话,函数也会将这个返回的差值放在指向的内存中带回去。如果timer是NULL,就只返回这个时间的差值。
time(NULL);
知道了上面的srand可以修改种子,但是又需要一个随时变化的数。time函数刚好就是返回时间差,我们可以将time函数返回的值作为参数传给srand修改种子(基准值)。
srand((unsigned int)time(NULL));
这样成功修改了种子后,使用rand生成的随机数序列也就是真正的随机数了。
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
int main()
{
srand((unsigned int)time(NULL));
printf("%d\n", rand());
printf("%d\n", rand());
printf("%d\n", rand());
printf("%d\n", rand());
printf("%d\n", rand());
return 0;
}
可以看到两次运行的rand随机数序列各不相同,就是因为种子是变化的,生成的随机数也在变化。
11.1.4 设置随机数的范围
如果我们要生成0-99之间的随机数,方法如下:
rand()%100;//因为rand当前生成的随机数%100的余数就是0-99之间
如果想要生成1-100之间的随机数,方法如下:
rand()%100+1;//如果rand%100余0加1就是1,如果%100余99加1就是100
如果想要生成100-200之间的随机数,方法如下:
100+rand()%(200-100+1);
按理说200-100+1直接写成100+1不是更好吗?为什么要写成200-100+1呢?因为这可以看作一个公式,如果想要求数值a到数值b之间的范围就可以使用这个公式。
a+rand()%(b-a+1);
100+rand()%(1000-100+1); //1-1000范围内的随机数
11.2 猜数字游戏的实现
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
void menu()
{
printf("******************\n");
printf("***** 1.play *****\n");
printf("***** 0.exit *****\n");
printf("******************\n");
}
void game()
{
//1.生成随机数
int randata = rand() % 100 + 1;
int data = 0;
//2.猜数字
while (1)//死循环
{
printf("请猜数字:>");
scanf("%d", &data);
if (data < randata){
printf("猜小了\n");
}
else if (data > randata){
printf("猜大了\n");
}
else{
printf("恭喜你,猜对了\n");
break;
}
}
}
int main()
{
srand((unsigned int)time(NULL));
int input = 0;
do
{
menu();
printf("请选择:>");
scanf("%d", &input);
switch (input)
{
case 1:
game();
break;
case 0:
printf("退出游戏\n");
break;
default:
printf("选择错误,请重新选择\n");
break;
}
} while (input);
return 0;
}
运行结果:
C语言第3篇笔记到这里也就结束了,有什么问题请在评论区留言或私信作者,再见。