14.C Primer Plus 6.6、6.7和6.9

6.6、6.7及6.9

6.6其他赋值运算符:+=、-=、*=、/=、%=

C有许多赋值运算符。

最基本、最常用的是=,它把右侧表达式的值赋给左侧的变量。其他赋值运算符都用于更新变量,其用法都是左侧是一个变量名,右侧是一个表达式。赋给变量的新值是根据右侧表达式的值调整后的值。确切的调整方案取决于具体的运算符。

如下图:

上述所列的运算符右侧都使用了简单的数,还可以使用更复杂的表达式,如下:

以上提到的赋值运算符与=的优先级相同,即比+或*优先级低。

上面最 后一个例子也反映了赋值运算符的优先级,3 * y先与12相加,再把计算结果 与x相乘,最后再把乘积赋给x。

并非一定要使用这些组合形式的赋值运算符。但是,它们让代码更紧凑,而且与一般形式相比,组合形式的赋值运算符生成的机器代码更高效。

特别是当需要在for循环中塞进一些复杂的表达式时,这些组合的赋值运算符特别有用。

6.7逗号运算符

逗号运算符扩展了for循环的灵活性,以便在循环头中包含更多的表达式。

下列示例程序演示了一个打印一类邮件资费的程序(在撰写本书时,邮资为首重40美分/盎司,续重20美分/盎 司,可以在互联网上查看当前邮资):


前5行输出结果如下:

该程序在初始化表达式和更新表达式中使用了逗号运算符。初始化表达 式中的逗号使ounces和cost都进行了初始化,更新表达式中的逗号使每次迭 代ounces递增1、cost递增20(NEXT_Z的值是20)。绝大多数计算都在for循 环头中进行。

逗号运算符并不局限于在for循环中使用,但是这是它最常用的地方。

逗号运算符有两个其他性质:

1.首先,它保证了被它分隔的表达式从左往右求 值(换言之,逗号是一个序列点,所以逗号左侧项的所有副作用都在程序执 行逗号右侧项之前发生)。因此,ounces在cost之前被初始化。

在该例中, 顺序并不重要,但是如果cost的表达式中包含了ounces时,顺序就很重要,例如下面的表达式:

ounces++, cost = ounces * FIRST_OZ

在该表达式中,先递增ounce,然后在第2个子表达式中使用ounce的新 值。作为序列点的逗号保证了左侧子表达式的副作用在对右侧子表达式求值 之前发生。

如下图:

2.其次,整个逗号表达式的值是右侧项的值。**例如:x = (y = 3, (z = ++y + 2) + 5);的效果是:先把3赋给y,递增y为4,然后把 4加2之和(6)赋给z,接着加上5,最后把结果11赋给 x。

另外一方面,如果在输入数字时不小心输入了逗号,如:

houseprice = 249,500;

C语言编译器会将其解释为一个逗号表达式,即

houseprice = 249 是逗号左侧的子表达式,500 是右侧的子表达式。因此,整 个逗号表达式的值是逗号右侧表达式的值,而且左侧的赋值表达式把249赋 给变量houseprice。与下列表达式效果相同:

houseprice = 249;

500;

记住,任何表达式后面加上一个分号就成了表达式语句。**所以, 500;也是一条语句,但是什么也不做。

但是,下面语句:

houseprice = (249,500);

赋给house price的值是整个逗号表达式的值即逗号右侧子表达式的值,即500.

同时,逗号还可以用作分隔符。在下面语句中逗号是分隔符,不是逗号运算符:

char ch, date;

printf("%d %d\n", chimps, chumps);

总结:新的运算符

其他的赋值运算符:

下面的运算符用右侧的值,根据指定的操作更新左侧的变量:

示例:

其他赋值运算符的优先级:

这些组合赋值运算符与普通赋值运算符的优先级相同,都比算术运算符的优先级低。

因此,

contents *= old_rate + 1.2;最终的效果等效于contents = contents * (old_rate + 1.2);

逗号运算符:

逗号运算符把两个表达式连接成一个表达式,并保证最左边的表达式最先求值。**逗号运算符通常在for循环头的表达式中用于包含更多的信息。整个逗号表达式的值是逗号右侧表达式的值。

示例:

6.7.1当Zeno遇到for循环

用for循环和逗号运算符解决古老的悖论。

希腊哲 学家 Zeno 曾经提出箭永远不会达到它的目标。首先,他认为箭要到达目标 距离的一半,然后再达到剩余距离的一半,然后继续到达剩余距离的一半, 这样就无穷无尽。Zeno认为箭的飞行过程有无数个部分,所以要花费无数时 间才能结束这一过程。不过,我们怀疑Zeno是自愿甘做靶子才会得出这样的 结论。 我们采用一种定量的方法,假设箭用1秒钟走完一半的路程,然后用1/2 秒走完剩余距离的一半,然后用1/4秒再走完剩余距离的一半,等等。

可以用下面的无限序列来表示总时间:

1 + 1/2 + 1/4 + 1/8 + 1/16 +....

下列示例程序演示求序列前几项的和。变量power_of_two的值分别为1.0、2.0、4.0、8.0。

下面为序列前15项的和:

不难看出,尽管不断添加新的项,但是总和看起来变化不大。就像程序 输出显示的那样,数学家的确证明了当项的数目接近无穷时,总和无限接近 2.0。

假设S为总和,用数学方法证明一下:

把S除以2得:

第一个式子减去第二个式子得:

除了第1个值为1,其他的值都是一正一负地成对出现,所以这些项都可 以消去。

最后得:

S/2 =1即S=2

从这个示例中得到的启示是,在进行复杂的计算之前,先看看数学上是 否有简单的方法可用。

程序本身需要注意的地方:该程序演示了在表达式中可以使用多 个逗号运算符,在for循环中,初始化了time、power_of_2和count。构建完循环条件之后,程序本身就很简短了。

6.9如何选择循环

选择使用哪一种循环的方法:确定是需要入口条件循环还是出口条 件循环。

通常,入口条件循环用得比较多,有几个原因:

1.一般原则是 在执行循环之前测试条件比较好。

2.测试放在循环的开头,程序的可读性更高。

3.另外,在许多应用中,要求在一开始不满足测试条件时就直接跳过 整个循环。

在确定需要一个入口条件循环后,还得确定是选择while循环还是for循环,这就取决 于个人喜好,因为二者皆可。

要让for循环看起来像while循环,可以省略第一个表达式(初始化)和第三个表达式(执行更新),例如:

要让while循环看起来像for循环,可以在while循环条件的前面初始化变量,并在while循环体中包含更新语句,例如:

初始化;

while(测试表达式)

{

其他语句

更新语句

}

与下面的for循环效果相似:

for(初始化;测试表达式;执行更新)

其他语句

一般而言,当循环涉及初始化和更新变量时,用for循环比较合适,而 在其他情况下用while循环更好。例如下面的条件的情况下使用while循环更好:

对于涉及索引计数的循环,用for循环更合适。例如:

posted @ 2022-02-06 23:22  喻雅芬  阅读(112)  评论(0)    收藏  举报