【C++ Primer Plus学习记录】for循环

很多情况下都需要程序执行重复的任务,C++中的for循环可以轻松地完成这种任务。

我们来从程序清单5.1了解for循环所做的工作,然后讨论它是如何工作的。

//forloop.cpp
#if 1
#include<iostream>
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

1.for循环的组成部分

for循环为执行重复的操作提供了循序渐进的步骤。for循环的具体工作步骤如下:

(1)设置初始值。

(2)执行测试,看看循环是否应当继续进行。

(3)执行循环操作。

(4)更新用于测试的值。

其结构为:

for(初始化;测试;更新)
    循环体

循环只执行一次初始化。

测试表达式决定循环体是否被执行。通常,这个表达式是关系表达式,即对两个值进行比较。C++并没有将测试的值限制为只能为真或假。可以使用任意表达式,C++将把结果强制转换为bool类型。因此,值为0的表达式将被转换为bool值false,导致循环结束。如果表达式的值非0,则被强制转换为bool值true,循环将继续进行。程序清单5.2通过将表达式i用作测试条件来演示了这一特点。

#if 1
#include<iostream>
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++表达式,还可以是其他控制表达式。

1.1表达式和语句

 C++中,每个表达式都有值。通常值是很明显的。

#if 1
#include<iostream>
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

1.2非表达式和语句

对任何表达式加上分号都可以成为语句,但是这句话反过来说就不对了。也就是说,从语句中删除分号,并不一定能将它转换为表达式。

1.3修改规则

C++循环允许像下面这样做:

for(int i = 0;i < 5;i++)

即在for循环的初始化部分中声明变量。 

2.回到for循环

程序清单5.4使用循环来计算并存储前16个阶乘。阶乘的计算方式如下:零阶乘写作0!,被定义为1。1!是1*0!,即1。依此类推。每个整数的阶乘都是该整数与前一个阶乘的乘积。该程序用一个循环来计算连续阶乘的值,并将这些值存储在数组中。然后,用另一个循环来显示结果。另外,该程序还在外部声明了一些值。

#if 1
#include<iostream>
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

3.修改步长

到现在为止,循环示例每一轮循环都将循环计数加1或减1。可以通过修改更新表达式来修改步长。

例如,程序清单5.5中的程序按照用户选择的步长值将循环计数递增。它没有将i++用作更新表达式,而是使用表达式i=i+by,其中,by是用户选择的步长值。

#if 1
#include<iostream>
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

4.使用for循环访问字符串

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

例如:程序清单5.6让用户能够输入一个字符串,然后按相反的方向逐个字符地显示该字符串。在这个例子中,可以使用string对象,也可以使用char数组,因为它们都让您能够使用数组表示法来访问字符串中的字符。程序清单5.6使用的是string对象。string类的size()获得字符串中的字符数;循环在其初始化表达式中使用这个值,将i设置为字符串中最后一个字符的索引(不考虑空值字符)。为了反向计数,程序使用递减运算符(--),在每轮循环后将数组下标减1。另外,程序清单5.6使用关系运算符(>=)来测试循环是否到达第一个元素。

#if 1
#include<iostream>
#include<string>//要使用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

5.递增运算符(++)和递减运算符(--)

递增运算符(++)和递减运算符(--):前缀版本位于操作数前面,如++x;后缀版本位于操作数后面,如x++。两个版本对操作数的影响是一样的,但是影响的时间不同。这就像吃饭前买单和吃饭后买单的最终结果是一样的,但是买单的时间不同。

程序清单5.7演示递增运算符(++)的这种差别。

//5.7
#if 1
#include<iostream>
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,然后使用新的值来计算表达式。 

6.副作用和顺序点

 副作用(side effect)指的是在计算表达式时对某些东西(如存储在变量中的值)进行了修改;

顺序点(sequence point)是程序执行过程中的一个点,在这里,进入下一步之前将确保对所有的副作用都进行了评估。在C++中,语句中的分号就是一个顺序点,这意味着程序处理下一条语句之前,赋值运算符、递增运算符和递减运算符执行的所有修改都必须完成。另外,任何完整的表达式末尾都是一个顺序点。

7.前缀格式和后缀格式

虽然选择使用前缀格式还是后缀格式对程序的行为没有影响,但执行速度可能有细微的差别。对于内置类型和当代的编译器而言,这看似不是什么问题。然而,C++允许您针对类定义这些运算符,在这种情况下,用户这样定义前缀函数:将值加1,然后返回结果;但后缀版本首先复制一个副本,将其加1,然后将复制的副本返回。因此,对于类而言,前缀版本的效率比后缀版本高。

总之,对于内置类型,采用哪种格式不会有差别;但对于用户定义的类型,如果有用户定义的递增和递减运算符,则前缀格式的效率更高。

8.递增/递减运算符和指针

可以将递增运算符用于指针和基本变量。前面介绍过,将递增运算符用于指针时,将把指针的值增加其指向的数据类型占用的字节数,这种规则适用于对指针递增和递减。

可以结合使用这些运算符和*运算符来修改指针指向的值。将*和++同时用于指针时提出了这样的问题:将什么解除引用,将什么递增。这取决于运算符的位置和优先级。前缀递增、前缀递减和解除引用运算符的优先级相同,以从右到左的方式进行结合。后缀递增和后缀递减的优先级相同,但比前缀运算符的优先级高,这两个运算符以从左到右方式进行结合

前缀运算符的从右到左结合规则意味着*++pt的含义如下:现将++应用于pt(因为++位于*的右边,)然后将*应用于被递增后的pt:

double x = *++pt;//increment pointer,take the value;i.e.,arr[2],or 23.4

另一方面,++*pt意味着先取得pt指向的值,然后将这个值加1:

++*pt;//increment the pointed to value;i.e.,change 23.4 to 24.4

在这种情况下,pt仍然指向arr[2]。

接下来:

(*pt)++;首先对指针解除引用,得到24.4.然后,运算符++将这个值递增到25.4,pt仍然指向arr[2]。

x = *pt++;后缀运算符++的优先级更高,这意味着将运算符用于pt,而不是*pt,因此对指针递增。

9.组合赋值运算符

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

10.复合语句(语句块)

代码块由一对花括号和它们包含的语句组成,被视为一条语句,从而满足句法的要求。

例如,程序清单5.8中的程序使用花括号将3条语句合并为一个代码块。这样,循环体便能够提示用户、读取输入并进行计算。该程序计算用户输入的数字的和,因此有机会使用+=运算符。

//5.8
#if 1
#include<iostream>
using namespace std;

int main()
{
	cout << "The Amazing Accounto will sum and average ";
	cout << "five numbers for you.\n";
	cout << "Please enter five values:\n";
	double number;
	double sum = 0.0;
	for (int i = 1; i <= 5; i++)
	{
		cout << "Value " << i << ": ";
		cin >> number;
		sum += number;
	}
	cout << "Five exquisite choices indeed! ";
	cout << "They sum to " << sum << endl;
	cout << "and average to " << sum / 5 << endl;
	cout << "The Amazing Accounto birds you adieu!\n";

	system("pause");
	return 0;
}
#endif

如果在语句块中定义一个新的变量,则仅当程序执行该语句块中的语句时,该变量才存在。执行完该语句块后,变量将被释放。这表明此变量仅在该语句块中才是可用的:

#if 1
#include<iostream>
using namespace std;

int main()
{
	int x = 20;
	{
		int y = 100;
		cout << x << endl;
		cout << y << endl;
	}
	cout << x << endl;
	cout << y << endl;//不可以
}
#endif

注意,在外部语句块中定义的变量在内部语句块中也是被定义了的。

如果在一个语句块中声明一个变量,而外部语句块中也有一个这种名称的变量,情况将如何呢?在声明位置到内部语句块结束的范围之内,新变量将隐藏旧变量;然后旧变量再次可见。

#if 0
#include<iostream>
using namespace std;

int main()
{
	int x = 20;//旧变量
	{
		cout << x << endl;//旧变量
		int x = 100;//新变量
		cout << x << endl;//新变量
		//cout << y << endl;
	}
	cout << x << endl;//旧变量
}
#endif

11.逗号运算符

逗号运算符允许将两个表达式放到C++句法只允许放一个表达式的地方。

程序清单5.9在一个程序中使用了两次逗号运算符,该程序将一个string类对象的内容反转。

//5.9
#if 1
#include<iostream>
#include<string>
using namespace std;

int main()
{
	cout << "Enter a word: ";
	string word;
	cin >> word;

	char temp;
	int i, j;
	for (j = 0, i = word.size(); j < i; --i, ++j)
	{
		temp = word[i];
		word[i] = word[j];
		word[j] = temp;
	}
	cout << word << "\nDone\n";

	system("pause");
	return 0;
}
#endif

14.C-风格字符串的比较

假设要知道字符数组中的字符串是不是mate。如果word是数组名,下面的测试可能并不像我们预想的那样工作:

word == “mate”

请记住,数组名是数组的地址。同样,用引号括起的字符串常量也是其地址。因此,上面的关系表达式不是判断两个字符串是否相同,而是查看它们是否存储在相同的地址上。两个字符串的地址是否相同呢?答案是否定的,虽然它们包含相同的字符。

程序清单5.11在for循环的测试条件中使用了strcmp()。该程序显示一个单词,修改其首字母,然后再次显示这个单词,这样循环往复,直到strcmp()确定该单词与字符串“mate”相同为止。

strcmp()函数:该函数接受两个字符串地址作为参数。这意味着参数可以是指针、字符串常量或字符数组名。如果两个字符串相同,该函数返回0;如果第一个字符串按字母顺序排在第二个字符串之前,则strcmp()将返回一个负数值;如果第一个字符串按字母顺序排在第二个字符串之后,则strcmp()将返回一个正数值。实际上,“按系统排列顺序”比“按字母顺序”更准确。这意味着字符是根据字符的系统编码来进行比较的。

//5.11
#if 1
#include<iostream>
#include<string>
using namespace std;

int main()
{
	char word[5] = "?ate";
	for (char ch = 'a'; strcmp(word, "mate"); ch++)
	{
		word[0] = ch;
		cout << word << endl;
	}
	cout << "After loop ends,word is " << word << endl;
	system("pause");
	return 0;
}
#endif

15.比较string类字符串

程序清单5.12是在程序清单5.11的基础上修改的,它使用string对象而不是char数组。

//5.12
#if 1
#include<iostream>
#include<string>
using namespace std;

int main()
{
	string word = "?ate";
	for (char ch = 'a'; word != "mate"; ch++)
	{
		word[0] = ch;
		cout << word << endl;
	}
	cout << "After loop ends,word is " << word << endl;
	system("pause");
	return 0;
}
#endif

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值