有时候,程序需要执行重复的任务。例如:

  1. #include <iostream>
  2. using namespace std;
  3. int main()
  4. {
  5. int i;
  6. for (i = 0; i < 5; ++i) {
  7. cout << "C++ knows loops.\n";
  8. }
  9. cout << "C++ knows when to stop.\n";
  10. return 0;
  11. }

首先将整数变量i设置为0;然后 i = 0 是循环初始化(loop initialization);i < 5 是循环测试(loop test);++i 是循环更新;接下来的花括号中的部分是循环体(loop body)。

5.1.1 for 循环的组成部分

  1. for (/*初始化*/; /*测试*/; /*更新操作*/) {
  2. //循环体
  3. }

for 循环是入口条件循环(entry-condition),每次循环前都会计算测试表达式的值,而每次循环后执行更新表达式(update-expression)。

5.1.2 回到 for 循环

  1. const int ArSize = 16;
  2. int main()
  3. {
  4. long long factorials[ArSize];
  5. factorials[1] = factorials[0] = 1LL;
  6. for (int i = 2; i < ArSize; ++i)
  7. factorials[i] = i * factorials[i-1];
  8. for (int i = 0; i < ArSize; ++i)
  9. std::cout << i << factorials[i] << std::endl;
  10. return 0;
  11. }

该程序创建一个数组存储阶乘值。元素 0 存储 0!,元素 1 存储 1!,以此类推。该示例演示了 for 循环如何通过提供一种访问每个数组成员的方便途径来与数组协同工作。通常定义一个 const 值来表述数组元素个数是个好方法。

5.1.3 修改步长

例如:

  1. int by = 5;
  2. for (int i = 0; i < 100; i += by) {
  3. cout << i << endl;
  4. }

该示例的重点是:更新表达式可以是任何有效的表达式。

5.1.4 使用 for 循环访问字符串

for 循环提供了一种依次访问字符串中每个字符的方式。

  1. string word = "abcd";
  2. for (int i = word.size() - 1; i >= 0; i--) {
  3. cout << word[i] << endl;
  4. }

5.1.5 递增运算符(++)和递减运算符(—)

递增运算符(++)和递减运算符(—)都有两种变体:前缀版和后缀版。区别在于影响的时间不同。

  1. int main()
  2. {
  3. int a = 20;
  4. int b = 20;
  5. cout << a << b << endl; //a = 20, b = 20
  6. cout << a++ << ++b << endl; //a = 20, b = 21 输出a值后,a的值加1;输出b值前先给b加1.
  7. cout << a << b << endl; //a = 21, b = 21
  8. return 0;
  9. }

5.1.6 副作用和顺序点

本届详细介绍递增运算符什么时候生效做了哪些规定。首先,副作用(side effect)指计算表达式时对某些东西(如存储在变量的值)进行修改;顺序点(sequence point)是程序执行过程中的一个点。C++ 中的一个分号就是一个顺序点,这意味着程序处理下一条语句前,赋值运算符/递增运算符/递减运算符等执行的所有修改都要完成。此外,完整的表达式的末尾也是一个顺序点。

什么是完整表达式?它是这样一个表达式:不是另一个更大表达式的子表达式。例如:

  1. while (a++ < 10) { //由于是循环测试条件,所以是一个完整表达式。因此进入循环体前完成a加1。
  2. cout << a << endl;
  3. }
  4. int x = 1;
  5. int y = (4 + a++) + (6 + a++); //第一部分不是完整表达式,C++不保证执行完第一部分后把x加1。应该避免这样写。

C++11 不再使用“顺序点”,因为难以讨论多线程执行,而是采用“顺序”表示有些事件在其他事件前发生。

5.1.7 前缀格式、后缀格式

我们可以为类定义这些运算符,例如定义前缀函数:将值加1,然后返回结果;定义后缀版本:先复制一个副本,将值加1,然后返回副本。因此,对于内置类型,两种格式五差别;对于用户定义的类型,前缀格式效率更高。

2.1.8 递增/递减运算符和指针

将递增运算符用在指针上时,将把指针的值增加其指向的数据类型占用的字节数。例如:

  1. int list[5] = {1,2,3,4,5};
  2. int *pt = list; //pt指向list[0]
  3. ++pt; //pt指向list[1]

可以结合 ++ 和 * 来修改指针指向的值。前缀版和解除引用的优先级相同,从右到左进行结合。后缀版的优先级高,从左到右进行结合。例如:

  • ++pt 含义是:先应用++,后应用
  • ++*pt 含义是:先取得 pt 指向的值,然后将这个值加1。
  • (*pt)++ 含义是:先取得值,然后将这个值进行++运算。
  • *pt++ 含义是:后缀优先级高,所以先应用 ++,然后取当前指针的值,然后指针指向下一个。

    5.1.9 组合赋值运算符

    | 操作符 | 作用(L为左操作数,R为右操作数) | | —- | —- | | += | 将 L+R 赋给 L | | -= | 将 L-R 赋给 L | | = | 将 LR 赋给 L | | /= | 将 L/R 赋给 L | | %= | 将 L%R 赋给 L |

5.1.10 复合语句(语句块)

用两个花括号可以构造一条复合语句(代码块)。这样,循环体就可以用花括号括起来可以写多条语句。例如:

  1. int sum = 0;
  2. for (int i = 0; i < 5; ++i)
  3. { //block starts here
  4. cout << "value = " << i << endl;
  5. sum += i;
  6. } //block ends here

在语句块中定义的变量是局部的,只有进入循环才存在,执行该语句块后被释放。此外,如果内外有同名变量,新变量将隐藏旧变量,然后旧变量再次可见。例如:

  1. int x = 20;
  2. {
  3. cout << x << endl; //使用旧变量x
  4. int x = 100; //使用新变量x
  5. cout << x << endl; //使用新变量x
  6. }
  7. cout << x << endl; //使用旧变量x

5.1.11 其他语法技巧 - 逗号运算符

花括号构造的语句块可以允许把多条语句放到按照 C++ 句法只能放一条语句的地方。同样,逗号运算符也有这样的作用。例如:

  1. int i, j;
  2. for (i = 10, j = 0; j < i; --i, ++j) {
  3. //...
  4. }

因此,逗号运算符最常见的用途就是把多条语句放到 for 循环中。

5.1.12 关系表达式

关系运算符:

操作符 含义
< 小于
<= 小于或等于
== 等于
> 大于
>= 大于或等于
!= 不等于

关系运算符的优先级比算术运算符低,意味着 x + 3 > y - 2 等价于 (x + 3) > (y - 2)

5.1.13 赋值、比较和可能犯的错误

不要混淆 == 和 =。

5.1.14 C 风格字符串的比较

应该使用 strcmp() 函数。

5.1.15 string 字符串的比较

使用更简单,因为 string 类重定义了这些运算符。