公司动态
C语言入门 | 从真假判断到goto,一条逻辑线学完分支与循环全部核心
目录一、真假判断和三种操作符1.真假判断2.三种操作符二、分支结构选择结构1.if 语句2.switch 语句三、循环结构1.while 循环2.for 循环3.do-while循环4.break和continue语句5.循环的嵌套四、goto语句前言这篇文章的目标是帮助小伙伴们学习分支和循环这一块知识主打构建清晰的逻辑线知识与知识之间是怎么联系在一起的也加入了自己的一些理解讲解不到之处请大家批评指正希望能对大家有帮助。一、真假判断和三种操作符1.真假判断由于分支结构和循环结构都涉及到要判断某个条件的真假所以我认为是很有必要先讨论真假的。笔者在初学C语言的时候就对这个环节一度感到十分困惑。但是不得不承认C语言的学习拓展了我对于真假的认知范围。为什么这么说呢因为C语言规定0为假非0为真。这和我们以往的认知似乎很不一样。真假怎么能是单独的数字呢应该是去判断一个条件是否成立才能得出真假吧。比如13 这个明显就是假再比如156这个显然是真。笔者当时的困惑就在于此。后来才知道机器始终不能和人脑相比它们只能识别数字再复杂的条件最终都会返回0或非0进行判断。因此单独的数字也有真假这件事情就不足为奇了。2.三种操作符现在我们已经清楚了C语言真假判定的底层规则。但仅仅依靠单纯的数字怎么能够敲出能实现一定功能的代码呢我们想要的程序肯定是具有复杂逻辑的不然何谈解放大脑呢接下来我们就来学习三种操作符因为复杂逻辑的实现依赖于这些操作符。2.1关系操作符C语言用于比较的表达式称为“关系表达式”里面使用的运算符就称为“关系运算符”。主要有以下6个 大于 小于 大于等于 小于等于 等于 !不等于关系表达式通常返回0或1表示真假。比如23返回032返回1。这里演示一段代码会提前用到if语句不会的小伙伴也可以先学习下面的if语句#include stdio.h int main() { int x 3; if (x 3) printf(x is 3.\n); return 0; }上面这段代码中“x3”这个表达式成立返回1表示真。如果不成立呢#include stdio.h int main() { int x 4; if (x 3) printf(x is 3.\n); return 0; }表达式不成立返回0表示假那自然就不执行语句了。注意1不要混淆赋值运算符 和相等运算符 。如果用错会怎么样先试试再说#include stdio.h int main() { int x 4; if (x 3) printf(x is 3.\n); return 0; }运行的结果和我们的想法不一致。为什么呢因为x3代表对变量x赋值3而这种赋值表达式的返回值就是赋予变量的数值。 3是非0所以条件判断为真执行语句。为了防止这种低级错误的出现也可以将变量写在等号的右边这样一来如果不小心用成了赋值符号编译器就会报错因为它肯定不懂把变量赋给数值是什么意思。注意2关系操作符除了可以判断数字还可以判断字符比如‘a’b这里实际比较的是ASCII码。那字符串呢不可以字符串的比较要用到strcmp函数下文goto语句的讲解会使用到。注意3多个关系运算符不宜连用。老规矩如果连用会怎么样int main() { int age 0; scanf(%d, age); if (18 age 36) printf(青年\n); return 0; }输入10也是青年这不符合我们想要的逻辑啊。为什么呢C语言中判断表达式18age36的逻辑和我们中学学过的数学逻辑可不一样编译器先判断18age1810显然不成立返回0再判断036成立返回1因此执行语句。总结一下如果C语言代码中出现表达式 ijk ,最终得到的结果只会是(ij)k而不会是我们想要的那种效果因为关系运算符是从左到右计算的。那么如何正确表达我们想要的意思呢那就涉及到下面的逻辑操作符了。2.2逻辑操作符逻辑操作符可以构建更复杂的表达式主要有下面3个逻辑与运算符就是并且的意思。“全真才真一假即假”||逻辑或运算符就是或者的意思。“一真即真全假才假”!逻辑取反运算符就是改变单个表达式的真假下面我们用游乐园的门票规则一次性看懂逻辑操作符规则年龄14岁或者身高1.4米必须够买成人票。每个人进入游乐园之前都要现场测量身高体重。代码要求售票员会将身高和体重两个数值输入电脑当输入的数值需要够买成人票时屏幕上打印“需要购买成人票”。很明显成人票的要求是年龄14岁或者身高1.4米用逻辑或 || 连接#include stdio.h int main() { int age 0; float height 0; scanf(%d%f, age,height); if (age14||height1.4) printf(需要购买成人票\n); return 0; }现在假设想实现的功能反了一下当输入的数值不需要够买成人票时屏幕上打印“儿童票”我们进行一个简单的推理就可以得出不需要够买成人票的条件是年龄14岁并且身高1.4米用逻辑与连接#include stdio.h int main() { int age 0; float height 0; scanf(%d%f, age,height); if (age14height1.4) printf(儿童票\n); return 0; }除了这种操作之外还有别的方法吗当然是有的那就是逻辑取反操作。仔细思考就会发现“年龄14岁并且身高1.4米”和“年龄14岁或者身高1.4米”是完全相反或者说完全互补的条件。因此(age14||height1.4)会使得原来真的数据假了原来假的数据真了而要使我们这次的条件为真就要得到使原来表达式结果为假的数据。#include stdio.h int main() { int age 0; float height 0; scanf(%d%f, age,height); if (!(age14||height1.4)) printf(儿童票\n); return 0; }可能会有小伙伴疑惑既然可以反过来写条件为什么非要用逻辑取反因为可以直接复用现成条件不用重复书写。就像上面的代码两处条件同步维护容易写错。下面是逻辑操作符的最后一个知识点短路其实逻辑运算符还有一个特点它总是先对左侧的表达式求值再对右侧的表达式求值。如果左边的表达式能够确定整个式子最终的真假时就不再进行右边的计算。这种情况称为“短路“。对于逻辑与运算符全真才是真只要左边是假整个式子就一定是假右边的表达式就不执行。对于逻辑或运算符有真就是真只要左边是真整个式子就一定是真右边的表达式就不执行。直接上一道题计算下列代码输出的结果#include stdio.h int main() { int i 0, a 0, b 2, c 3, d 4; i a b d; printf(a %d\nb %d\nc %d\nd %d\n, a, b, c, d); return 0; }这道题是逻辑与a借先使用后自增1所以判断的条件是0而不是10代表假由于逻辑与遇到假就不再往后计算了所以最终得到上述结果。#include stdio.h int main() { int i 0, a 0, b 2, c 3, d 4; i a||b||d; printf(a %d\nb %d\nc %d\nd %d\n, a, b, c, d); return 0; }这道题是逻辑或a还是一样先使用后自增1判断的条件为假由于是逻辑或还不足以判断结果所以继续往后计算b这里b是先自增1后使用b自增1变成3非0为真逻辑或遇到真后面就不再计算了因此得到上述结果。2.3条件操作符条件操作符也叫三目操作符需要接受三个操作数语法形式exp1?exp2:exp3逻辑先判断exp1是否为真。如果真则计算exp2计算的结果是整个表达式的结果如果假则计算exp3计算结果是整个表达式的结果。举例说明例1使用条件表达式实现找两个数中较大值#include stdio.h int main() { int a 0; int b 0; scanf(%d%d, a, b); int max a b ? a : b; printf(%d, max); return 0; }例2使用条件操作符表示下面的代码逻辑这里涉及到了if else语句没接触过的小伙伴也可以先跳过等学习完再回头看。很明显这条if else语句可以被一个条件表达式代替。a5吗真的b3假的b-3。#include stdio.h int main() { int a 0; int b 0; scanf(%d, a); b a 5 ? 3 : -3; printf(%d, b); return 0; }好了到此为止我想小伙伴们对于真假的判断应该是游刃有余了。下面就可以轻松学习各种不同的语句了二、分支结构选择结构首先我们来了解分支结构。这种结构的机制用大白话来解释就是先进行判断如果是真就执行相对应的操作如果不满足就跳过这项操作接着往下。C语言在这里提供了两种语句if语句和 switch语句 。我们分别来进行讨论1.if 语句1.1 单独1条if语句语法形式if表达式语句;逻辑表达式为真语句执行表达式为假语句不执行。例输入一个整数判断是否为奇数#include stdio.h int main() { int num 0; scanf(%d, num); if (num % 2 1) printf(奇数\n); return 0; }不过上面的代码只能在满足条件时执行语句如果我们想要设定不满足条件时执行另外一条语句呢当然也是可以的。1.2 if…else…语句语法形式if表达式语句1else语句2逻辑表达式为真执行语句1表达式为假执行语句2。例输入一个年龄18就输出成年否则就输出未成年#include stdio.h int main() { int age 0; scanf(%d, age); if (age 18) printf(成年); else printf(未成年); return 0; }注意if 和 else 语句默认都只控制一条紧随其后的语句。例1if 后#include stdio.h int main() { int score 0; scanf(%d, score); if (score 60) printf(及格了\n); printf(可以获得基础学分\n); return 0; }不要被格式所迷惑if语句如果不加{ }就是只能控制紧随其后的一条完整语句if…else…语句也是一条完整语句。那else呢其实也是一样的例2else后#include stdio.h int main() { int score 0; scanf(%d, score); if (score 60) printf(及格了\n); else printf(不及格\n); printf(需要参加补考\n); return 0; }果然在我们的预料之中那如果想要它们控制不只一条语句呢方法很简单只需要加上 { } 即可#include stdio.h int main() { int score 0; scanf(%d, score); if (score 60) //if 后使⽤{} 控制多条语句-这个块也叫程序块或者复合语句 { printf(及格了\n); printf(可以获得基础学分\n); } else //else 后使⽤{}控制多条语句-这个块也叫程序块或者复合语句 { printf(不及格\n); printf(需要参加补考\n); } return 0; }1.3 嵌套if出现else if在 if…else… 语句中else里面可以再嵌套 if…else… 语句依此类推可以无限嵌套下去。例输入一个整数判断是0还是正数还是负数#include stdio.h int main() { int num 0; scanf(%d, num); if (num 0) printf(0\n); else { //这里不加{}逻辑也没有问题因为else紧跟的完整语句就是if…else…语句只是加了逻辑更清晰 if (num 0) printf(正数\n); else printf(负数\n); } return 0; }大家注意上面才是 else if 最本来的面貌。这么写当然可以但是我们更通用的写法是下面这种就是把嵌套的 if 搬上去和 else 同一行非常简洁明了#include stdio.h int main() { int num 0; scanf(%d, num); if (num 0) printf(0\n); else if (num 0) //如果还有更多的分支可以使用更多的else if printf(正数\n); else printf(负数\n); return 0; }下面这张图是我个人对这几种常见if语句的理解可能有理解不到位的地方请大家批评指正下面是if语句最后一个小知识点悬空else问题嵌套if不只可以在else分支内也同样可以在if分支内使用像这样#include stdio.h int main() { int a 0; int b 2; if (a 1) { if (b 2) printf(hehe\n); else printf(haha\n); } return 0; }那么这里很明显因为a等于0所以if语句判断为假直接结束程序啥都不打印如果一模一样的程序我换一种写法小伙伴们还能准确识别逻辑吗#include stdio.h int main() { int a 0; int b 2; if (a 1) if (b 2) printf(hehe\n); else printf(haha\n); return 0; }这就是所谓的悬空else问题如果多个if后面跟一个elseelse总是跟最接近的 if 匹配。千万不要被格式所迷惑了哦~另外一个角度也能解释前面的学习我们知道if语句如果不加{}就是只能控制紧随其后的一条完整语句。本例中后面的if…else…就是完整的一个语句并且紧跟在if(a1)后面当不满足这一条件的时候程序就直接跳过嵌套if。2.switch 语句除了if语句外C语言还提供了switch语句来实现分支结构。语法形式switch(expression)expression必须是整型表达式{case value1:statementcase后的值必须是整型常量表达式case value2:statementcase和后面的数字之间必须有空格…default:statement}逻辑根据表达式expression不同的值执行相应的case分支。如果找不到对应的值就执行default语句先来见识一下它如何使用例1输入任意一个整数值计算除3之后的余数#include stdio.h int main() { int input 0; scanf(%d, input); switch (input % 3) { case 1: printf(余数是1\n); case 2: printf(余数是2\n); case 0: printf(整除余数是0\n); } return 0; }这显然不是我们要的结果。那这是怎么回事呢原来只有在switch语句中使用break才能跳出switch语句。如果case的语句后面没有break语句代码会继续往下执行遇到其他case中的语句也会执行直到遇到break语句或者switch语句结束。加上break再来看看#include stdio.h int main() { int input 0; scanf(%d, input); switch (input % 3) { case 1: printf(余数是1\n); break; case 2: printf(余数是2\n); break; case 0: printf(整除余数是0\n); break; //最后一个也可以不加但建议加上 } return 0; }那是任何情况下都要加break吗答不是要看需求例2输入1~5输出“工作日”输入6~7输出“休息日”#include stdio.h int main() { int day 0; scanf(%d, day); switch (day) { case 1: case 2: case 3: case 4: case 5: printf(工作日\n); break; case 6: case 7: printf(休息日\n); } return 0; }那如果用户输入了除1~7之外的数字比如“8”会怎样大家可以猜一下没错就是你想的那样程序直接结束。那如果我们想提示用户输入错误该怎么实现呢这就轮到default上场了。#include stdio.h int main() { int day 0; scanf(%d, day); switch (day) { case 1: case 2: case 3: case 4: case 5: printf(工作日\n); break; case 6: case 7: printf(休息日\n); default: printf(输入错误\n); } return 0; }三、循环结构C语言共提供了3种循环分别是while循环for循环do-while循环下面分别来介绍。PS循环的学习需要多多练习因此以下每种循环都会举2个例子。第一个比较简单且都一样主要是学习一下如何使用第二个稍有挑战当做练习可以先自己敲一遍再看答案1.while 循环语法形式while(表达式)语句如果循环体想包含更多的语句可以加上大括号逻辑表达式为假跳过循环表达式为真进入循环执行语句执行完再上来判断表达式是否为真。如此这样循环往复。例1在屏幕上打印1~10的值#include stdio.h int main() { int i 1; while (i 10) { printf(%d , i); i; } return 0; }例2输入一个正的整数逆序打印这个整数的每一位。例如输入1234输出4 3 2 1思路1.首先取出个位数只需要该数字%10即可得到2.再取出百位数需要该数字先/10去掉个位使原来的百位变成个位再%103.重复第2步操作。#include stdio.h int main() { int input 0; scanf(%d, input); while (input) { printf(%d , input % 10); input / 10; } return 0; }2.for 循环语法形式for(表达式1;表达式2;表达式3)表达式之间是分号语句如果循环体想包含更多的语句可以加上大括号{ }逻辑表达式1用于循环变量的初始化表达式2用于循环结束条件的判断表达式3用于循环变量的调整例1在屏幕上打印1~10的值#include stdio.h int main() { int i 0; for (i 1;i 10;i) { printf(%d , i); } return 0; }下面我们根据这个例子来对比一下for循环和while循环从图中可以看出for循环和while循环都有初始化、判断、调整这三个部分但是for循环的三个部分非常集中便于代码的维护因此形式上for循环要更优一些。同时日常写代码for循环的应用频率也是高于while循环的。例2计算1~100之间3的倍数的数字之和思路首先是要找出3的倍数那么就需要判断每个数%3是否等于0如果是的则这个数就是3的倍数要加起来。每个数都进行判断这就是循环。#include stdio.h int main() { int i 0; int sum 0; for (i 1;i 100;i) { if (i % 3 0) sum i; } printf(%d\n, sum); return 0; }这里我们进行一个小小的优化如果能直接产生3的倍数的数字就省去了多余的循环#include stdio.h int main() { int i 0; int sum 0; for (i 3;i 100;i3) { sum i; } printf(%d\n, sum); return 0; }注意for(表达式1;表达式2;表达式3)这里的3个表达式初始化、判断、调整是可以根据实际情况省略的但分号必须保留;C99标准也支持表达式1中创建循环变量比如for(int i 0;表达式2;表达式3)但是这样 i 的使用范围就变小了只能在循环内部使用。这里不详细展开感兴趣的小伙伴可以自行尝试。3.do-while循环这是用得最少的一种循环但很适用于那些至少要执行1次的循环程序。因为while和for都是先判断条件满足再进入循环而do-while是不管三七二十一先执行一次然后再判断。语法形式do语句如果循环体想包含更多的语句可以加上大括号{ }while(表达式);逻辑先直接进入循环体执行循环语句然后再执行while后的判断表达式是真则再进入循环体是假则不再循环。例1在屏幕上打印1~10的值#include stdio.h int main() { int i 1; do { printf(%d , i); i; } while (i 10); return 0; }例2输入一个正整数计算这个整数是几位数思路这道题并不是一定要用do-while但是正整数至少会有一位所以循环至少会被执行一次。从上文while讲解的例2可知某个数字每次/10就会砍掉个位数。因此我们可以设置一个变量来计数初始化0每次砍掉一位数就加一。每一次循环将砍掉个位的数字作为判断条件如果还不为0说明还有数字继续循环如果为0说明已经没有数字了跳出循环。#include stdio.h int main() { int count 0; int num 0; scanf(%d, num); do { num / 10; count; } while (num); printf(%d\n, count); return 0; }4.break和continue语句在循环执行的过程中有些时候我们需要提前终止循环或者跳过某次循环这是非常常见的C语言提供了break和continue两个关键字来实现这类功能。break字面意思是“休息”作用是永久的终止循环continue字面意思是“继续”作用也确实是跳过本次循环中continue后面的代码继续执行下一个循环。下面我们分别来探索这两个关键词在不同的循环中如何发挥作用4.1当while遇上break和continuebreak:#include stdio.h int main() { int i 1; while(i10) { if (i 5) break; printf(%d , i); i; } return 0; }continue:#include stdio.h int main() { int i 1; while(i10) { if (i 5) continue; printf(%d , i); i; } return 0; }break的效果如我们所料但是continue的效果和我们的预期有所差别问题在哪里呢仔细观察会发现continue把调整循环变量的那一步也跳过了导致每次if语句判断时 i 始终等于5条件成立执行continue。如此循环往复程序永远没有结束的时候因此陷入死循环。4.2当for遇上break和continuebreak:#include stdio.h int main() { for (int i 1;i 10;i) { if (i 5) break; printf(%d , i); } return 0; }continue:#include stdio.h int main() { for (int i 1;i 10;i) { if (i 5) continue; printf(%d , i); } return 0; }for循环中break的效果和while循环中的没有区别但是continue却截然不同for循环中的coutinue才是我们要的那种效果。为什么呢原因就在于调整循环变量的位置不同。while循环放在循环体里continue会跳过for循环放在循环体外continue不会跳过。图示如下4.3当while遇上break和continuebreak:#include stdio.h int main() { int i 1; do { if (i 5) break; printf(%d , i); i; } while (i10); return 0; }continue:#include stdio.h int main() { int i 1; do { if (i 5) continue; printf(%d , i); i; } while (i10); return 0; }结论do-while循环中break和continue产生的效果和while循环几乎一模一样。5.循环的嵌套上面学习了3种循环也分别举了2个例子但是上面的例子都只涉及到一个循环在实际运用中这三种循环往往嵌套在一起才能更好的解决问题。下面我们通过一个练习来说明题目找出100~200之间的素数并打印在屏幕上思路素数就是除了“1”和本身不能被其他的数字整除了。因此任何一个数我们都要进行试除判断余数是否为0即从2除到比它本身小1的数字内层循环而我们现在不是只有1个数字而是要遍历100~200外层循环。因此这道题就涉及到了循环的嵌套。#include stdio.h int main() { for (int i 100;i 200;i) //产生被除数 { int j 0; for (j 2;j i;j) //产生除数 { if (i % j 0) //试除 break; } /*跳出内层循环有2种情况一种是可以被某个数字整除break跳出来 另一各种是确实是素数不能被任何一个数字整除因此还需要一次判断*/ if (j i) printf(%d , i); } return 0; }思考这段代码还有哪里可以优化呢idea1:素数一定不会是偶数#include stdio.h int main() { for (int i 101;i 199;i2) //被除数只产生奇数 { int j 0; for (j 2;j i;j) { if (i % j 0) break; } if (j i) printf(%d , i); } return 0; }idea2被除数其实没有必要每个都试试到除数的平方根就可以了因为一个数字的两个因数总有一个因数是小于等于该数字的平方根的。但是这样的话内层循环出来后的判断条件就要变了我们在代码中说明#include stdio.h #include math.h //sqrt()函数需要包含的头文件 int main() { for (int i 101;i 199;i2) { int flag 1; //这一步相当于先做一个标记 int j 0; for (j 2;j sqrt(i);j) { if (i % j 0) { //下面这一步的作用是如果不是素数就把原来的标记改掉后面的判断就可以有区分了 flag 0; break; } } if (flag1) //if(flag)也可以因为1就是真0就是假 printf(%d , i); } return 0; }四、goto语句C语言还提供了一种非常特别的语法就是goto语句。goto语句可以实现在同一个函数内跳转到设置好的标号处。先来看一段简短的代码说明它的使用方法#include stdio.h int main() { printf(hehe\n); goto next; //next是跳转标号可以自主命名但需满足语法规则规则和变量命名相同命名最好有意义 printf(haha\n); next: //并不是只能跳到goto语句的下方也可以跳到上方只要在一个函数里面 printf(跳过了haha的打印\n); return 0; }注意goto语句不是一个必需品它能做到的事情通过我们前面所学的知识也能做到而且goto语句如果使用不当就会导致在函数内部随意乱跳转打乱执行流程因此尽量不去使用。但是它仍有自己的用武之地在多层循环的代码中如果想快速跳出使用goto就非常的方便for (...) { for (...) { for (...) { if (disaster) goto error; } } } error:其实上面这段代码break也能做到但break一次只能跳出一层循环意味着如果用break就要用3次才能跳出3层循环在这种情况下用goto就很快捷。最后我们来运用一下goto语句吧来实现一段有意思的代码关机程序1.程序运行起来电脑就开始倒计时1分钟关机2.如果输入我是猪就取消关机知识补充windows上的几个命令1.shutdown -s 立即关机2.shutdown -s -t 60 60秒后关机3.shutdown -a 取消关机#include stdio.h #include stdlib.h //system函数要包含的头文件 #include string.h //strcmp函数要包含的头文件 int main() { char input[20] { 0 }; //字符数组 system(shutdown -s -t 60); //system函数用来执行系统命令 again: printf(你的电脑将在1分钟内关机如果输入我是猪就取消关机\n); scanf(%s, input); if (strcmp(input, 我是猪) 0) //2个字符串比较相等使用strcmp返回值为0代表字符串相等 system(shutdown -a); else goto again; return 0; }上文提到过goto语句并不是必需品我们下面用while循环和break语句来实现#include stdio.h #include stdlib.h //system函数要包含的头文件 #include string.h //strcmp函数要包含的头文件 int main() { char input[20] { 0 }; //字符数组 system(shutdown -s -t 60); //system函数用来执行系统命令 while (1) { printf(你的电脑将在1分钟内关机如果输入我是猪就取消关机\n); scanf(%s, input); if (strcmp(input, 我是猪) 0) //2个字符串比较相等使用strcmp返回值为0代表字符串相等 { system(shutdown -a); break; } } return 0; }运行结果同上小伙伴们可以自己试一试哦~感谢观看希望能对你有帮助有问题欢迎在评论区留言或者私信完