很多情况下都需要程序执行重复的任务,C++中的for循环可以轻松地完成这种任务。
我们来从程序清单5.1了解for循环所做的工作,然后讨论它是如何工作的。
//forloop.cpp
#if 1
#include
using namespace std;
int main()
{
int i;
for (i = 0; i < 5; i++)
{
cout << i;
cout << " C++ knows loops.\n";//循环体
}
cout << "C++ knows when to stop.\n";
system("pause");
return 0;
}
#endif
for循环为执行重复的操作提供了循序渐进的步骤。for循环的具体工作步骤如下:
(1)设置初始值。
(2)执行测试,看看循环是否应当继续进行。
(3)执行循环操作。
(4)更新用于测试的值。
其结构为:
for(初始化;测试;更新)
循环体
循环只执行一次初始化。
测试表达式决定循环体是否被执行。通常,这个表达式是关系表达式,即对两个值进行比较。C++并没有将测试的值限制为只能为真或假。可以使用任意表达式,C++将把结果强制转换为bool类型。因此,值为0的表达式将被转换为bool值false,导致循环结束。如果表达式的值非0,则被强制转换为bool值true,循环将继续进行。程序清单5.2通过将表达式i用作测试条件来演示了这一特点。
#if 1
#include
using namespace std;
int main()
{
cout << "Enter the starting countdown value: ";
int limit;
cin >> limit;
int i;
for (i = limit; i; i--)
cout << "i = " << i << endl;
cout << "Done now that i = " << i << endl;
system("pause");
return 0;
}
#endif
for循环是入口条件循环。这意味着在每轮循环之前,都将计算测试表达式的值,当测试表达式为false时,将不会执行循环体。例如,假设重新运行程序清单5.2中的程序,但将起始值设置为0,则由于测试条件在首次被判定时便为false,循环体将不被执行。这种在循环之前进行检查的方式可避免程序遇到麻烦。
更新表达式在每轮循环结束时执行,此时循环体已经执行完毕。通常,它用来对跟踪循环轮次的变量的值进行增减。然而,它可以是任何有效的C++表达式,还可以是其他控制表达式。
C++中,每个表达式都有值。通常值是很明显的。
#if 1
#include
using namespace std;
int main()
{
int x;
cout << "The expression x = 100 has the value ";
cout << (x = 100) << endl;
cout << "Now x = " << x << endl;
cout << "The expression x < 3 has the value ";
cout << (x < 3) << endl;
cout << "The expression x > 3 has the value ";
cout << (x > 3) << endl;
cout.setf(ios_base::boolalpha);//调用设置了一个标记,该标记命令cout显示true和false,而不是1和0。
cout << "The expression x < 3 has the value ";
cout << (x < 3) << endl;
cout << "The expression x > 3 has the value ";
cout << (x > 3) << endl;
system("pause");
return 0;
}
#endif
对任何表达式加上分号都可以成为语句,但是这句话反过来说就不对了。也就是说,从语句中删除分号,并不一定能将它转换为表达式。
C++循环允许像下面这样做:
for(int i = 0;i < 5;i++)
即在for循环的初始化部分中声明变量。
程序清单5.4使用循环来计算并存储前16个阶乘。阶乘的计算方式如下:零阶乘写作0!,被定义为1。1!是1*0!,即1。依此类推。每个整数的阶乘都是该整数与前一个阶乘的乘积。该程序用一个循环来计算连续阶乘的值,并将这些值存储在数组中。然后,用另一个循环来显示结果。另外,该程序还在外部声明了一些值。
#if 1
#include
using namespace std;
const int ArSize = 16;
int main()
{
long long factorials[ArSize];
factorials[1] = factorials[0] = 1LL;
for (int i = 2; i < ArSize; i++)
factorials[i] = i * factorials[i - 1];
for (int i = 0; i < ArSize; i++)
cout << i << "! = " << factorials[i] << endl;
system("pause");
return 0;
}
#endif
到现在为止,循环示例每一轮循环都将循环计数加1或减1。可以通过修改更新表达式来修改步长。
例如,程序清单5.5中的程序按照用户选择的步长值将循环计数递增。它没有将i++用作更新表达式,而是使用表达式i=i+by,其中,by是用户选择的步长值。
#if 1
#include
using namespace std;
int main()
{
cout << "Enter an integer: ";
int by;
cin >> by;
cout << "Counting by " << by << "s:\n";
for (int i = 0; i < 100; i = i + by)
cout << i << endl;
system("pause");
return 0;
}
#endif
for循环提供了一种依次访问字符串中每个字符的方式。
例如:程序清单5.6让用户能够输入一个字符串,然后按相反的方向逐个字符地显示该字符串。在这个例子中,可以使用string对象,也可以使用char数组,因为它们都让您能够使用数组表示法来访问字符串中的字符。程序清单5.6使用的是string对象。string类的size()获得字符串中的字符数;循环在其初始化表达式中使用这个值,将i设置为字符串中最后一个字符的索引(不考虑空值字符)。为了反向计数,程序使用递减运算符(--),在每轮循环后将数组下标减1。另外,程序清单5.6使用关系运算符(>=)来测试循环是否到达第一个元素。
#if 1
#include
#include//要使用string类,必须在程序中包含头文件string
using namespace std;
int main()
{
string str1;
cout << "Enter a string: ";
cin >> str1;
for (int i = str1.size() - 1; i >= 0; i--)//string类的size()获得字符串中的字符数;循环在其初始化表达式中使用这个值,将i设置为字符串中最后一个字符的索引(不考虑空值字符)。
cout << str1[i];
cout << "\nBye.\n";
system("pause");
return 0;
}
#endif
递增运算符(++)和递减运算符(--):前缀版本位于操作数前面,如++x;后缀版本位于操作数后面,如x++。两个版本对操作数的影响是一样的,但是影响的时间不同。这就像吃饭前买单和吃饭后买单的最终结果是一样的,但是买单的时间不同。
程序清单5.7演示递增运算符(++)的这种差别。
//5.7
#if 1
#include
using namespace std;
int main()
{
int a = 20, b = 20;
cout << "a = " << a << ": b = " << b << endl;
cout << "a++ = " << a++ << ": ++b = " << ++b << endl;
cout << "a = " << a << ": b = " << b << endl;
system("pause");
return 0;
}
#endif
a++意味着使用a的当前值计算表达式,然后将a的值加1;而++b的意思是先将b的值加1,然后使用新的值来计算表达式。
副作用(side effect)指的是在计算表达式时对某些东西(如存储在变量中的值)进行了修改;
顺序点(sequence point)是程序执行过程中的一个点,在这里,进入下一步之前将确保对所有的副作用都进行了评估。在C++中,语句中的分号就是一个顺序点,这意味着程序处理下一条语句之前,赋值运算符、递增运算符和递减运算符执行的所有修改都必须完成。另外,任何完整的表达式末尾都是一个顺序点。
虽然选择使用前缀格式还是后缀格式对程序的行为没有影响,但执行速度可能有细微的差别。对于内置类型和当代的编译器而言,这看似不是什么问题。然而,C++允许您针对类定义这些运算符,在这种情况下,用户这样定义前缀函数:将值加1,然后返回结果;但后缀版本首先复制一个副本,将其加1,然后将复制的副本返回。因此,对于类而言,前缀版本的效率比后缀版本高。
总之,对于内置类型,采用哪种格式不会有差别;但对于用户定义的类型,如果有用户定义的递增和递减运算符,则前缀格式的效率更高。