C++Primer学习(5.4和5.5 迭代语句和跳转语句)

5.4 迭代语句
迭代语句通常称为循环,它重复执行操作直到满足某个条件才停下来。while和for语句在执行循环体之前检查条件,do while语句先执行循环体,然后再检查条件。
5.4.1 while 语句
只要条件为真,while语句(while statement)就重复地执行循环体,它的语法形式是

while (condition)
	statement;

在 while 结构中,只要condition的求值结果为真就一直执行siatement(常常是一个块)。
condiion不能为空,如果 condition第一次求值就得false,statement 一次都不执行。
while的条件部分可以是一个表达式或者是一个带初始化的变量声明(参见5.2节,第155页)。通常来说,应该由条件本身或者是循环体设法改变表达式的值,否则循环可能无法终止。
定义在while条件部分或者while循环体内的变量每次选代都经历从创建到销毁的过程。
使用 while 循环
当不确定到底要迭代多少次时,使用while循环比较合适,比如读取输入的内容就是如此。还有一种情况也应该使用while循环,这就是我们想在循环结束后访问循环控制变量。例如:

vector<int>V;
int i;//重复读入数据,直至到达文件末尾或者遇到其他输入问题
while(cin >>i)
	v.push_back(i);
	//寻找第一个负值元素
auto beg =v.begin();
while(beg!=v.end()&& *beg >=0)
	++beg;
if(beg == v.end())
//此时我们知道v中的所有元素都大于等于0

第一个循环从标准输入中读取数据,我们一开始不清楚循环要执行多少次,当cin读取到无效数据、遇到其他一些输入错误或是到达文件末尾时循环条件失效。第二个循环重复执行直到遇到一个负值为止,循环终止后,beg或者等于v.end(),或者指向v中一个小于0的元素。可以在 while 循环外继续使用 beg的状态以进行其他处理。
5.4.2传统的for语句
for 语句的语法形式是

for (init-statemen; condition; expression )
	statement;

关键字for及括号里的部分称作for语句头。init-sitatement 必须是以下三种形式中的一种:声明语句、表达式语句或者空语句,因为这些语句都以分号作为结束,所以for语句的语法形式也可以看做

for (initializer; condition; expression )
	statement;

一般情况下,init-statement 负责初始化一个值,这个值将随着循环的进行而改变。condiion作为循环控制的条件,只要condition为真,就执行一次statement。如果condition第一次的求值结果就是false,则statement一次也不会执行。expression负责修改init-siatement初始化的变量,这个变量正好就是condition检查的对象,修改发生在每次循环迭代之后。statement可以是一条单独的语句也可以是一条复合语句。
传统 for 循环的执行流程
我们以3.2.3节(第85页)的for循环为例:

//重复处理s中的字符直至我们处理完全部字符或者遇到了一个表示空白的字符
for(decltype(s.size())index=0;
	index !=s.size()&& !isspace(s[index]); ++index)
		s[index]=toupper(s[index]);//将当前字符改成大写形式

求值的顺序如下所示:
1.循环开始时,首先执行一次 init-statement。此例中,定义 index并初始化为0。
2.接下来判断 condition。如果index不等于s.size()而且在s[index]位置的字符不是空白,则执行for循环体的内容。否则,循环终止。如果第一次迭代时条件就为假,for循环体一次也不会执行。
3.如果条件为真,执行循环体。此例中,for循环体将s[index]位置的字符改写成大写形式。
4.最后执行expression。此例中,将index的值加1。
这4步说明了for 循环第一次迭代的过程。其中第1步只在循环开始时执行一次,第 2、3、4步重复执行直到条件为假时终止,也就是在s中遇到一个空白字符或者 index 大于s.size()时终止。
牢记 for语句头中定义的对象只在for循环体内可见。因此在上面的例子中for循环结束后 index就不可用了。
for 语句头中的多重定义
和其他的声明一样,init-statement也可以定义多个对象。但是init-statement 只能有一条声明语句,因此,所有变量的基础类型必须相同(参见2.3节,第45页)。举个例子我们用下面的循环把vector的元素拷贝一份添加到原来的元素后面:

//记录下v的大小,当到达原来的最后一个元素后结束循环
for(decltype(v.size())i=0,sz=v.size();i!=sz;++i)
v.push back(v[i]);

在这个循环中,我们在init-statement 里同时定义了索引i和循环控制变量 sz。
省略 for 语句头的某些部分
for语句头能省略掉init-statement、condition和expression中的任何一个(或者全部)。如果无须初始化,则我们可以使用一条空语句作为init-statement。例如,对于在vector对象中寻找第一个负数的程序,完全能用for循环改写:
auto beg =v.begin();
for(/空语句/;beg!=v.end()&& *beg >=0;++beg);//什么也不做
注意,分号必须保留以表明我们省略掉了init-siatement。说得更准确一点,分号表示的是一个空的 init-statement。在这个循环中,因为所有要做的工作都在for语句头的条件和表达式部分完成了,所以for循环体也是空的。其中,条件部分决定何时停止查找,表达式部分递增迭代器。
省略condition的效果等价于在条件部分写了一个true。因为条件的值永远是true,所以在循环体内必须有语句负责退出循环,否则循环就会无休止地执行下去:

for(inti=0;/*条件为空*/;++i)
{
	//对i进行处理,循环内部的代码必须负责终止选代过程!
}

我们也能省略掉for语句头中的expression,但是在这样的循环中就要求条件部分或者循环体必须改变迭代变量的值。举个例子,之前有一个将整数读入vector的while循环,我们使用for语句改写它:

vector<int>v;
for(inti;cin>>i;/*表达式为空*/)
v.push back(i);

因为条件部分能改变i的值,所以这个循环无须表达式部分。其中,条件部分不断检查输入流的内容,只要读取完所有的输入或者遇到一个输入错误就终止循环。
5.4.3 范围for 语句
C++11新标准引入了一种更简单的for语句,这种语句可以遍历容器或其他序列的所有元素。范围for语句(range for statement)的语法形式是:

for(declaration :expression)
	statement;

expression 表示的必须是一个序列,比如用花括号括起来的初始值列表(参见3.3.1节,第88页)、数组(参见3.5节,第101页)或者vector或string等类型的对象,这些类型的共同特点是拥有能返回迭代器的begin和end成员(参见3.4节,第95页)。
declaralion定义一个变量(循环控制变量),序列中的每个元素都得能转换成该变量的类型(参见 4.11节,第141页)。确保类型相容最简单的办法是使用auto类型说明符(参见2.5.2节,第61页),这个关键字可以令编译器帮助我们指定合适的类型。如果需要对序列中的元素执行写操作,循环变量必须声明成引用类型。
每次迭代都会重新定义循环控制变量,并将其初始化成序列中的下一个值,之后才会执行 statement。像往常一样,statement可以是一条单独的语句也可以是一个块。所有元素都处理完毕后循环终止。
之前我们已经接触过几个这样的循环。接下来的例子将把vector对象中的每个元素都翻倍,它涵盖了范围for语句的几乎所有语法特征:

vector<int>v={0,1,2,3,4,5,6,7,8,9};
//范围变量必须是引用类型,这样才能对元素执行写操作
for(auto &r:v)//对于v中的每一个元素
r*= 2;//将v中每个元素的值翻倍

for 语句头声明了循环控制变量r,并把它和v关联在一起,我们使用关键字 auto 令编译器为r指定正确的类型。由于准备修改v的元素的值,因此将r声明成引用类型。此时,在循环体内给r赋值,即改变了r所绑定的元素的值。
范围for语的定义来源于与之等价的传统for语:

for(auto beg=v.begin(),end =v.end();beg != end; ++beg)
{
	auto &r=*beg;//r必须是引用类型,这样才能对元素执行写操作
	r *= 2;//将v中每个元素的值翻倍
}

学习了范围for语句的原理之后,我们也就不难理解为什么在3.3.2节(第90页)强调不能通过范围for语句增加vector对象(或者其他容器)的元素了。在范围for 语句中,预存了end()的值。一旦在序列中添加(删除)元素,end函数的值就可能变得无效了(参见3.4.1节,第98页)。关于这一点,将在9.3.6节(第315页)做更详细的介绍。
5.4.4 do while 语句
do while 语句(do while statement)和 while 语句非常相似,唯一的区别是,do while语句先执行循环体后检查条件。不管条件的值如何,我们都至少执行一次循环。do while语句的语法形式如下所示:

do
statement;
while (condition);

do while语句应该在括号包围起来的条件后面用一个分号表示语句结束。
在 do语句中,求 condition 的值之前首先执行一次 statement,condition 不能为空。如果condition的值为假,循环终止:否则,重复循环过程。condition使用的变量必须定义在循环体之外。
我们可以使用 do while循环(不断地)执行加法运算:

//不断提示用户输入一对数,然后求其和
string rsp;//作为循环的条件,不能定义在do的内部
do
{
	cout <<"please enter two values:";
	int val1=0,val2 =0;
	cin >>val1>>val2;
	cout <The sum of"<<val1<< "and<< val2
	<<"="<< vall + val2 << "\nn"
	<<"More? Enter yes or no:";
	cin>> rSP;
}while(!rsp.empty()&&rsp[0]!='n');

循环首先提示用户输入两个数字,然后输出它们的和并询问用户是否继续。条件部分检查用户做出的回答,如果用户没有回答,或者用户的回以字母n开始,循环都将终止。否则循环继续执行。因为对于do while来说先执行语句或者块,后判断条件,所以不允许在条件部分定义变量:

do{
mumble(foo);
}while(intfoo=get foo());//错误:将变量声明放在了 do的条件部分

如果允许在条件部分定义变量,则变量的使用出现在定义之前,这显然是不合常理的!
5.5跳转语句
跳转语句中断当前的执行过程。C++语言提供了4种跳转语句:break、continue、goto和return。本章介绍前三种跳转语句,return语句将在6.3节(第199页)进行介绍。
5.5.1 break语句
break语句(break statement)负责终止离它最近的while、do while、for 或 switch 语句,并从这些语句之后的第一条语句开始继续执行。
break语句只能出现在迭代语句或者switch语句内部(包括嵌套在此类循环里的语句或块的内部)。break语句的作用范围仅限于最近的循环或者switch:

string buf;
while(cin >>buf && !buf.empty())
{
	switch(buf[0])
		{
			Case '-':
					//处理到第一个空白为止
					for(auto it=buf.begin()+1;it !=buf.end();++it)
					{
						if(*it ==' ')
						break;//#1,离开for循环
					}
			       //break #1将控制权转移到这里
			       //剩余的'-'处理:
			       break;//#2,离开switch语句
			Case '+':
			//.
		}//结束switch
		//结束switch:break #2将控制权转移到这里
}// 结束 while

示记为#1的 break语句负责终止连字符case 标签后面的 for 循环。它不但不会终止switch语句,甚至连当前的case分支也终止不了。接下来,程序继续执行 for 循环之后的第一条语句,这条语句可能接着处理连字符的情况,也可能是另一条用于终止当前分支的 break语句。
标记为#2的 break语句负责终止switch语句,但是不能终止 while 循环。执行完这个break后,程序继续执行while的条件部分。
5.5.2 continue 语句
continue 语句(continue statement)终止最近的循环中的当前迭代并立即开始下一次迭代。continue语句只能出现在for、while和do while循环的内部,或者嵌套在此类循环里的语句或块的内部。和break语句类似的是,出现在嵌套循环中的continue语句也仅作用于离它最近的循环。和 break语句不同的是,只有当switch语句嵌套在迭代语句内部时,才能在 switch 里使用 continue。
continue语句中断当前的迭代,但是仍然继续执行循环。对于while或者do while语句来说,继续判断条件的值;对于传统的for循环来说,继续执行for语头的expression;而对于范围for语句来说,则是用序列中的下一个元素初始化循环控制变量。
例如,下面的程序每次从标准输入中读取一个单词。循环只对那些以下画线开头的单词感兴趣,其他情况下,我们直接终止当前的迭代并获取下一个单词:

string buf;
while(cin>>buf && !buf.empty())
{
	if(buf[0]!='_')
	continue;//接着读取下一个输入
	//程序执行过程到了这里?说明当前的输入是以下画线开始的;接着处理buf…
}

5.5.3 goto 语句
goto 语句(goto statement)的作用是从 goto 语句无条件跳转到同一函数内的另一条语句。
不要在程序中使用 goto语句,因为它使得程序既难理解又难修改。goto语句的语法形式是:

goto label;

其中,label是用于标识一条语句的标示符。带标签语句(labeled statement)是一种特殊的语句,在它之前有一个标示符以及一个冒号:

end:return;//带标签语句,可以作为goto的目标

标签标示符独立于变量或其他标示符的名字,因此,标签标示符可以和程序中其他实体的标示符使用同一个名字而不会相互干扰。goto语句和控制权转向的那条带标签的语句必须位于同一个函数之内。和switch语句类似,goto语句也不能将程序的控制权从变量的作用域之外转移到作用域之内:

//.....
goto end;
int ix=10;//错误:goto语句绕过了一个带初始化的变量定义
end :
//错误:此处的代码需要使用ix,但是 goto语句绕过了它的声明
ix = 42;

向后跳过一个已经执行的定义是合法的。跳回到变量定义之前意味着系统将销毁该变量,然后重新创建它:

//向后跳过一个带初始化的变量定义是合法的
begin:
int s=get size();
if(sz<= 0)
{
	goto begin;
}

在上面的代码中,goto语句执行后将销毁sz。因为跳回到begin 的动作跨过了 sz 的定义语句,所以sz将重新定义并初始化。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值