本节必须掌握的知识点:
掌握if语句语法
熟练使用if语句
7.2.1 示例二十三
■while语句其语法形式:
while(表达式)
{
语句块;
}
●语法解析:
第一步:执行表达式,如果表达式为真,则执行第二步;
第二步:执行语句块;
第三步:跳转到第一步,重复第一步的动作,判断表达式是否为真,为真则执行第二步,为假则结束while循环。
示例代码二十三
●第一步:分析需求,设计程序结构框架。
分析需求:构建一个while循环语句,当n>5时,重复执行while语句内的重复块。
设计程序结构框架:循环结构while语句。
●第二步:数据定义,定义恰当的数据结构;
int n;//定义一个int类型的整型局部变量,并初始化为5。
●第三步:分析算法。
利用while语句,当n>5时,重复执行while语句内的重复块。先判断后执行。
●第四步:编写伪代码,即用我们自己的语言来编写程序。
int main(void) {
定义一个int类型整型循环变量n=5;
while(n>5)当n>5时执行大括号内的循环语句块{
调用printf函数打印一个提示信息:"n > 5\n"
n++;
}
调用printf函数打印一个提示信息:"n <= 5\n"
system("pause");
return 0;
}
●第五步:画流程图,使用Visio、Excel或者其他绘图工具绘制算法流程和逻辑关系图; 如图7-2所示。
图7-2 示例二十三while循环语句
●第六步:编写源程序,其实就是将我们的伪代码翻译成计算机语言;
/*
while语句
*/
#include <stdio.h>
#include <stdlib.h>
int main(void) {
int n = 5;//循环变量
while (n > 5)
{
printf("n > 5\n");
n++;
}
printf("n <= 5\n");
system("pause");
return 0;
}
●输出结果:
n <= 5
●第七步:调试程序,修复程序中可能出现的BUG;
参见反汇编代码。
●第八步:优化代码,尝试更好的设计方案,效率更高的算法,逻辑更为清晰简洁明了。
示例二十三并没有真正的执行while循环语句块,这样就失去了while语句的意义。我们
将在示例四十四中实现真正的while循环。
7.2.2 代码分析
首先定义一个int型循环变量n,并赋初始值为5,每循环一次,n的值加1。
然后判断while语句的条件表达式是否为真。表达式为n>5,而我们定义的n的值等于5,而不是不大于5,所以表达式为假,不执行while语句里面的重复语句块;
跳过while循环语句块,直接执行代码printf(“n<=5”);
7.2.3 汇编解析
■汇编代码
;C标准库头文件和导入库
include vcIO.inc
.data
n sdword 5
.const
szMsg1 db "n > 5",0dh,0ah,0
szMsg2 db "n <= 5",0dh,0ah,0
.code
start:
NEXT:
;(n > 5)
.if n > 5
invoke printf,offset szMsg1
inc sdword ptr n;
jmp NEXT
.endif
;
invoke printf,offset szMsg2
;
invoke _getch
ret
end start
●输出结果:
n <= 5
上述汇编代码中,当n>5条件成立时,会执行.if和.endif之间的语句块;如果条件为假,则直接跳过重复语句块,直接输出下面的szMsg2。我们会发现,C语言的while语句尽然可以使用.if条件语句+jmp指令实现。其实这很正常,不论是if语句、do while语句还是while语句对应的汇编指令都是JCC条件判断指令。接下来我们做一个实验。
实验四十三:.while高级汇编语句
用.while高级汇编语句实现C语言中的while语句:
;C标准库头文件和导入库
include vcIO.inc
.data
n sdword 5
.const
szMsg1 db "n > 5",0dh,0ah,0
szMsg2 db "n <= 5",0dh,0ah,0
.code
start:
NEXT:
;(n > 5)
.while n > 5
invoke printf,offset szMsg1
inc sdword ptr n;
;jmp NEXT ;去掉
.endw
;
invoke printf,offset szMsg2
;
invoke _getch
ret
end start
区别于7-2-1.asm中使用.if/.endif语句实现while循环,上述汇编代码使用了高级汇编伪指令.while/.endw实现while循环。除了伪指令变化之外,一个最重要的区别就是重复块语句中去掉了jmp NEXT语句,很显然这是编译器隐含的实现代码。为了证明“不论是if语句、do while语句还是while语句对应的汇编指令都是JCC条件判断指令”这句话的正确性,我们来分析一下反汇编代码的实现。
■反汇编代码
int n = 5;
01211838 mov dword ptr [n],5
while (n > 5)
0121183F cmp dword ptr [n],5 ;先比较n和5的大小
01211843 jle main+4Dh (0121185Dh) ;如果b<=5,则跳转到地址0121185Dh处
{
printf("n > 5\n");否则执行下面的重复语句块
01211845 push offset string "n > 5\n" (01217BE0h)
0121184A call _printf (0121104Bh)
0121184F add esp,4
n++;
01211852 mov eax,dword ptr [n]
01211855 add eax,1
01211858 mov dword ptr [n],eax
}
0121185B jmp main+2Fh (0121183Fh) ;无条件跳转到0121183Fh处
printf("n <= 5\n");
0121185D push offset string "n <= 5\n" (01217B34h)
01211862 call _printf (0121104Bh)
01211867 add esp,4
system("pause");
0121186A mov esi,esp
0121186C push offset string "pause" (01217B3Ch)
01211871 call dword ptr [__imp__system (0121B168h)]
01211877 add esp,4
上述反汇编代码先判断n和5的大小,如果b<=5,则跳转到地址0121185Dh处,跳过重复语句块,直接执行第二个printf语句。
在重复语句块内,通过无条件跳转指令jmp跳转到while语句的起始位置,再次比较n和5的大小。
实验四十四:实现while语句循环
在VS中新建项目7-2-2.c:
/*
while语句
*/
#include <stdio.h>
#include <stdlib.h>
int main(void) {
int n = 6;
while (n > 5)
{
printf("n > 5\n");
n++;
}
printf("n <= 5\n");
system("pause");
return 0;
}
●输出结果:
n > 5
n > 5
n > 5
n > 5
n > 5
n > 5
…
陷入死循环。
●代码分析:
1.定义int型变量n,并赋初始值为6。
2.while语句是先判断表达式是否为真,表达式为n>5,而我们定义的n的值是6,所以6>5,所以表达式为真,执行while语句里面的语句块。
3.执行代码printf(“n>5”);n++;
4、进入下轮循环,判断while语句的表达式n>5,而n的值加1后为7,7>6,表达式为真,执行while语句里面的语句块;
5、执行代码printf(“n>5”);
……
一直反复执行,步骤2、3、形成一个死循环,直到内存溢出。
●反汇编:
int n = 6;
00331838 mov dword ptr [n],6
while (n > 5)
0033183F cmp dword ptr [n],5
00331843 jle main+4Dh (033185Dh) ;n始终大于5
{
printf("n > 5\n");
00331845 push offset string "n > 5\n" (0337B30h)
0033184A call _printf (033104Bh)
0033184F add esp,4
n++;
00331852 mov eax,dword ptr [n]
00331855 add eax,1
00331858 mov dword ptr [n],eax
}
0033185B jmp main+2Fh (033183Fh) ;无限循环
printf("n <= 5\n");
0033185D push offset string "n <= 5\n" (0337B38h)
00331862 call _printf (033104Bh)
00331867 add esp,4
通过上面的代码分析,示例代码将无限循环下去,那么问题来了,我们该如何让其代码摆脱死循环呢?当然解决办法有很多种,给循环语句加上退出条件即可。接下来我们继续做一个实验。
实验四十五:实现while语句循环退出
在VS中新建项目7-2-3.c:
/*
while语句退出循环
*/
#include <stdio.h>
#include <stdlib.h>
int main(void) {
int n = 6;
while (n > 5)
{
printf("n > 5\n");
n--;
}
printf("n <= 5\n");
system("pause");
return 0;
}
●输出结果:
n > 5
n <= 5
实验四十五的代码中,当第一次循环时,n的值为6,大于5,条件成立,执行循环语句,输出“n>5”;
“n--;”执行后,n=5,再次跳转到while语句的起始位置,此时“n>5”条件为假,while循环结束,输出“n<=5”。
结论
1.while语句是先判断再执行,如果条件为真,执行循环语句块;如果条件为假,则退出循环,执行循环语句块之后的语句。
2.使用while语句时,需要特别注意,防止陷入死循环。循环条件的设置应该趋近于结束循环。
3.while语句和do while语句、if语句从本质上都是一致的,还原成汇编语句都是由条件CMP/JCC指令和无条件JMP指令的实现。
练习
- 请读者书写程序7-2-3.c伪代码,并绘制流程图。
- 请读者将7-2-3.c翻译成汇编语言实现。
- 请读者分析7-2-3.c的反汇编代码。
实验四十六: while语句循环变量递增1
在VS中新建项目7-2-4.c:
/*
while语句循环变量递增1
*/
#include <stdio.h>
#include <stdlib.h>
int main(void) {
int i = 0;
while (i <= 20)
{
printf("i = %d\t",i);
i++;//循环变量递增1
}
printf("\n");
system("pause");
return 0;
}
●输出结果:
i = 0 i = 1 i = 2 i = 3 i = 4 i = 5 i = 6 i = 7 i = 8 i = 9 i = 10 i = 11 i = 12 i = 13 i = 14 i = 15 i = 16 i = 17 i = 18 i = 19 i = 20
请读者自行分析上述代码的反汇编代码。
实验四十七: while语句循环变量递减2
在VS中新建项目7-2-5.c:
/*
while语句循环变量递减2
*/
#include <stdio.h>
#include <stdlib.h>
int main(void) {
int i = 20;
while (i >= 0)
{
printf("i = %d\t", i);
i -= 2;//复合运算符,循环变量递减2
}
printf("\n");
system("pause");
return 0;
}
●输出结果:
i = 20 i = 18 i = 16 i = 14 i = 12 i = 10 i = 8 i = 6 i = 4 i = 2 i = 0
上面代码请读者朋友动手分析,且能自己写出来大概的汇编代码,如果分析对了且汇编代码也写对了,那么while语句对你来说就是小菜一碟了。其实在实际的工作中,都是在这些基础语法上增加点代码块,俗话说:不积跬步无以至千里,所以一定要打好基础。
练习
1、从输入的整数开始倒数到0。
2、从输入的整数开始倒数到1。
3、递增显示从0到输入的正整数为止的各个整数。
4、编写一段程序,按照升序显示出小于输入值的所以正偶数。
5、编写一段程序,按照升序显示出小于输入值的整数的所有2的乘方。
6、输入一个整数,连续显示出该整数个*。
7、输入规定数量个整数并显示出它们的合计值和平均值。
8、请输入一个正整数:1963,该整数逆向显示的结果是3691。
9、编写一个程序,读取一个正整数,显示其位数。
本文摘自编程达人系列教材《汇编的角度——C语言》。资料下载:www.bcdaren.com