本节必须掌握的知识点:
for循环嵌套语句
示例二十五
代码分析
汇编解析
7.4.1 for循环嵌套语句
在我们总结中,语句块这个解释不知道读者是否感到不解,既然语句块里可以做任何事情,那么它的里面就可以再写一个循环语句,我们称为循环嵌套语句。顾名思义,循环语句中再嵌套一个或多个循环语句。
那么我们看下它的语法形式。
■for循环嵌套语句
为了使读者更容易理解,我们把嵌套在里面的for循环条件改成,for(表达式4;表达式5;表达式6;语句块2;其中语句块1替换成了for循环语句。
for(表达式1;表达式2;表达式3)
{
//语句块1;
for(表达式4;表达式5;表达式6)
{
语句块2;
}
}
●语法解析:
第一步:执行表达式1,表达式1表示初始化外循环变量;
第二步:执行表达式2,表达式2:终止条件,如果条件表达式为真,则执行循环体内的语句块,否则退出for语句;
第三步:执行循环体内的语句1,语句块1替换成内层for循环语句,执行表达式4,表达式4表示初始化内循环变量;
第四步执行表达式5,表达式5:终止条件,如果条件表达式为真,则执行循环体内的语句块,否则退出内层嵌套for语句;
第五步:执行代码块2;
第六步:执行表达式6后置表达式;
第七步:继续执行下一轮循环,直到表达式5的条件为假,跳出内层for循环语句。
第八步:执行表达式3;
第九步:执行表达式2,如果条件表达式2为真,则执行循环体内的语句块1,否则退出for语句;
……
第N步:继续执行下一轮循环,直到表达式2的条件为假,跳出for循环语句。
执行顺序:表达式1->表达式2->语句块1(表达式4->表达式5->语句块2->表达式6->表达式5->语句块2->表达式6->表达式5->……直到不满足条件跳出内层循环)->表达式3->表达式2->语句块1……一直循环下去,直到到表达式2为假时,停止循环。我们看示例代码二十五来理解循环嵌套for语句的执行流程。
示例代码二十五 |
7.4.2 示例二十五
●第一步:分析需求,设计程序结构框架。
分析需求:构建一个双重for循环嵌套语句,当i< 3时,重复执行内层for循环语句,当j <= 3时,重复执行内循环的重复语句块。
设计程序结构框架:双循环结构for语句。
●第二步:数据定义,定义恰当的数据结构;
int i;//定义一个int类型的整型局部变量i,并将其初始化为0,作为外循环变量。
int j;//定义一个int类型的整型局部变量j,并将其初始化为0,作为内循环变量。
●第三步:分析算法。
当i< 3时,重复执行内层for循环语句,当j <= 3时,重复执行内循环的重复语句块。
●第四步:编写伪代码,即用我们自己的语言来编写程序。
int main(void) {
定义一个int类型整型循环变量i和j;
外循环:
表达式1:将循环变量i初始化为0;
表达式2:当i < 3时执行大括号内的内循环for语句块
表达式3:执行完printf语句后,循环变量i加1
for (i = 0; i < 3; i++){
调用printf函数打印信息:"\ni = %d\n"
内循环:
表达式1:将循环变量j初始化为0;
表达式2:当j <= 3时执行大括号内的内循环for语句块
表达式3:执行完printf语句后,循环变量j加1
for (i = 0; j < 3; j++){
调用printf函数打印信息:"j = %d\t"
}
}
调用printf函数打印信息:"\n"
system("pause");
return 0;
}
●第五步:画流程图,使用Visio、Excel或者其他绘图工具绘制算法流程和逻辑关系图; 如图7-4所示。
图7-4 示例二十五for循环嵌套语句
●第六步:编写源程序,其实就是将我们的伪代码翻译成计算机语言;
/*
for语句的嵌套
*/
#include <stdio.h>
#include <stdlib.h>
int main(void) {
int i, j;
for (i = 0; i < 3; i++)
{
printf("\ni = %d\n", i);
for (j = 0; j <= 3; j++)
{
printf("j = %d\t", j);
}
}
printf("\n");
system("pause");
return 0;
}
●输出结果:
i = 0
j = 0 j = 1 j = 2 j = 3
i = 1
j = 0 j = 1 j = 2 j = 3
i = 2
j = 0 j = 1 j = 2 j = 3
7.4.3 代码分析
分析执行流程:
第一次执行外层循环:
第一步:执行 int i = 0;
第二步:执行 i<3;0<3条件为真,继续往下执行 ;
第三步:执行printf(“\ni=%d\n”,i);输出 i = 0;
第一次执行内层循环:
第四步:执行内层for循环,j=0;
第五步:执行j<=3,0<3,条件为真,继续往下执行;
第六步:执行printf(“j=%d\t”,j);输出 j = 0;
第七步:执行j++,此时j=0+1,j=1;
第八步:执行j<=3,1<3,条件为真,继续往下执行;
第九步:执行printf(“j=%d\t”,j);输出 j = 1;
第十一步:执行j<=3,2<3,条件为真,继续往下执行;
第十二步:执行printf(“j=%d\t”,j);输出 j =2;
第十三步:执行j++,此时j=2+1,j=3;
第十四步:执行j<=3,3=3,条件为真,继续往下执行;
第十五步:执行printf(“j=%d\t”,j);输出 j =3;
第十六步:执行j++,此时j=3+1,j=4;
第十七步:执行j<=3,4>3,条件为假,跳出内层循环。
第二次执行外层循环:
第十八步:执行i++,i=0+1,i=1;
第二十步:执行i<3;1<3条件为真,继续往下执行,
第二十一步:执行printf(“\ni=%d\n”,i);输出 i =1;
第二次执行内循环:(重复第一次执行内循环做的事情)
第二十二步:执行内层for循环,j=0;
第二十三步:执行j<=3,0<3,条件为真,继续往下执行;
第二十四步:执行printf(“j=%d\t”,j);输出 j = 0;
第二十五步:执行j++,此时j=0+1,j=1;
第二十六步:执行j<=3,1<3,条件为真,继续往下执行;
第二十七步:执行printf(“j=%d\t”,j);输出 j = 1;
第二十八步:执行j++,此时j=1+1,j=2;
第二十九步:执行j<=3,2<3,条件为真,继续往下执行;
第三十步:执行printf(“j=%d\t”,j);输出 j =2;
第三十一步:执行j++,此时j=2+1,j=3;
第三十二步:执行j<=3,3=3,条件为真,继续往下执行;
第三十三步:执行printf(“j=%d\t”,j);输出 j =3;
第三十四步:执行j++,此时j=3+1,j=4;
第三十五步:执行j<=3,4>3,条件为假,跳出内层循环。
第三次执行外层循环:
第三十六步:执行i++,i=1+1,i=2;
第三十七步:执行i<3;2<3条件为真,继续往下执行,
第三十八步:执行printf(“\ni=%d\n”,i);输出 i =2;
第三次执行内循环:(重复第一次执行内循环做的事情)
第三十九步:执行内层for循环,j=0;
第四十步:执行j<=3,0<3,条件为真,继续往下执行;
第四十一步:执行printf(“j=%d\t”,j);输出 j = 0;
第四十二步:执行j++,此时j=0+1,j=1;
第四十三步:执行j<=3,1<3,条件为真,继续往下执行;
第四十四步:执行printf(“j=%d\t”,j);输出 j = 1;
第四十五步:执行j++,此时j=1+1,j=2;
第四十六步:执行j<=3,2<3,条件为真,继续往下执行;
第四十七步:执行printf(“j=%d\t”,j);输出 j =2;
第四十八步:执行j++,此时j=2+1,j=3;
第四十九步:执行j<=3,3=3,条件为真,继续往下执行;
第五十步:执行printf(“j=%d\t”,j);输出 j =3;
第五十一步:执行j++,此时j=3+1,j=4;
第五十二步:执行j<=3,4>3,条件为假,跳出内层循环。
第四次执行外层循环:
第五十三步:执行i++,i=2+1,i=3;
第五十四步:执行i<3;3=3条件为假,跳出外层循环,当然结束外层循环那么内层循环将不会执行。
7.4.4 汇编解析
■汇编代码
;C标准库头文件和导入库
include vcIO.inc
.data
i sdword ?
j sdword ?
.const
szMsg1 db 0dh,0ah,"i = %d",0dh,0ah,0
szMsg2 db "j = %d",09h,0
szMsg3 db 0dh,0ah,0
.code
start:
mov i,0
NEXT1:
.while i < 3
invoke printf,offset szMsg1,i
mov j,0
NEXT2:
.while j <= 3
invoke printf,offset szMsg2,j
inc sdword ptr j;
jmp NEXT2
.endw
inc sdword ptr i;
jmp NEXT1
.endw
;
invoke printf,offset szMsg3
;
invoke _getch
ret
end start
●输出结果:
i = 0
j = 0 j = 1 j = 2 j = 3
i = 1
j = 0 j = 1 j = 2 j = 3
i = 2
j = 0 j = 1 j = 2 j = 3
上述汇编代码中,使用高级汇编伪指令.while替换C语言中的for语句实现双重循环。NEXT1地址标号作为外循环起始地址,NEXT2地址标号作为内循环起始地址。
【注意】内循环开始前,需要将内循环变量j重新初始化为0。
■反汇编代码
int i, j;
for (i = 0; i < 3; i++);外循环
00E01838 mov dword ptr [i],0 ;表达式1
int i, j;
for (i = 0; i < 3; i++)
00E0183F jmp main+3Ah (0E0184Ah)
00E01841 mov eax,dword ptr [i]
00E01844 add eax,1 ;表达式3
00E01847 mov dword ptr [i],eax
00E0184A cmp dword ptr [i],3 ;表达式2
00E0184E jge main+7Eh (0E0188Eh) ;i>=3时,退出外循环
{
printf("\ni = %d\n", i);循环语句块2
00E01850 mov eax,dword ptr [i]
00E01853 push eax
00E01854 push offset string "\ni = %d\n" (0E07B30h)
00E01859 call _printf (0E0104Bh)
00E0185E add esp,8
for (j = 0; j <= 3; j++);内循环
00E01861 mov dword ptr [j],0 ;表达式4
00E01868 jmp main+63h (0E01873h)
00E0186A mov eax,dword ptr [j]
00E0186D add eax,1 ;表达式6
00E01870 mov dword ptr [j],eax
00E01873 cmp dword ptr [j],3 ;表达式5
00E01877 jg main+7Ch (0E0188Ch) ;j>3时,退出内循环
{
printf("j = %d\t", j);循环语句块2
00E01879 mov eax,dword ptr [j]
00E0187C push eax
00E0187D push offset string "j = %d\t" (0E07B3Ch)
00E01882 call _printf (0E0104Bh)
00E01887 add esp,8
}
00E0188A jmp main+5Ah (0E0186Ah) ;内循环,跳转到表达式6
}
00E0188C jmp main+31h (0E01841h) ;外循环,跳转到表达式3
printf("\n");
00E0188E push offset string "\n" (0E07B44h)
00E01893 call _printf (0E0104Bh)
00E01898 add esp,4
system("pause");
00E0189B mov esi,esp
system("pause");
00E0189D push offset string "pause" (0E07B48h)
00E018A2 call dword ptr [__imp__system (0E0B168h)]
00E018A8 add esp,4
上述反汇编代码验证了for循环嵌套语句的执行流程。请读者参考注释阅读代码,此处不再赘述。
实验五十三: 打印九九乘法表
在VS中新建项目7-4-2.c:
/*
for语句双重循环:打印九九乘法表
*/
#include <stdio.h>
#include <stdlib.h>
int main(void) {
int i, j;
j = 0;
for (i = 1; i <= 9; i++)
{
for (j = 1; j <= 9; j++)
{
printf("%3d ", i * j);
}
printf("\n");
}
system("pause");
return 0;
}
●输出结果:
1 2 3 4 5 6 7 8 9
2 4 6 8 10 12 14 16 18
3 6 9 12 15 18 21 24 27
4 8 12 16 20 24 28 32 36
5 10 15 20 25 30 35 40 45
6 12 18 24 30 36 42 48 54
7 14 21 28 35 42 49 56 63
8 16 24 32 40 48 56 64 72
9 18 27 36 45 54 63 72 81
请按任意键继续. . .
上述代码9行*9列的方式在控制台窗口输出九九乘法表。程序使用for语句的双循环结构,循环变量i表示行,循环变量j表示列。循环变量的取值范围为1~9。
实验五十四: 打印直角三角形
在VS中新建项目7-4-3.c:
/*
for语句双重循环:打印直角三角形
*/
#include <stdio.h>
#include <stdlib.h>
int main(void) {
int i, j, n;
int len;
printf("生成直角在左下方的等腰直角三角形。\n");
printf("短边:");
scanf_s("%d", &len);
for (i = 1; i <= len; i++)//输出行
{
for (n = 1; n <= len - i; n++)//输出列' '
{
putchar(' ');
}
for (j = 1; j <= i; j++)//输出列'*'
{
putchar('*');
}
printf("\n");//换行
}
system("pause");
return 0;
}
●输出结果:
生成直角在左下方的等腰直角三角形。
短边:8
*
**
***
****
*****
******
*******
********
上述代码实现其实非常简单,我们可以把直角三角形看成是一个由空格和*号组成的长方形,只需要建立for语句双循环结构分别循环打印行和列就可以实现了。需要注意的是,在输出列时分别输出len-i个空格和i个*号。
提示
通常在项目中,我们仅需要双循环结构就可以了。很少需要用到三重及以上的循环结构。即使确有多重循环的需要,也可以将其封装为函数调用的形式实现。我们将在第九章详细介绍函数的知识。
练习
- 画一个长方形:
高:3,宽:7
*******
*******
*******
2、生成直角在左下方的等腰直角三角形。
短边:5
*
**
***
****
*****
3、编写一段程序,像右面这样显示所输入整数为边长的正方形。
生成一个正方形
正方形有几层:3
***
***
***
4、显示出一个横向较长的长方形。
读取两个边的边长,以较小的数作为行数,以较大的数作为列数。
一边:7
另一边:3
*******
*******
*******
5、编写一段程序,输入一个整数,像右面这样显示出输入整数层的金字塔形状。
提示;第i行显示(i-1)*2+1个星
金字塔有几层:3
*
***
*****
6、编写一段程序,像右面这样显示出输入整数层的向下的金字塔形状。第i行显示i%10的结果。
金字塔有几层:3
11111
222
3
7、使用for语句,输出于下面的图所示的样式。
2(1)
2(2)
2(3)
- 编写一段程序,输出九九乘法表。
1
本文摘自编程达人系列教材《汇编的角度——C语言》。