0基础C语言积跬步之分支与循环(上)
首先声明此博客不照搬抄袭存在自己的见解我所使用的编译器是VS2022目录1.if 语句2.关系操作符3.条件操作符4.逻辑操作符||,5.switch 语句6.while 循环7.for 循环8.while 循环和 for 循环的对比9.do-while 循环10.break 和 continue 语句11.循环的嵌套12.goto 语句首先正文之前我们知道C语言是一个结构化的程序设计语言包括顺序结构选择结构循环结构我们可以使用if和switch语句来控制选择结构用whiledo...whilefor循环语句来控制循环结构下面给出一张三种程序结构的思维导图来大致了解一下三种结构有什么语句1.if语句if语法形式if表达式{语句}表达式的值非0为真进入if语句0为假不进入if语句如判断一个数是否为奇数代码#include stdio.h int main() { int num 0; scanf(%d, num); if(num % 2 1) { printf(%d 是奇数\n, num); } return 0; }如果输入的num是3if判断3%21条件为真就会进入if语句打印3是奇数运行截图那如果输入的是2怎么办呢2是偶数啊这时候我们就需要用到if...else语句if...else语法形式if (表达式){语句;}else{语句;}if中的表达式成立为真则进入if语句而不进入else语句若if中的表达式不成立为假则进入else语句绝对不可能两个都不执行一定会执行其中一个二选一#include stdio.h int main() { int num 0; scanf(%d, num); if (num % 2 ! 0) printf(%d 是奇数\n, num); else printf(%d 是偶数\n, num); return 0; }我们在上述判断奇数的if语句代码中加入了else模块表示若判断奇数的表达式不成立为假那么就进入else语句证明是偶数0也是偶数奇偶数包括负数如-2是偶数-1是奇数运行截图嵌套ifif可以和多个if..else嵌套使用如if{if{ 语句 }else{ 语句}}例如我们写一串代码判断一个数是正数0还是负数#include stdio.h int main() { int num 0; scanf(%d, num); if(num 0) { printf(输入的数字是0\n); } else { // 这里嵌套一个 if 判断正负 if(num 0) { printf(输入的数字是正数\n); } else { printf(输入的数字是负数\n); } } return 0; }以上代码是在else语句中又嵌套了一个if来判断正负咱们可以简化成以下代码#include stdio.h int main() { int num 0; scanf(%d, num); if(num 0) printf(输⼊的数字是0\n); else if(num 0) //这⾥的if 相当于嵌套在else语句中形成了嵌套结构 printf(输⼊的数字是正数\n); else printf(输⼊的数字是负数\n); return 0; }悬空else问题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; }如果你认为会打印haha,那么请看看下方的解释运行截图从图中可以看出什么都没有打印这是因为我们来分析语句a不等于0所以不会从ifa1语句进去很多人在这时候就会误解那既然不进去if那就进去下面的else啊但我们要看清下面的else对应的是哪个if记住规则else 总是跟最接近的 if 匹配除非用花括号括起来进行逻辑限制而上图中的else明显离ifb2最近所以既然ifa1不进去自然也不会进到ifb2里自然也不会进入else里所以该程序什么都没有打印注意if语句花括号问题#include stdio.h int main() { int a 10; if (a 0) printf(cam\n); else printf(haha\n); printf(success\n); return 0; }运行截图明明a等于10大于0应该进入if条件打印cam而已而不会进入else语句打印success才对但最终运行结果却打印了两条语句这是因为一个if语句和一个else语句默认后面只能跟一条语句所以else其实只控制printf(haha\n)这条语句而printf(success\n)无论如何都会打印因为它不归谁管就表示是一条要打印的语句若不想此语句被打印那需要给else加一对花括号如#include stdio.h int main() { int a 10; if (a 0) printf(cam\n); else { printf(haha\n); printf(success\n); } return 0; }运行截图2.关系操作符C语言中常见的关系表达式中里面的操作符被称为关系操作符运算符名称大于运算符小于运算符大于等于运算符小于等于运算符相等运算符!不相等运算符关系表达式通常返回0和10表示假1表示真。如53为真则返回1。32为假则返回0注意相等运算符和赋值运算符可不一样。例如ifa3和ifa3他们所代表的含义可不一样ifa3是判断a是否等于3是的话返回1不是返回0。而ifa3代表把3这个值赋给a那a就等于3a3这个表达式最终结果就为3始终为真不具备判断作用为防止这种少写一个等号的错误情况出现我们一般在写代码的时候写if (3a)因为这样万一我们少写一个等号写成if3a编译器就会提示报错我们就可以及时修改但如果写成ifa3而我们要的是判断a是否等于3这个条件这时候少写一个等号编译器不会报错只会执行赋值操作并把结果当作真从而导致逻辑错误。多个关系运算符不宜连用例如 i j k像这种表达式是先计算ij)k,从左到右计算先得出ij结果的返回值再将返回值和k比较如果i是3j是2那么ij的返回值就是0再将0和k进行比较这样的比较方式不是我们最初原本想要的计算逻辑我们可以这样写例如 ij jk 意思是i小于j 并且 j小于k3.条件操作符三目操作符条件操作符也叫三目操作符有三个操作数形式如下exp1 ? exp2 : exp3他的计算逻辑是如果exp1为真则计算exp2exp2的结果为整个表达式的结果如果exp1为假则计算exp3exp3的结果为整个表达式的结果下面分析一串代码#include stdio.h int main() { int a 0; int b 0; scanf(%d, a); if (a 5) b 3; else b -3; printf(%d\n, b); return 0; }这串代码所要表达的逻辑是输入一个值给a如果a5,则把3的值赋给b如果a5就把-3的值赋给b。这个逻辑也可以写成 a5 ? b3;b-3例如#include stdio.h int main() { int a 0; int b 0; scanf(%d, a); a5 ? b3b-3 printf(%d\n, b); return 0; }但是这样写虽然我们逻辑上是对的但是在编译器里会报错这是因为操作符具有优先顺序三目操作符的优先级高于 所以编译器会理解为(a 5 ? b 3 : b) -3;把-3的值赋给了前面这个表达式而前面这个(a 5 ? b 3 : b)表达式计算结果如果a5为真 → 表达式结果是b 3→ 最终是b本身也就是3如果为假 → 表达式结果直接是b而3-3不能这样赋值b-3也不是我们想要的结果我们想要的逻辑是如果a5,则把3的值赋给b如果a5就把-3的值赋给b。所以我们应该这样写b( a5?3:-3 )#include stdio.h int main() { int a 0; int b 0; scanf(%d, a); b a 5 ? 3 : -3; printf(%d\n, b); return 0; }记住三目优先级高于 但别靠记忆加括号最稳。优先级口诀从高到低算术运算符 →关系运算符→三目运算符→赋值运算符4.逻辑操作符||,逻辑与运算符并且的意思注意exp1 exp2如果当exp1为假时则整个表达式结果都为假会发生逻辑短路不再计算表达式exp2但如果exp1为真才会进入exp2并进行计算此时如果exp2为真则整个表达式结果为真(exp1 exp2)1若exp2为假则整个表达式的结果为假(exp1 exp2)0||||逻辑或运算符或者的意思注意注意exp1 exp2如果当exp1为真时则整个表达式结果都为真会发生逻辑短路不再计算表达式exp2但如果exp1为假就会进入exp2并进行计算此时如果exp2为真则整个表达式结果为真(exp1 exp2)1若exp2为假则整个表达式的结果为假(exp1 exp2)0逻辑取反运算符如3意思就是不等于3如果我们有一个变量flag我们可以有以下操作把它当作if语句的开关使用:#include stdio.h int main() { int flag 0; if(!flag) //因为flag0所以flag就等于1因为1为真这个规定 { printf(do something\n); //会执行语句 } return 0; }如果 flag 为真 !flag 就是假如果 flag 为假 !flag 就是真练习闰年的判断输入一个年份year判断year是否是闰年闰年判断的规则 1. 能被4整除并且不能被100整除是闰年2. 能被400整除是闰年#include stdio.h int main() { int year 0; scanf(%d, year); if ((year % 4 0 year % 100 ! 0) || year % 400 0) { printf(%d是闰年, year); } else printf(%d不是闰年, year); return 0; }5.switch 语句switch语句也是用来实现分支结构的语句语法形式switch (整型表达式) { case 常量1: 语句; break; case 常量2: 语句; break; // 可以有很多 case default: //都不匹配时执行; 语句; break; }注• switch 后的值必须是整型表达式• case 后的值必须是整形常量表达式去掉break会发生的情况#include stdio.h int main() { int n 0; scanf(%d, n); switch (n % 3) { case 0: printf(整除余数为0\n); case 1: printf(余数是1\n); case 2: printf(余数是2\n); } return 0; }运行截图你看三条case语句都执行了这是因为当输入3时3%3等于0从case 0进入会执行 printf(整除余数为0\n)但因为没有break所以程序不会跳出switch语句而是继续执行下面的case 1case 2直到把下面所有代码跑完才跳出switch语句所以会继续执行 printf(余数是1\n)和 printf(余数是2\n)default子句当switch后面表达式的值没法匹配任何一条case子句中的值时就会进入default子句执行default子句的内容如输入周一到周五则打印为工作日输入周六到周日则打印为休息日#include stdio.h int main() { int day 0; while (1) { scanf(%d, day); switch (day) { case 1: case 2: case 3: case 4: case 5: printf(工作日\n); break; case 6: case 7: printf(节假日\n); break; default: printf(输入错误请输入1-7的值\n\n); break; } printf(\n); } return 0; }运行截图注意上方代码我们可以捕获一个知识点就是我们在case中写的语句和使用的break时是可以根据实际情况来决定是否要写的如上方我们要的是周一到周五都是工作日我们就可以省去每一个case后面都写printf(工作日\n)语句而总和在case 5后面写这样输入1-4时都会打印工作日达到我们想要的结果所以我们发现应该根据实际的情况来决定代码中是否使⽤ break 或者在哪⾥使⽤ break 以来完成实际的需求。而上述代码当我们输入数字8时没有任何case后面的整型常量表达式与之匹配所以会进入default子句执行printf(输入错误请输入1-7的值\n\n)语句#include stdio.h int main() { int day 0; while (1) { scanf(%d, day); switch (day) { default: printf(输入错误请输入1-7的值\n\n); break; case 1: case 2: case 3: case 4: case 5: printf(工作日\n); break; case 6: case 7: printf(节假日\n); break; /*default: printf(输入错误请输入1-7的值\n\n); break;*/ } printf(\n); } return 0; }default子句不一定放在最后只要保证逻辑正确放在开头也可以如果上述代码逻辑一样正确不过我们通常是把 default子句放在最后处理的6.while 循环while语法形式while(表达式) { 语句; }while循环循环规则while循环表达式中的值为真则进入while循环语句为假则退出while循环语句首先上来就是执行判断表达式表达式的值为 0循环直接结束表达式的值不为 0则执行循环语句语句执行完后再继续判断是否进行下一次判断。while循环语句执行流程思维导图下图将用while循环打印数字1-10的代码#include stdio.h int main() { int i 1; while (i 10) { printf(%d , i); //打印1 2 3 4 5 6 7 8 9 10 i i 1; //每次循环i都会加1从1加到10直到加到11时再次判断后直接退出循环 } return 0; }i初始化为1每次循环i都会加1从1加到10直到加到11时再次判断后直接退出循环运行截图7. for循环for循环语法形式for(表达式1;表达式2;表达式3;) { 语句; }表达式1用于循环变量的初始化如int i 0表达式2用于循环结束条件的判断如 i 10表达式3用于循环变量的调整如 i注意for循环中每个表达式之间是用分号;间隔开而不是逗号for循环循环规则for循环循环规则思维导图首先进入表达式1初始一下循环变量然后进入表达式2判断初始的循环变量是否符合条件不符合为假直接跳出循环符合为真继续执行循环体语句执行完后执行表达式3更新循环变量然后再次执行表达式2开始判断循环条件是否符合就这样循环下去直到循环变量不再符合表达式2的要求则跳出循环下图将用for循环打印数字1-10的代码#include stdio.h int main() { int i 0; for (i 1; i 10; i) { printf(%d , i); } return 0; }运行截图注意for循环的判断条件里如果是逗号表达式表达式1表达式2如下图代码中i10,a2#include stdio.h int main() { int i 0; int a 0; for (i 1, a 0; i 10, a 2; i,a) { printf(%d , i); } return 0; }运行截图只循环一次这是因为当for 循环的循环条件里是逗号表达式只看最后一个表达式如上图中只看a2这个判断循环条件但是初始化是正常的i1a0都正常初始化后面更新循环变量也都正常更新i会a也会所以当a0时a2进入循环体此时i1打印1然后i为2a为1此时再次进行判断a2,再次进入循环体因为i此时等于2所以打印2然后i变为3a变为2此时再次进入判断发现a不小于2了于是退出了循环自始自终只执行了两次循环体所以只打印了1 28.while 循环和 for 循环的对比从形式上来看for循环语句的初始化判断和调整都更集中一点当代码内容很多时用for循环会更方便于我们后期去修改我们的条件所以从语法形式上来看for循环更优一点9.do-while 循环do-while循环语法形式do { 语句; }while(表达式);do-while循环循环规则do-while循环语句思维导图注意do-while循环先执行一次语句再判断表达式即使条件一开始为假循环体也至少执行一次最后面的分号不能丢丢了会报错用do-while循环打印数字1-10#include stdio.h int main() { int i 1; do { printf(%d , i); i i 1; } while (i 10); return 0; }运行截图do-while使用场景do-while适合在不管怎样都要执行一次的场景下例如判断数字有几位数如1234#include stdio.h int main() { int num 0; int count 0; scanf(%d, num); do { count; num / 10; } while (num!0); printf(%d\n, count); return 0; }因为无论有几个数字就算数字是0那也应该执行一次count也应该加1来表示一位数10.break 和 continue 语句break会在switch语句中和循环语句中出现continue语句只会在循环语句中出现switch中的break语句break 的作用跳出当前 switch 分支结束整个 switch 语句不再执行后面的 case或者default循环中的break和continue语句首先在循环中break 的作用是用于永久的终止循环只要 break 被执行直接就会跳出循环继续往后执行。continue 的作用是跳过本次循环中 continue 后边的代码在 for 循环和 while 循环中有所差异的。while循环break#include stdio.h int main() { int i 1; while (i 10) { if (i 5) break;//当i等于5后就执⾏break循环就终⽌了 printf(%d , i); i i 1; } return 0; }运行截图当i等于5时执行break语句中直接跳出while循环不会管下面的语句所以只打印了1234continueint main() { int i 1; while (i 10) { if (i 5) continue; //当i等于5后就执⾏continue直接跳过continue的代码去循环的判断的地⽅ //因为这⾥跳过了i i1所以i⼀直为5程序陷⼊和死循环 printf(%d , i); i i 1; } return 0; }运行截图当i等于5后就执行continue直接跳过continue的代码去循环的判断的地方因为这里跳过了i i1所以再次进入while循环时仍然是5程序进入死循环for循环breakfor循环中的break语句和while循环中作用一样都是跳出当层循环一次跳出一层循环#include stdio.h int main() { int i 1; for (i 1; i 10; i) { if (i 5) break; printf(%d , i); } return 0; }运行截图continuecontinue稍微有点不一样作用同样是跳过continue后面的语句进入下次循环但while中进入的是while循环的判断的条件再次进行判断然后执行里面的内容在再次遇到continue之前如果遇不见更新循环变量的语句程序就会进入死循环而for循环中跳过后面的语句后首先进入的是for循环中更新循环变量的表达式所以程序不会进入死循环#include stdio.h int main() { int i 1; for(i1; i10; i) { if(i 5) continue; // 跳过后面的printf直接去执行 i printf(%d , i); //打印1 2 3 4 6 7 8 9 10 } return 0; }运行截图该for循环continue跳过后面的printf直接去执行 i然后再进行循环条件的判断符合循环条件就重新执行循环体此时i后变成了6不会再进入continue语句所以程序会正常运行do-while循环do-while循环和while循环中break和continue的作用一模一样演示一下break#include stdio.h int main() { int i 1; do { if (i 5) break; printf(%d , i); i i 1; } while (i 10); return 0; }运行截图当i等于5时直接break跳出循环最终只打印了1234continue#include stdio.h int main() { int i 1; do { if (i 5) continue; // 这里一执行直接跳去判断whilei永远不 printf(%d , i); i i 1; // i5时这句永远执行不到 } while (i 10); return 0; }运行截图程序死循环i永远是5不会执行到下面ii1这个更新循环变量的语句因为总是被continue跳过11.循环的嵌套循环可以进行嵌套调用来更好的解决实际问题如我们要打印乘法口诀表#include stdio.h int main() { int i 0; int j 0; for (i 1; i 9; i) { for (j 1; j i; j) { printf(%d*%d%d, j, i, (i * j)); } printf(\n); } return 0; }运行截图:此时我们就用了两层for循环嵌套使用来实现数学乘法表的规整打印外层for循环打印行内层for循环打印列12.goto 语句goto语句的语法咱们看一个示例就懂了goto的用法比较灵活它的两个作用是第一它可以用于模拟循环但有可能死循环也可能不循环第二它可以用于快速跳出多层循环嵌套正常循环每一层都需要一个break语句来跳出当前循环但是用goto语句就可以直接退出简单来说就是goto语句可以实现在同⼀个函数内跳转到设置好的标号处第三可以跳过一个你不想实现的代码段直接跳过一段你不想执行的代码直接跳到标签位置。第一个示例死循环#include stdio.h int main() { again: printf(hello ); goto again; return 0; }当语句打印完一次 hello 后从 goto 语句进入我们设置了一个标签名 again这个可以自己设置命名规则和变量一样不能使用 C 语言原有的关键字goto again的意思是跳转到 again 所在的位置所以程序会重新回到again:处然后接着执行printf(hello );之后又再次遇到goto again如此反复就形成了死循环。运行截图第二个示例正常循环#include stdio.h int main() { int i 1; loop: if (i 10) { goto end; // 有出口 } printf(%d , i); i; goto loop; end: return 0; }此代码的逻辑是首先int i1然后打印i后接着i自增1之后就进入goto loop语句跳转到loop之后判断i是否大于10大于的话就会从end出此代码i打印完10增到11时又进入loop进行了一次i值是否小于10的判断发现i10,则进入if语句goto end去到了end的位置从而结束程序运行截图第三个示例跳出多层嵌套循环使用goto语句#include stdio.h int main() { int i, j, k; // 三层嵌套循环 for (i 0; i 10; i) { for (j 0; j 10; j) { for (k 0; k 10; k) { printf(i%d, j%d, k%d\n, i, j, k); // 当k等于5时直接跳出所有三层循环 if (k 5) { goto out; // 跳转到out标签 } } } } out: // 标签所有循环的出口 printf(成功跳出所有三层嵌套循环\n); return 0; }运行截图此程序是三层代码的嵌套循环如上图代码当我们进入了最里层的for循环是k从0慢慢自增当k5时就会进入goto out语句从而直接跳转到out的位置直接跳出三层嵌套循环本来 for 循环想提前退出得使用 break 一个 break 只能跳出一层 for 循环如果 3 层循环嵌套就得使用 3 个 break 才能跳出循环所以在这种情况下我们使用 goto 语句就会更加的快捷。不使用goto语句int main() { int i 0, j 0, k 0; int flag 0; // 额外的标志位 for (i 0; i 10; i) { for (j 0; j 10; j) { for (k 0; k 10; k) { printf(i%d, j%d, k%d\n, i, j, k); if (k 5) { flag 1; break; // 只跳出最内层循环 } } if (flag) break; // 跳出中间层循环 } if (flag) break; // 跳出最外层循环 } printf(成功跳出所有三层嵌套循环\n); return 0; }运行截图就要使用三个break语句一层一层跳出循环做不到一下子跳转不使用goto语句不用flag变量也可以这样写#include stdio.h int main() { int i 0, j 0, k 0; for (i 0; i 10; i) { for (j 0; j 10; j) { for (k 0; k 10; k) { printf(i%d, j%d, k%d\n, i, j, k); if (k 5) { break; // 只跳出最内层循环 } } break; // 跳出中间层循环 } break; // 跳出最外层循环 } printf(成功跳出所有三层嵌套循环\n); return 0; }运行截图第四个示例跳过不想执行的代码句#include stdio.h int main() { printf(hihi\n); goto jump; printf(hehe\n); jump: printf(haha\n); return 0; }会直接跳过打印hehe的语句只打印hihi和haha因为goto语句还有一个作用就是可以跳过一个你不想实现的代码段直接跳过一段你不想执行的代码直接跳到标签位置。运行截图