本节必须掌握的知识点:
示例二十二
代码分析
汇编解析
■do while语句其语法形式:
do{
语句块;
}while(表达式)
■语法解析:
●执行do循环体内的语句块;
●判断while语句里的表达式,表达式为真继续下次循环,若为假则跳出循环;
7.1.1 示例二十二
示例代码二十二
●第一步:分析需求,设计程序结构框架。
分析需求:重复输入一个整数并判断该整数是奇数还是偶数。
设计程序结构框架:循环结构(do/while语句)中嵌套一个if/else结构。
●第二步:数据定义,定义恰当的数据结构;
int GoOn = 0;//定义一个int类型的整型局部变量,并初始化为0。GoOn变量作为do/while语句的循环控制变量。
int num;//定义一个int类型的整型局部变量,利用scanf_s接收键盘输入整数完成初始化。
●第三步:分析算法。
利用do/while语句结构重复实现一个过程。
该过程为:从键盘输入一个整数,利用(num % 2)的算法判断和输出该整数是奇数还是偶数。
判断完成后,输出结果。
●第四步:编写伪代码,即用我们自己的语言来编写程序。
int main(void) {
定义一个int类型整型变量GoOn=0;
定义一个int类型整型变量num
do{
调用printf函数打印一个提示信息:"请输入一个整数:\t"
调用scanf_s函数接收键盘输入一个整数,并存入变量num;
if(num%2)
如果num%2的模为真:调用printf函数输出"这个数是奇数\n";
else
如果num%2的模为假:调用printf函数输出"这个数是偶数\n";
调用printf函数打印一个提示信息:"重复输入1,不重复输入2:\t"
调用scanf_s函数接收键盘输入一个整数,并存入变量GoOn;
}while(如果GoOn等于1则继续循环)
system("pause");
return 0;
}
●第五步:画流程图,使用Visio、Excel或者其他绘图工具绘制算法流程和逻辑关系图;
图7-1 示例二十二do while语句流程图
●第六步:编写源程序,其实就是将我们的伪代码翻译成计算机语言;
/*
重复输入一个整数并判断该整数是奇数还是偶数
*/
#include <stdio.h>
#include <stdlib.h>
int main(void) {
int GoOn = 0; //已初始化
int num; //未初始化
do {
printf("请输入一个整数:\t");
scanf_s("%d", &num);
if (num % 2)
{
printf("这个数是奇数\n");
}
else
{
printf("这个数是偶数\n");
}
printf("重复输入1,不重复输入2:\t");
scanf_s("%d", &GoOn);
} while (GoOn == 1);
system("pause");
return 0;
}
●输出结果:
请输入一个整数: 1
这个数是奇数
重复输入1,不重复输入2: 1
请输入一个整数: 2
这个数是偶数
重复输入1,不重复输入2: 2
●第七步:调试程序,修复程序中可能出现的BUG;
参见反汇编代码。
●第八步:优化代码,尝试更好的设计方案,效率更高的算法,逻辑更为清晰简洁明了。
(无)
7.1.2 代码分析
示例二十二是一个do/while结构嵌套一个if/else结构的程序。
- 进入do语句主体,在控制台窗口输出“请输入一个整数”;
- 在控制台输入:1;
3.判断 1%2有余数,执行printf(“这个数是奇数”);
4.执行printf("重复输入1,不重复输入2:\t");
5.如果我们输入1,那么将再次循环进入do语句主体;如果输入2则直接退出循环。
因为while里的表达式是GoOn的值判断是否等于1,如果等于1则继续循环,如果不等则退出循环。
显然这是一个标志循环,循环的次数是不确定的。根据循环控制变量GoOn是否为标志值1来决定是否继续循环。
7.1.3 汇编解析
■汇编代码
;C标准库头文件和导入库
include vcIO.inc
.data
GoOn sdword 0
num sdword ?
.const
szMsg1 db "请输入一个整数:",0
szMsg2 db "%d",0
szMsg3 db "这个数是奇数",0dh,0ah,0
szMsg4 db "这个数是偶数",0dh,0ah,0
szMsg5 db "重复输入1,不重复输入2:",09h,0
.code
start:
NEXT:
;输入整数num
invoke printf,offset szMsg1
invoke scanf,offset szMsg2,ADDR num
;
mov eax,num
mov ebx,2
cdq ;被除数扩展到EDX:EAX
idiv ebx
.if edx
invoke printf,offset szMsg3
.else
invoke printf,offset szMsg4
.endif
;输入控制变量
invoke printf,offset szMsg5
invoke scanf,offset szMsg2,ADDR GoOn
;
.if GoOn == 1
jmp NEXT
.endif
;
invoke _getch
ret
end start
●输出结果:
请输入一个整数:1
这个数是奇数
重复输入1,不重复输入2: 1
请输入一个整数:2
这个数是偶数
重复输入1,不重复输入2: 1
请输入一个整数:3
这个数是奇数
重复输入1,不重复输入2: 2
由上述的汇编代码我们可知,C语言中的do/while语句对应的汇编实现方法为:
NEXT:
…
.if GoOn == 1
jmp NEXT
.endif
当GoOn == 1时,使用jmp指令直接跳转到NEXT:地址标号处,开始下一轮循环。
■反汇编代码
int GoOn = 0;//已初始化
00AD1952 mov dword ptr [GoOn],0
int num;//未初始化
do {
printf("请输入一个整数:\t");
00AD1959 push offset string "\xc7\xeb\xca\xe4\xc8\xeb\xd2\xbb\xb8\xf6\xd5\xfb\xca\xfd:\t" (0AD7B30h)
00AD195E call _printf (0AD104Bh)
00AD1963 add esp,4
scanf_s("%d", &num);
00AD1966 lea eax,[num]
00AD1969 push eax
scanf_s("%d", &num);
00AD196A push offset string "%d" (0AD7B44h)
00AD196F call _scanf_s (0AD1154h)
00AD1974 add esp,8
if (num % 2)
00AD1977 mov eax,dword ptr [num]
00AD197A and eax,80000001h
00AD197F jns main+66h (0AD1986h) ;如果num为正整数,则跳转到0AD1986h地址处
00AD1981 dec eax ;num为负整数
00AD1982 or eax,0FFFFFFFEh
00AD1985 inc eax
00AD1986 test eax,eax ;测试eax是否为0
00AD1988 je main+79h (0AD1999h) ;如果eax==0为偶数,跳转到0AD1999h地址处
{
printf("这个数是奇数\n");
00AD198A push offset string "\xd5\xe2\xb8\xf6\xca\xfd\xca\xc7\xc6\xe6\xca\xfd\n" (0AD7B48h)
00AD198F call _printf (0AD104Bh)
00AD1994 add esp,4
}
00AD1997 jmp main+86h (0AD19A6h)
else
{
printf("这个数是偶数\n");
00AD1999 push offset string "\xd5\xe2\xb8\xf6\xca\xfd\xca\xc7\xc5\xbc\xca\xfd\n" (0AD7B58h)
00AD199E call _printf (0AD104Bh)
00AD19A3 add esp,4
}
printf("重复输入1,不重复输入2:\t");显示提示信息
00AD19A6 push offset string "\xd6\xd8\xb8\xb4\xca\xe4\xc8\xeb1\xa3\xac\xb2\xbb\xd6\xd8\xb8\xb4\xca\xe4\xc8\xeb2:\t" (0AD7B68h)
00AD19AB call _printf (0AD104Bh)
00AD19B0 add esp,4
scanf_s("%d", &GoOn);键盘输入整数值并存入变量GoOn地址处
00AD19B3 lea eax,[GoOn]
00AD19B6 push eax
00AD19B7 push offset string "%d" (0AD7B44h)
00AD19BC call _scanf_s (0AD1154h)
00AD19C1 add esp,8
} while (GoOn == 1);
00AD19C4 cmp dword ptr [GoOn],1 ;比较GoOn和1是否相等
00AD19C8 je main+39h (0AD1959h) ; GoOn == 1时,跳转到0AD1959h地址处
system("pause");
00AD19CA mov esi,esp
00AD19CC push offset string "pause" (0AD7B88h)
00AD19D1 call dword ptr [__imp__system (0ADB168h)]
00AD19D7 add esp,4
由上述反汇编代码可知:
C语言中的do/while语句翻译为汇编指令语句为:
00AD1959:do循环的开始地址
…
} while (GoOn == 1);循环条件判断语句
00AD19C4 cmp dword ptr [GoOn],1 ;比较GoOn和1是否相等
00AD19C8 je main+39h (0AD1959h) ; GoOn == 1时,跳转到0AD1959h地址处
结论
1.while语句是先判断后执行(下一节详细讲述);
2.do while语句是先执行后判断,属于标志循环,循环次数不确定;
3.do while语句通常用于输入检查。
4.不要忘记while后面的条件语句以’;’结尾。
在C语言中 for,while ,do while语句本质没有区别,可以相互转换,只是在使用形式上,do while 语句适合用于至少执行一次的循环。而while语句中,如果条件不成立,永远不会执行循环体内的代码。
实验四十二:do while语句实现输入检查
在VS中新建项目7-1-2.c,使用do while语句改写示例二十一,实现循环输入并对输入进行错误检查。
/*
do/while语句实现循环输入并对输入进行错误检查
*/
#include <stdio.h>
#include <stdlib.h>
int main(void) {
int GoOn = 0;//已初始化
int day = 0;
printf("\t\t☆☆☆☆☆天气查询☆☆☆☆☆\n");
printf("\t\t ☆查询周一天气请输入:1\n");
printf("\t\t ☆查询周二天气请输入:2\n");
printf("\t\t ☆查询周三天气请输入:3\n");
printf("\t\t ☆查询周四天气请输入:4\n");
printf("\t\t ☆查询周五天气请输入:5\n");
printf("\t\t ☆查询周六天气请输入:6\n");
printf("\t\t ☆查询周日天气请输入:7\n");
do {
do
{
printf("请输入查询天气:");
scanf_s("%d", &day);
if (day < 1 || day > 7)
printf("输入错误,请重新输入(1,2,3,4,5,6,7任意一个整数值)\n");
} while (day < 1 || day > 7);
switch (day)
{
case 1:
printf("周一天气:晴天\n");
break;
case 2:
printf("周二天气:阴天\n");
break;
case 3:
printf("周三天气:大雨\n");
break;
case 4:
printf("周四天气:中雪\n");
break;
default:
printf("周%d天气:雨夹雪\n", day);
break;
}
printf("重复输入1,不重复输入2:\t");
scanf_s("%d", &GoOn);
} while (GoOn == 1);
system("pause");
return 0;
}
●输出结果:
请输入查询天气:1
周一天气:晴天
重复输入1,不重复输入2: 1
请输入查询天气:7
周7天气:雨夹雪
重复输入1,不重复输入2: 1
请输入查询天气:0
输入错误,请重新输入(1,2,3,4,5,6,7任意一个整数值)
请输入查询天气:8
输入错误,请重新输入(1,2,3,4,5,6,7任意一个整数值)
请输入查询天气:3
周三天气:大雨
重复输入1,不重复输入2: 2
实验四十二使用了两个do while语句进行了嵌套,外层的do while语句实现了重复查询天气的功能,内层的do while语句对输入的变量day的值进行了错误检查。如果day <1或day > 7,则提示重新输入正确的值。确保了键盘输入的整数值必须在1~7之间,防止输入错误的出现。这种设计是非常有必要的,增强了程序的稳定性和人性化设计。
练习
- 请读者书写程序7-1-2.c伪代码,并绘制流程图。
- 请读者将7-1-2.c翻译成汇编语言实现。
- 请读者分析7-1-2.c的反汇编代码。
4、假设用户的密码是一个三位整数,令用户输入密码,如果输入成功,则提示“正确”。如果输入错误,则提示“您输入的密码有误”,并提示重新输入密码。最多输入三次,如果三次没有成功,则退出程序。
5、 (1)打印0~100中所有的偶数。
(2)打印0~100中所有的奇数
6、根据读取的整数值显示所出的拳(只接收0,1,2),【0...石头/1...剪刀/2...布】。
7、不停的输入整数,显示和及平均值。
8、编写一段程序重复实现:读取两个整数的值,然后计算并显示两个整数的和。
9、编写一段程序重复实现:读取两个整数的值,然后计算并显示两个整数的平均值。
10、编写一段程序重复实现:读取一个整数的值,然后判断该整数是正数、负数或零。
本文摘自编程达人系列教材《汇编的角度——C语言》。