【小课堂】四、数组、循环结构(2)

dowhile循环

dowhile语句语法如下:

1
2
3
do{
语句或语句块;
}while(条件);

要注意while后面有个分号,不要忘了。
此语句与while语句的唯一区别是:dowhile循环是先执行语句,再判断是否符合循环条件;while循环是先判断再执行语句。可以发现,dowhile循环中的语句至少执行一次,而while循环中的语句则可能一次不执行。dowhile循环也是while循环的变体,我们将视情况合理运用三种循环语句。

死循环

请看以下程序:

1
2
3
while(1==1){
……
}

我们知道,while循环是条件成立时反复执行,然而此程序中的条件永远成立,这就意味着这个循环在不采取特殊方法的情况下将无法结束;我们称这种无法自己结束的循环为死循环。这一概念下,循环变量的意义也就能体现出来了。

更一般地,如果要构造一个死循环,只有让条件恒为true即可,在C++中为true,在C中即为1,如:
C++:

1
2
3
while(true){
……
}

C:

1
2
3
while(1){
……
}

for循环也有对应的写法:

1
2
3
for(;;){
……
}

死循环的表现形式还可以多种多样,只要是循环条件永远无法达到,就会出现死循环。甚至你可能自己编了个死循环却不自知:

1
2
3
4
5
6
7
8
9
i=1;
while(i<=10){
i=2;
i++;
}


for(int i=10;i>=1;i++)
printf("%d ",i);

死循环在多数情况下是有害的。因为它往往出现才你不自知的情况下,程序的运行结果也往往不可控。如上例的for循环,可能只是想倒序输出1~10,然而由于i++一句成为了死循环(由于整型数溢出的原理也不算完全的死循环,有兴趣也可以尝试一下看看输出了什么)。
死循环的坏处还不止如此,之前也有同学为了一次运行尝试处理多组数据而用到了死循环,然而此程序会因此无法退出,这样的程序是不安全的。尽管说windows系统能关闭了窗口就结束了进程,这种程序也是不可控、须避免的。比如“输入一个非法输入是结束循环”就是一个可操作的替代方案。
那有什么特别办法让死循环也可以退出呢?

break与continue

break,即打断。break语句的作用是跳出本层循环,如:

1
2
3
4
5
6
i=1;
while(true){
i++;
break;
}
i++;

此时,进入循环后,先执行i++语句,再执行break语句跳出循环,循环结束,执行i++;程序段结束后,i的值为3。

需要注意的是,break语句只能跳出它所在的一层循环:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
for(i=1;i<=10;i++){
for(j=1;j<=10;j++){
……
break;//1
}
//2
for(k=1;k<=10;k++){
……
break;//3
}
//4
……
break;//5
}
//6

其中:1处的break执行后会跳到2处,3处的break执行后会跳到4处,5处的break执行后会跳到6处

与break类似的,continue,即为继续执行,作用是忽略本次循环的其他语句,直接进入下一次循环,如:

1
2
3
4
for(i=1;i<=10;i++){
if(i==3)continue;
printf("%d ",i);
}

输出:
1 2 4 5 6 7 8 9 10
我们可以看到,i==3是执行了continue语句,忽略了输出i==3直接继续i==4了。

对于死循环的思考

细心的同学可能已经发现,如果使用if语句加break语句,就可以把所有循环用死循环的写法实现,如:

1
2
3
4
5
int i=1;
while(i<10){
printf("%d ",i);
i++;
}
1
2
3
4
5
6
int i=1;
while(true){
if(i==10)break;
printf("%d ",i);
i++;
}

上面的两段程序是完全等价的。
而第一段程序中通过while语句的条件判断让循环在条件下正常退出;第二段程序则是已一个死循环为基础,通过内部if语句判断来实现退出的控制。
这里的if(i==10)解释为i的值为10时结束循环,好像从编程逻辑的角度比第一种更为直观,也更易理解。
这就引起了一个疑问:死循环到底有没有益,应不应该使用死循环呢?

首先要明确的一点是:无法退出的循环一定是有害的。其次
在实际操作中还是需要具体分析。但要注意的一点是:随着学习的深入,程序编写在大多数情况下都有不止一种写法,但一种优秀的写法要符合几个标准:

  • 能很容易让人理解(可读性)
  • 能较为容易地修改功能(可维护)
  • 同一个程序/项目尽量使用同一种写法(风格统一)
  • 尽量优雅(见仁见智)

从此观点看,我们在保持正确性的条件下应尽可能少使用死循环/不必要的break/continue语句。

数组

我们先来看一道题:
输入3个正整数,倒序输出
很简单,只需三个变量,读取后输出即可。
C++:

1
2
3
int a,b,c;
cin>>a>>b>>c;
cout<<c<<" "<<b<<" "<<a;

C:
1
2
3
int 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
2
3
4
int a[100];
<数据类型a> 数组名1[数组长度];
<数据类型b> 数组名2[数组长度]={元素1,元素2};
<数据类型c> 数组名3[数组长度],变量1,变量2;

第一行:声明了一个整型,名为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
2
3
4
5
for(int i=0;i<300;i++)
cin>>a[i];

for(int i=299;i>=0;i--)
cout<<a[i];

就这样简单解决了例4.6的难题。从上例,我们发现一般情况下,数组的输入输出须借助循环实现。

二维数组

上面的数组正如一个数列,只有一个下标作为索引,被称为一维数组。而同样的,将数组的概念拓展,拥有两个下标索引,就得到了二维数组,乃至多维数组
定义方法如下:

1
2
3
4
5
6
7
int a[10][20];
int b[3][3]={{1,2,3},
{4,5,6},
{7,8,9}};
<数据类型a> 二维数组1[长度1][长度2];
<数据类型b> 三维数组1[长度1][长度2][长度3];
……

可以用一维数组的概念类推,第一行声明一个二维数组a,第一维度长为10,第二维度长为20;
第二行定义了一个二维数组b,并赋了初值,此时b[0][0]==1,b[2][2]==9,注意多维数组的初始化方法,并需留意:只有在定义数组时才可以这样给数组赋值,数组一旦定义完毕,就只能用循环进行整体的操作了。
与之对应地,二维数组的输入输出可以为:

1
2
3
4
5
6
7
8
9
10
int a[10][20];
for(int i=0;i<10;i++)
for(int j=0;j<20;j++)
cin>>a[i][j];

for(int i=0;i<10;i++){
for(int j=0;j<20;j++)
cout<<a[i][j]<<" ";
cout<<"\n";
}

有了循环和数组,就可以开始进行很复杂的程序的编写了。

作业:

思考:

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
2
3
4
5
 1    2    6    7   15
3 5 8 14 16
4 9 13 17 22
10 12 18 21 23
11 19 20 24 25