流程图里面有哪些结构
在流程图中,常见的结构包括以下几种:
- 「起始/结束符号」(Start/End Symbol):表示流程的开始和结束。通常用椭圆形表示开始和结束。
- 「处理符号」(Process Symbol):表示执行某个操作或处理某些数据的步骤。通常用矩形表示。
- 「判断符号」(Decision Symbol):表示进行条件判断的步骤。根据判断条件的真假,流程会分支到不同的路径。通常用菱形表示。
- 「连接线」(Connector):连接各个符号,表示流程的顺序和逻辑关系。
- 「输入/输出符号」(Input/Output Symbol):表示输入或输出数据的步骤。输入通常用平行四边形表示,输出通常用椭圆形表示。
- 「循环符号」(Loop Symbol):表示循环结构,用于表示某个步骤需要重复执行的情况。通常用弧线表示。
而在我们的C语言中,每一段(或函数)程序都可以用一个流程图来表示。
起始结束符号其实就是我们的main函数里面,我们之前讲了句法规则里面,就包含了处理语句,输入输出语句和顺序执行语句。
目前还剩下条件判断语句和循环语句,那么在这一讲,我们将学习条件判断语句的写法。
IF语句
在C语言中,if语句是一种条件判断语句,用于根据指定条件执行不同的代码块。if语句的基本语法如下:
if (condition) { // 如果条件为真(非零),执行这里的代码 } else { // 如果条件为假(零),执行这里的代码(可选部分) }
condition是一个表达式,用于进行条件判断。如果condition的值为非零(即真),if语句后面的代码块将被执行;如果condition的值为零(即假),else语句后面的代码块(如果存在)将被执行。
例如,下面的代码演示了一个简单的if语句:
#include <stdio.h> int main() { int num = 10; if (num > 0) { printf("The number is positive.\n"); } else { printf("The number is non-positive.\n"); } return 0; }
在这个例子中,如果num的值大于0,程序将输出"The number is positive.";否则,它将输出"The number is non-positive."。在if语句中,可以使用各种比较运算符(如>、<、>=、<=、==、!=)来构建条件表达式。
需要注意的是,else部分是可选的,可以不在if语句中包含else。如果省略else,当条件为假时,程序将跳过if语句后面的代码,继续执行if语句之后的代码。
多个if语句并排
#include <stdio.h> int main() { int num = 10; if (num > 0) { printf("The number is positive.\n"); } if (num % 2 == 0) { printf("The number is even.\n"); } return 0; }
嵌套的if语句
#include <stdio.h> int main() { int num = 10; if (num > 0) { printf("The number is positive.\n"); if (num % 2 == 0) { printf("The number is positive and even.\n"); } else { printf("The number is positive but odd.\n"); } } else { printf("The number is non-positive.\n"); } return 0; }
if-else if-else结构
#include <stdio.h> int main() { int num = 10; if (num > 0) { printf("The number is positive.\n"); } else if (num < 0) { printf("The number is negative.\n"); } else { printf("The number is zero.\n"); } return 0; }
在if-else if-else结构中,程序首先检查第一个条件(num > 0),如果为真,执行对应的代码块,并且跳过后面的else if和else部分。如果第一个条件为假,程序继续检查下一个条件(num < 0),如果为真,执行相应的代码块,以此类推。如果所有条件都为假,执行else部分的代码块。
这种结构可以用于处理多个不同情况下的条件判断,使代码更加清晰和易读。
编写if语句的注意事项
在编写if语句时,有一些注意事项可以帮助确保代码的正确性和可读性:
- 「条件表达式的正确性:」 确保
if语句中的条件表达式(放在圆括号内的部分)是一个可以求值的表达式,并且能够得到true或false结果。比如,不要把赋值语句(如num = 10)误写成相等比较(如num == 10)。 - 「花括号的使用:」 尽量始终使用花括号(
{})来明确if语句的代码块,即使if语句只包含一条语句。这样做可以避免因缩进错误引发的逻辑错误。
// 推荐的写法 if (condition) { // code to be executed } // 避免的写法(不推荐) if (condition) // code to be executed (only this line)
3.「条件表达式的括号:」 虽然在C语言中,条件表达式的括号是可选的,但为了可读性和代码的明确性,建议在条件表达式外侧加上括号。
// 推荐的写法 if ((x > 0) && (y < 10)) { // code to be executed } // 避免的写法(不推荐) if (x > 0 && y < 10) { // code to be executed }
4.「逻辑运算符的正确使用:」 当使用逻辑运算符(如&&、||)连接多个条件时,确保逻辑表达式的顺序和括号的使用符合预期,以避免逻辑错误。
5.「处理多个条件的顺序:」 当有多个if-else if-else条件时,确保条件的顺序是按照优先级排列的,以避免条件判断失效或者多个条件同时满足时执行了错误的分支。
6.「注释:」 如果if语句的逻辑比较复杂,可以使用注释来解释条件的含义和预期行为,增加代码的可读性。
7.「避免嵌套过深:」 尽量避免嵌套过多的if语句,深度过深的嵌套会降低代码的可读性和可维护性。如果嵌套过多,可以考虑使用函数进行逻辑的封装。
多路判断Switch语句
switch语句是C语言中用于多路判断的一种控制语句。它可以根据一个表达式的值,从多个选项中选择一个分支执行。switch语句的基本语法如下:
switch (expression) { case constant1: // code to be executed if expression equals constant1 break; case constant2: // code to be executed if expression equals constant2 break; // more cases can be added as needed default: // code to be executed if expression doesn't match any constant }
expression是一个整数表达式(可以是变量、常量、或者表达式),其值将与case后面的常量进行比较。case constant1:和case constant2:是分支标签,表示当expression的值等于constant1或constant2时,执行相应的代码块。break;语句用于结束switch块,防止程序继续执行后面的分支。如果没有break;语句,程序将会继续执行下一个case分支,直到遇到break;或switch语句结束。default:是可选的,用于处理expression不匹配任何case时的情况。它类似于if-else语句中的else分支。
下面是一个使用switch语句的示例:
#include <stdio.h> int main() { int day = 3; switch (day) { case 1: printf("Monday\n"); break; case 2: printf("Tuesday\n"); break; case 3: printf("Wednesday\n"); break; case 4: printf("Thursday\n"); break; case 5: printf("Friday\n"); break; default: printf("Weekend\n"); } return 0; }
在这个例子中,day的值为3,因此switch语句匹配到case 3:,输出"Wednesday"。如果day的值不在1到5之间,将匹配到default分支,输出"Weekend"。
case穿透
在C语言中,switch语句中的"case穿透"(case fall-through)可能导致程序逻辑错误或不易维护的代码。为了避免潜在的问题,以下是关于case穿透的一些注意事项:
- 「明确使用
break语句:」 在每个case标签的最后,通常应该使用break语句,以确保在匹配到一个case后执行相应的语句后立即跳出switch语句。如果不使用break,程序会继续执行下一个case标签的语句,直到遇到break、return、goto、函数结束或者大括号结束(即switch语句的结束)。
switch (day) { case 1: printf("Monday\n"); break; // 使用break结束当前的case块 case 2: printf("Tuesday\n"); break; // ... }
2.「避免不必要的穿透:」 在设计switch语句时,应该尽量避免不必要的case穿透。如果两个case标签下的代码是相似的,可以将共享的代码提取为一个函数或者使用其他方式避免重复。
switch (option) { case 1: case 2: handleOption1And2(); // 共享的处理代码可以提取为一个函数 break; case 3: handleOption3(); break; // ... }
3.「在default标签前使用break:」 如果default标签位于switch语句的中间,确保在default标签前面加上break,以避免不必要的穿透。因为default通常是switch语句中的最后一个分支,如果不加break,可能会导致与预期不符的结果。
switch (day) { case 1: printf("Monday\n"); break; default: printf("Other day\n"); break; // 在default标签前使用break }
4.「注释case穿透的意图:」 如果有意使用case穿透,应该在代码中添加注释,明确指出这种做法的意图,以便其他人阅读代码时能够理解穿透的原因。
switch (grade) { case 'A': case 'B': printf("Excellent\n"); // Intentional fall-through for 'A' and 'B' grades break; // ... }
通过以上的注意事项,可以更加安全和清晰地使用switch语句,避免因为case穿透而引发的错误和混乱。
Case 穿透的实现原理
switch语句中的case穿透并不是一个bug,而是C语言中的一种设计特性。C语言的switch语句是由编译器实现的,它按照标准的语法规则进行工作。
在switch语句的实现中,编译器通常会将case标签的值转换为跳转表(jump table)或者一系列的条件分支(conditional branches)。跳转表是一个数组,其中每个元素对应一个case标签的值,而数组的内容是对应case标签的代码块的入口地址。当switch语句的表达式的值被计算后,编译器会使用该值来索引跳转表,从而直接跳转到相应的case标签的代码块,这样可以实现高效的分支选择。
case穿透是因为在这个跳转表或条件分支中,并没有包含break语句。如果在一个case标签下没有break,程序会继续执行下一个case标签的代码块,直到遇到break、return、goto、函数结束或者大括号结束(即switch语句的结束)。这种行为是C语言的标准规定,允许程序员在需要的时候使用case穿透。
也就是说,某个case对应的值会存储索引这个值以下的所有程序代码,并没有按照case进行严格隔开,case语句本身只用于第一次构建switch数组,在接下来的执行过程中,并不会再次判断,所以要求我们在每个case后面必须带上自己的break语句,不然就会出现case 穿透。