dowhile循环
dowhile语句语法如下:
1 | do{ |
要注意while后面有个分号,不要忘了。
此语句与while语句的唯一区别是:dowhile循环是先执行语句,再判断是否符合循环条件;while循环是先判断再执行语句。可以发现,dowhile循环中的语句至少执行一次,而while循环中的语句则可能一次不执行。dowhile循环也是while循环的变体,我们将视情况合理运用三种循环语句。
死循环
请看以下程序:
1 | while(1==1){ |
我们知道,while循环是条件成立时反复执行,然而此程序中的条件永远成立,这就意味着这个循环在不采取特殊方法的情况下将无法结束;我们称这种无法自己结束的循环为死循环。这一概念下,循环变量的意义也就能体现出来了。
更一般地,如果要构造一个死循环,只有让条件恒为true即可,在C++中为true,在C中即为1,如:
C++:
1 | while(true){ |
C:
1 | while(1){ |
for循环也有对应的写法:
1 | for(;;){ |
死循环的表现形式还可以多种多样,只要是循环条件永远无法达到,就会出现死循环。甚至你可能自己编了个死循环却不自知:
1 | i=1; |
死循环在多数情况下是有害的。因为它往往出现才你不自知的情况下,程序的运行结果也往往不可控。如上例的for循环,可能只是想倒序输出1~10,然而由于i++一句成为了死循环(由于整型数溢出的原理也不算完全的死循环,有兴趣也可以尝试一下看看输出了什么)。
死循环的坏处还不止如此,之前也有同学为了一次运行尝试处理多组数据而用到了死循环,然而此程序会因此无法退出,这样的程序是不安全的。尽管说windows系统能关闭了窗口就结束了进程,这种程序也是不可控、须避免的。比如“输入一个非法输入是结束循环”就是一个可操作的替代方案。
那有什么特别办法让死循环也可以退出呢?
break与continue
break,即打断。break语句的作用是跳出本层循环,如:
1 | i=1; |
此时,进入循环后,先执行i++语句,再执行break语句跳出循环,循环结束,执行i++;程序段结束后,i的值为3。
需要注意的是,break语句只能跳出它所在的一层循环:
1 | for(i=1;i<=10;i++){ |
其中:1处的break执行后会跳到2处,3处的break执行后会跳到4处,5处的break执行后会跳到6处
与break类似的,continue,即为继续执行,作用是忽略本次循环的其他语句,直接进入下一次循环,如:
1 | for(i=1;i<=10;i++){ |
输出:
1 2 4 5 6 7 8 9 10
我们可以看到,i==3是执行了continue语句,忽略了输出i==3直接继续i==4了。
对于死循环的思考
细心的同学可能已经发现,如果使用if语句加break语句,就可以把所有循环用死循环的写法实现,如:
1 | int i=1; |
1 | int i=1; |
上面的两段程序是完全等价的。
而第一段程序中通过while语句的条件判断让循环在条件下正常退出;第二段程序则是已一个死循环为基础,通过内部if语句判断来实现退出的控制。
这里的if(i==10)解释为i的值为10时结束循环,好像从编程逻辑的角度比第一种更为直观,也更易理解。
这就引起了一个疑问:死循环到底有没有益,应不应该使用死循环呢?
首先要明确的一点是:无法退出的循环一定是有害的。其次
在实际操作中还是需要具体分析。但要注意的一点是:随着学习的深入,程序编写在大多数情况下都有不止一种写法,但一种优秀的写法要符合几个标准:
- 能很容易让人理解(可读性)
- 能较为容易地修改功能(可维护)
- 同一个程序/项目尽量使用同一种写法(风格统一)
- 尽量优雅(见仁见智)
从此观点看,我们在保持正确性的条件下应尽可能少使用死循环/不必要的break/continue语句。
数组
我们先来看一道题:
输入3个正整数,倒序输出
很简单,只需三个变量,读取后输出即可。
C++:
1
2
3int a,b,c;
cin>>a>>b>>c;
cout<<c<<" "<<b<<" "<<a;
C:
1
2
3int a,b,c;
scanf("%d%d%d",&a,&b,&c);
printf("%d %d %d",c,b,a);
那么,如果稍微修改一下这道题:
例4.6
输入300个正整数,倒序输出
怎么样,是不是要300个变量然后……
1
int a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11,a12,a13,a14,a15,a16,a17,a18,a19,a20,...
光声明变量这一句就要写300个,那么输入、输出呢?如果再把题目里的300改成30000呢?
C/C++语言中提供数组解决这一操作。
与定义变量类似,数组的定义方法格式如下:
1 | int a[100]; |
第一行:声明了一个整型,名为a,长度为100的数组。即一次声明了100个整型变量。它们分别是a[0]、a[1]、a[2]、a[3]、……、a[99]。与数学中的数列类似,我们把中括号中的数称为数组a某个元素的下标,并发现:C/C++中的数组下标从0开始,为一个整型数。
更一般地,下面的三行介绍了其他定义数组和初始化的方法:
第二行:声明了一个没有初值的数组,名为数组名1。
第三行:定义一个名为数组名2的数组,并给前两个元素分别赋值为元素1,元素2。
第四行:类型相同的数组和变量可以在同一行声明。
对于之前的例4.6,我们可以定义一个长度为300的数组进行操作:
1 | int a[300]; |
那么,如何输入输出?cin>>a[0]>>a[1]>>a[2]>>a[3]…显然没有解决问题。是的,应该用循环完成:
1 | for(int i=0;i<300;i++) |
就这样简单解决了例4.6的难题。从上例,我们发现一般情况下,数组的输入输出须借助循环实现。
二维数组
上面的数组正如一个数列,只有一个下标作为索引,被称为一维数组。而同样的,将数组的概念拓展,拥有两个下标索引,就得到了二维数组,乃至多维数组。
定义方法如下:
1 | int a[10][20]; |
可以用一维数组的概念类推,第一行声明一个二维数组a,第一维度长为10,第二维度长为20;
第二行定义了一个二维数组b,并赋了初值,此时b[0][0]==1,b[2][2]==9,注意多维数组的初始化方法,并需留意:只有在定义数组时才可以这样给数组赋值,数组一旦定义完毕,就只能用循环进行整体的操作了。
与之对应地,二维数组的输入输出可以为:
1 | int a[10][20]; |
有了循环和数组,就可以开始进行很复杂的程序的编写了。
作业:
思考:
1、三种循环语句有什么不同?为什么要提供三种循环语句?
2、什么是死循环?为什么要避免死循环?
3、什么是规范的代码?我们为什么要在代码正确的基础上强调美观?
4、什么是数组?什么是多维数组?它们能解决哪些问题?
习题:
1、输入一个正整数n,和n个正整数,将n个数倒序输出
例:
输入:
5
1 5 2 8 6
输出:
6 8 2 5 1
2、输入一个正整数n,和n个正整数,一个位置m和正整数k,将k插入n个数的第m个位置
输入:
5
1 5 2 8 6
3 4
输出:
1 5 4 2 8 6
3、输入一个正整数n,和n个正整数,将n个数升序排列输出
例:
输入:
5
1 5 2 8 6
输出:
1 2 5 6 8
4、输入正整数n,m,一个n*m的矩阵,输出它的转置矩阵
例:
输入:
3 2
1 2
3 4
5 6
输出:
1 3 5
2 4 6
*5、输入正整数n,按规律输出一个n*n矩阵:
例:
输入:
5
输出:
1 | 1 2 6 7 15 |