while 语句
还记的之前的递归吗?就像是重复在运行但又稍微有些许不同,这种每次都有点区别的重复工作称之为迭代(Iteration)。迭代可以用递归实现,但是我们还可以使用循环来实现它。
我们依旧编写一个求阶乘的函数:
这样是不是更好理解,这样的流程称为循环(loop)
while ( i-- ) 这样的写法很常见,通过控制 i 的数值,轻易实现循环多少次。
学到两个新概念,之前递归实现阶乘的方法,局部变量没有被额外改变,只在初始化时被赋值,但是循环结构,上面的函数中,i 的值就不断的被改变,这是两种思路,前者称之为:函数式编程( Functional Programming ),后者称为:命令式编程( Imperative Programming )。数学函数时没有副作用的,不会修改变量的值,但是C语言的函数是有副作用的,之前提到 printf() 函数输出就是一种副作用,而在这里,给局部变量赋值也是一种副作用,全局变量多次赋值会给调试带来麻烦,当函数很长,控制流程复杂时,局部变量被多次调用也会有同样问题,对全局变量多次赋值会影响代码的线程安全性,需要改变一种思维,变量可以多次赋值并不是天经地义的,甚至有的语言(Erlang)规定变量的值不允许改变,虽然是这样啊,但是我们用C语言的话还是主要在采用命令式编程。
递归函数和循环函数编写的时候都要注意,一不小心就会写成无限循环,
do / while 语句
其实只要是学过C的话,这几个循环应该式用的最多的,它和while的区别就是至少运行一次,先循环在判断。
注意在语句的最后要加上 ; 这个符号,否则编译器无法判断是do / while 的结束还是新的一个while循环的开头
for 语句
其实在循环中最常使用的是 for 循环,经常见到的死循环写法:
C99引入一种新的循环写法,可以在第一个位置定义变量:
用GCC编译的时候要加上 -std=c99,为了保证兼容性,不建议使用这种写法
break 和 continue
break 和 continue 用来实现跳出循环体,continue语句也用来终止当前循环,和break语句不同的 是,continue语句终止当前循环后又回到循环体的开头准备再次执行循环体。对 于while和do/while,continue之后测试控制表达式,如果值为真则继续执行下一次循环;对于for循环,continue之后首先计算控制表达式3,然后测试控制表达式2,如果值为真则继续执 行下一次循环。
尝试写一个打印处1到100的素数:
先试着自己写一个:
1 #include <stdio.h> 2 3 int Prime_number(int n) 4 { 5 int i; 6 if (n < 2) 7 { 8 return 0; 9 } 10 for (i = 2; i <= n; i++) 11 { 12 if (n % i == 0) 13 { 14 if (n == i) 15 { 16 return 1; 17 } 18 else 19 return 0; 20 } 21 } 22 } 23 24 int main(int argc, char const *argv[]) 25 { 26 int i; 27 int j = 0; 28 for (int i = 0; i <= 100; i++) 29 { 30 j = j + Prime_number(i); 31 if (Prime_number(i)) 32 { 33 printf("%d\n", i); 34 } 35 } 36 printf("%d\n", j); 37 return 0; 38 }
好长,然后看看如果使用 break 和 continue 写的:
1 #include <stdio.h> 2 3 int is_prime(int n) 4 { 5 int i; 6 for (i = 2; i < n; i++) 7 if (n % i == 0) 8 { 9 break; 10 } 11 if (i == n) 12 return 1; 13 else 14 return 0; 15 } 16 17 int main(int argc, char const *argv[]) 18 { 19 int i; 20 for (i = 1; i <= 100; i++) 21 { 22 if (!is_prime(i)) 23 { 24 continue; 25 } 26 printf("%d\n",i); 27 } 28 return 0; 29 }
说实话,我看到这样的写法之后,顿时感觉到,原来还能这样写啊,代码量少了10行。
这算是一个很明确反应了这两个语句是如何跳出循环的了,看完之后我恍然大悟。
嵌套循环
循环中可以嵌套循环,就像是函数中调用函数一样,需要注意的是,嵌套循环的话,break 只能跳出最内层循环或者 switch 语句,continue 也只能终止最内层循环并回到该循环的开头。
到这里,我仿佛看到了初学C语言的时候,各种输出金字塔,星星,棱形巴拉巴拉的,说多了都是泪啊,要是早在大一能深刻意识到自己的目标就好了。
goto 语言
goto语言我以前是完全没接触过,它实现无条件跳转,break 只能跳出最内层的循环,如果我们需要跳出到循环之外的某个地方,就可以使用goto
上面的 error: 叫做标号(Label),标号也遵循标识符命名规则。goto只能跳到同一个函数的某个标号处,不能跳到别的函数里,滥用goto会是程序的控制流变得非常复杂,可读性很差,著名的计算机科学家(实际上我不认识)Edsger W.Dijkstra 最早提出goto的危害,提倡废除goto,可以的话,上述代码我们可以替换成这样:
除了处理错误,其他任何场合都不要轻易考虑使用 goto
今天有点晚了,不过感觉还行,在看一会吧。