-
Notifications
You must be signed in to change notification settings - Fork 25
/
ch06s01.html
17 lines (17 loc) · 10.4 KB
/
ch06s01.html
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"><head><meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /><title>1. while语句</title><link rel="stylesheet" href="styles.css" type="text/css" /><meta name="generator" content="DocBook XSL Stylesheets V1.73.2" /><link rel="start" href="index.html" title="Linux C编程一站式学习" /><link rel="up" href="ch06.html" title="第 6 章 循环语句" /><link rel="prev" href="ch06.html" title="第 6 章 循环语句" /><link rel="next" href="ch06s02.html" title="2. do/while语句" /></head><body><div class="navheader"><table width="100%" summary="Navigation header"><tr><th colspan="3" align="center">1. while语句</th></tr><tr><td width="20%" align="left"><a accesskey="p" href="ch06.html">上一页</a> </td><th width="60%" align="center">第 6 章 循环语句</th><td width="20%" align="right"> <a accesskey="n" href="ch06s02.html">下一页</a></td></tr></table><hr /></div><div class="sect1" lang="zh-cn" xml:lang="zh-cn"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a id="id2726104"></a>1. while语句</h2></div></div></div><p>在<a class="xref" href="ch05s03.html#func2.recursion">第 3 节 “递归”</a>中,我们介绍了用递归求n!的方法,其实每次递归调用都在重复做同样一件事,就是把n乘到(n-1)!上然后把结果返回。虽说是重复,但每次做都稍微有一点区别(<code class="literal">n</code>的值不一样),这种每次都有一点区别的重复工作称为迭代(Iteration)<a id="id2725493" class="indexterm"></a>。我们使用计算机的主要目的之一就是让它做重复迭代的工作,因为把一件工作重复做成千上万次而不出错正是计算机最擅长的,也是人类最不擅长的。虽然迭代用递归来做就够了,但C语言提供了循环语句使迭代程序写起来更方便。例如<code class="literal">factorial</code>用<code class="literal">while</code>语句可以写成:</p><pre class="programlisting">int factorial(int n)
{
int result = 1;
while (n > 0) {
result = result * n;
n = n - 1;
}
return result;
}</pre><p>和<code class="literal">if</code>语句类似,<code class="literal">while</code>语句由一个控制表达式和一个子语句组成,子语句可以是由若干条语句组成的语句块。</p><div class="literallayout"><p>语句 → while (控制表达式) 语句</p></div><p>如果控制表达式的值为真,子语句就被执行,然后再次测试控制表达式的值,如果还是真,就把子语句再执行一遍,再测试控制表达式的值……这种控制流程称为循环(Loop)<a id="id2726166" class="indexterm"></a>,子语句称为循环体。如果某次测试控制表达式的值为假,就跳出循环执行后面的<code class="literal">return</code>语句,如果第一次测试控制表达式的值就是假,那么直接跳到<code class="literal">return</code>语句,循环体一次都不执行。</p><p>变量<code class="literal">result</code>在这个循环中的作用是累加器(Accumulator)<a id="id2726198" class="indexterm"></a>,把每次循环的中间结果累积起来,循环结束后得到的累积值就是最终结果,由于这个例子是用乘法来累积的,所以<code class="literal">result</code>的初值是1,如果用加法累积则<code class="literal">result</code>的初值应该是0。变量<code class="literal">n</code>是循环变量(Loop Variable)<a id="id2726227" class="indexterm"></a>,每次循环要改变它的值,在控制表达式中要测试它的值,这两点合起来起到控制循环次数的作用,在这个例子中<code class="literal">n</code>的值是递减的,也有些循环采用递增的循环变量。这个例子具有一定的典型性,累加器和循环变量这两种模式在循环中都很常见。</p><p>可见,递归能解决的问题用循环也能解决,但解决问题的思路不一样。用递归解决这个问题靠的是递推关系n!=n·(n-1)!,用循环解决这个问题则更像是把这个公式展开了:n!=n·(n-1)·(n-2)·…·3·2·1。把公式展开了理解会更直观一些,所以有些时候循环程序比递归程序更容易理解。但也有一些公式要展开是非常复杂的甚至是不可能的,反倒是递推关系更直观一些,这种情况下递归程序比循环程序更容易理解。此外还有一点不同:看<a class="xref" href="ch05s03.html#func2.factorial">图 5.2 “factorial(3)的调用过程”</a>,在整个递归调用过程中,虽然分配和释放了很多变量,但所有变量都只在初始化时赋值,没有任何变量的值发生过改变,而上面的循环程序则通过对<code class="literal">n</code>和<code class="literal">result</code>这两个变量多次赋值来达到同样的目的。前一种思路称为函数式编程(Functional Programming)<a id="id2726291" class="indexterm"></a>,而后一种思路称为命令式编程(Imperative Programming)<a id="id2726299" class="indexterm"></a>,这个区别类似于<a class="xref" href="intro.program.html" title="1. 程序和编程语言">第 1 节 “程序和编程语言”</a>讲的Declarative和Imperative的区别。函数式编程的“<span class="quote">函数</span>”类似于数学函数的概念,回顾一下<a class="xref" href="ch03s01.html#func.mathfunc">第 1 节 “数学函数”</a>所讲的,数学函数是没有Side Effect的,而C语言的函数可以有Side Effect,比如在一个函数中修改某个全局变量的值就是一种Side Effect。<a class="xref" href="ch03s04.html#func.localvar">第 4 节 “全局变量、局部变量和作用域”</a>指出,全局变量被多次赋值会给调试带来麻烦,如果一个函数体很长,控制流程很复杂,那么局部变量被多次赋值也会有同样的问题。因此,不要以为“<span class="quote">变量可以多次赋值</span>”是天经地义的,有很多编程语言可以完全采用函数式编程的模式,避免Side Effect,例如LISP、Haskell、Erlang等。用C语言编程主要还是采用Imperative的模式,但要记住,<span class="emphasis"><em>给变量多次赋值时要格外小心,在代码中多次读写同一变量应该以一种一致的方式进行</em></span>。所谓“<span class="quote">一致的方式</span>”是说应该有一套统一的规则,规定在一段代码中哪里会对某个变量赋值、哪里会读取它的值,比如在<a class="xref" href="ch25s02.html#stdlib.errno">第 2.4 节 “errno与perror函数”</a>会讲到访问<code class="literal">errno</code>的规则。</p><p>递归函数如果写得不小心就会变成无穷递归,同样道理,循环如果写得不小心就会变成无限循环(Infinite Loop)<a id="id2726376" class="indexterm"></a>或者叫死循环。如果<code class="literal">while</code>语句的控制表达式永远为真就成了一个死循环,例如<code class="literal">while (1) {...}</code>。在写循环时要小心检查你写的控制表达式有没有可能取值为假,除非你故意写死循环(有的时候这是必要的)。在上面的例子中,不管<code class="literal">n</code>一开始是几,每次循环都会把<code class="literal">n</code>减掉1,<code class="literal">n</code>越来越小最后必然等于0,所以控制表达式最后必然取值为假,但如果把<code class="literal">n = n - 1;</code>这句漏掉就成了死循环。有的时候是不是死循环并不是那么一目了然:</p><pre class="programlisting">while (n != 1) {
if (n % 2 == 0) {
n = n / 2;
} else {
n = n * 3 + 1;
}
}</pre><p>如果<code class="literal">n</code>为正整数,这个循环能跳出来吗?循环体所做的事情是:如果<code class="literal">n</code>是偶数,就把<code class="literal">n</code>除以2,如果<code class="literal">n</code>是奇数,就把<code class="literal">n</code>乘3加1。一般来说循环变量要么递增要么递减,可是这个例子中的<code class="literal">n</code>一会儿变大一会儿变小,最终会不会变成1呢?可以找个数试试,例如一开始<code class="literal">n</code>等于7,每次循环后<code class="literal">n</code>的值依次是:7、22、11、34、17、52、26、13、40、20、10、5、16、8、4、2、1。最后<code class="literal">n</code>确实等于1了。读者可以再试几个数都是如此,但无论试多少个数也不能代替证明,这个循环有没有可能对某些正整数<code class="literal">n</code>是死循环呢?其实这个例子只是给读者提提兴趣,同时提醒读者写循环时要有意识地检查控制表达式。至于这个循环有没有可能是死循环,这是著名的3x+1问题,目前世界上还无人能证明。许多世界难题都是这样的:描述无比简单,连小学生都能看懂,但证明却无比困难。</p><div class="simplesect" lang="zh-cn" xml:lang="zh-cn"><div class="titlepage"><div><div><h3 class="title"><a id="id2726515"></a>习题</h3></div></div></div><p>1、用循环解决<a class="xref" href="ch05s03.html#func2.recursion">第 3 节 “递归”</a>的所有习题,体会递归和循环这两种不同的思路。</p><p>2、编写程序数一下1到100的所有整数中出现多少次数字9。在写程序之前先把这些问题考虑清楚:</p><div class="orderedlist"><ol type="1"><li><p>这个问题中的循环变量是什么?</p></li><li><p>这个问题中的累加器是什么?用加法还是用乘法累积?</p></li><li><p>在<a class="xref" href="ch04s02.html#cond.ifelse">第 2 节 “if/else语句”</a>的习题1写过取一个整数的个位和十位的表达式,这两个表达式怎样用到程序中?</p></li></ol></div></div></div><div class="navfooter"><hr /><table width="100%" summary="Navigation footer"><tr><td width="40%" align="left"><a accesskey="p" href="ch06.html">上一页</a> </td><td width="20%" align="center"><a accesskey="u" href="ch06.html">上一级</a></td><td width="40%" align="right"> <a accesskey="n" href="ch06s02.html">下一页</a></td></tr><tr><td width="40%" align="left" valign="top">第 6 章 循环语句 </td><td width="20%" align="center"><a accesskey="h" href="index.html">起始页</a></td><td width="40%" align="right" valign="top"> 2. do/while语句</td></tr></table></div></body></html>