网站 建设 场地 资金,途牛网网站是哪家公司做的,wordpress不同用户不同菜单,小生意是做网站还是公众号本节必须掌握的知识点#xff1a; 示例二十四 代码分析 汇编解析 
7.3.1 示例二十四 
■for语句语法形式#xff1a; 
for(表达式1;表达式2;表达式3) 
{ 
语句块; 
} 
●语法解析#xff1a; 
第一步#xff1a;执行表达式1#xff0c;表达式1初始化循环变量#xff1b; …本节必须掌握的知识点 示例二十四 代码分析 汇编解析 
7.3.1 示例二十四 
■for语句语法形式 
for(表达式1;表达式2;表达式3) 
{ 
语句块; 
} 
●语法解析 
第一步执行表达式1表达式1初始化循环变量 
第二步执行表达式2表达式2条件判断语句如果表达式条件为真则执行循环体内的语句块否则退出for语句 
第三步执行循环体内的语句块 
第四步执行表达式3后置表达式 
第五步继续执行下一轮循环直到表达式2的条件为假跳出for循环语句。 
执行顺序表达式1-表达式2-循环语句块-表达式3-表达式2-循环语句块-表达式3-表达式2-循环语句块……一直循环下去直到到表达式2为假时停止循环。接下来我们示例二十四来理解for语句的执行流程。 示例代码二十四   
●第一步分析需求设计程序结构框架。 
分析需求构建一个for循环语句当i 5时重复执行for语句内的重复块。 
设计程序结构框架循环结构for语句。 ●第二步数据定义定义恰当的数据结构 int i;//定义一个int类型的整型局部变量i并将其初始化为0作为循环变量。 ●第三步分析算法。 利用for语句当i 5时重复执行for语句内的重复块。先判断后执行。 ●第四步编写伪代码即用我们自己的语言来编写程序。 int main(void) { 定义一个int类型整型循环变量i; 表达式1将循环变量i初始化为0 表达式2当i  5时执行大括号内的循环语句块 表达式3执行完printf语句后循环变量i加1 for (i  0; i  5; i){ 调用printf函数打印信息i  %d\n                                      } 调用printf函数打印信息i  %d\n  system(pause); return 0;                                                                                                     
} 
●第五步画流程图使用Visio、Excel或者其他绘图工具绘制算法流程和逻辑关系图            如图7-3所示。 图7-3 示例二十四for循环语句 
●第六步编写源程序其实就是将我们的伪代码翻译成计算机语言 
/* for语句 
*/ 
#include stdio.h 
#include stdlib.h int main(void) { int i; for (i  0; i  5; i) { printf(i  %d\n, i); } printf(i  %d\n, i); system(pause);                                         return 0;                            
}     
●输出结果 
i  0 
i  1 
i  2 
i  3 
i  4 
i  5 
i  6 
●第七步调试程序修复程序中可能出现的BUG 
参见反汇编代码。 
●第八步优化代码尝试更好的设计方案效率更高的算法逻辑更为清晰简洁明了。 
无 
7.3.2 代码分析 
分析for语句执行流程 
第一步执行 int i  0; 
第二步执行 i5;05条件为真继续往下执行 ; 
第三步执行printf(“i  %d”,i);输出 i  0; 
第四步执行i; i01,i1; 第五步执行i5;15条件为真,继续往下执行; 
第六步执行printf(“i  %d”,i);输出 i 1; 
第七步执行i;i11,i2; 第八步执行i5;25条件为真,继续往下执行; 
第九步执行printf(“i  %d”,i);输出 i 2; 
第十步执行i;i21,i3; 第十一步执行i5;35条件为真,继续往下执行; 
第十二步执行printf(“i  %d”,i);输出 i 3; 
第十三步执行i;i13,i4; 第十四步执行i5;45条件为真,继续往下执行; 
第十五步执行printf(“i  %d”,i);输出 i 4; 
第十六步执行i;i14,i5; 第十七步执行i5;55条件为真,继续往下执行; 
第十八步执行printf(“i  %d”,i);输出 i 5; 
第十九步执行i;i15,i6; 第二十步执行i5;65条件为假,退出循环; 
退出for循环后再次输出i的值为6。 这是这个程序的正向分析流程我们看下它的反汇编是怎么体现的。 7.3.3 汇编解析 
■汇编代码 
;C标准库头文件和导入库 
include vcIO.inc .data 
i sdword  ? 
.const     
szMsg db i  %d,0dh,0ah,0 
.code      
start: mov i,0 
NEXT:             .while i  5 invoke printf,offset szMsg,i  inc sdword ptr i; jmp NEXT             .endw            ; invoke printf,offset szMsg,i ;      invoke _getch ret                        
end start 
●输出结果 
i  0 
i  1 
i  2 
i  3 
i  4 
i  5 
i  6 上述汇编代码使用了.while高级汇编伪指令没有.for高级汇编伪指令实现了C语言中的for循环语句。 结论 
从本质上看C语言中的for语句就是while语句的简化形式。“mov i,0”语句对应表达式1条件表达式“i  5”对应表达式2“inc sdword ptr i”语句对应表达式3。 
■反汇编代码 int i; for (i  0; i  5; i) 
00391838  mov         dword ptr [i],0  ;表达式1初始化循环变量i0 
0039183F  jmp         main3Ah (039184Ah);无条件跳转到表达式2语句 
00391841  mov         eax,dword ptr [i]   
00391844  add         eax,1              ;表达式3变量i加一 
00391847  mov         dword ptr [i],eax   
0039184A  cmp         dword ptr [i],5  ;表达式2比较变量i和5的大小 int i; for (i  0; i  5; i) 
0039184E  jg          main53h (0391863h) ;如果i5则跳出for循环否则执行循环语句 { printf(i  %d\n, i) 
00391850  mov         eax,dword ptr [i]   
00391853  push        eax                               
00391854  push        offset string i  %d\n (0397B30h) ;循环语句 
00391859  call        _printf (039104Bh)   
0039185E  add         esp,8   } 
00391861  jmp         main31h (0391841h) ;无条件跳转到表达式3 printf(i  %d\n, i); 
00391863  mov         eax,dword ptr [i]  ;for循环外的下一条语句 
00391866  push        eax   
00391867  push        offset string i  %d\n (0397B30h)   
0039186C  call        _printf (039104Bh)   
00391871  add         esp,8   system(pause); 
00391874  mov         esi,esp   
00391876  push        offset string pause (0397B38h)   
0039187B  call        dword ptr [__imp__system (039B168h)]   
00391881  add         esp,4   总结 
在for语句中 
表达式1基本上是用来定义循环控制变量或者说对循环语句的开始进行控制 
表达式2一般是用来判断条件是否满足的语句满足条件则继续执行不满足则退出for语句 
表达式3称为步长一般是用来控制循环次数的 
语句块可以做任何事情。 
实验四十八 for语句表现形式1 
在VS中新建项目7-3-2.c 
/* for语句的表现形式1省略第一个表达式 
*/ 
#include stdio.h 
#include stdlib.h int main(void) { int i  0;         // 在for循环之前初始化 for (; i  10; i)// for语句中省去第一个表达式。但是分号不能少 { printf(■); } printf(\n); system(pause); return 0; 
} 
●输出结果 
■■■■■■■■■■ 
请按任意键继续. . . 上述代码中的表达式1放置在for语句之前for语句内只保留了一个’;’其他保持不变。接下来我们通过反汇编代码观察一下这种形式的for语句的执行流程。                      
■反汇编代码 int i  0;         //  在for循环之前初始化 
009B1838  mov         dword ptr [i],0  ;表达式1前置 for (; i  10; i)// for语句中省去第一个表达式。但是分号不能少 
009B183F  jmp         main3Ah (09B184Ah)   
009B1841  mov         eax,dword ptr [i]   
009B1844  add         eax,1                ;表达式3 
009B1847  mov         dword ptr [i],eax   
009B184A  cmp         dword ptr [i],0Ah  ;表达式2 for (; i  10; i)// for语句中省去第一个表达式。但是分号不能少 
009B184E  jge         main4Fh (09B185Fh)  ;当i10退出循环 { printf(■); 
009B1850  push        offset string \xa1\xf6 (09B7B30h)   
009B1855  call        _printf (09B104Bh)   
009B185A  add         esp,4   } 
009B185D  jmp         main31h (09B1841h)  ;跳转到表达式3 printf(\n); 
009B185F  push        offset string \n (09B80D8h)   
009B1864  call        _printf (09B104Bh)   
009B1869  add         esp,4     结论 
由上述反汇编代码可知表达式1前置对应for语句的执行流程没有任何改变。 
练习 
请读者书写程序7-3-2.c伪代码并绘制流程图。请读者将7-3-2.c翻译成汇编语言实现。请读者分析7-3-2.c的反汇编代码。 
实验四十九 for语句表现形式2 
在VS中新建项目7-3-3.c 
/* for语句的表现形式2:省略第二个表达式 
*/ 
#include stdio.h 
#include stdlib.h int main(void) { for (int i  0;; i)//变量i的作用域仅限for语句块内 { if (i  10) { break;//如果i10则跳出循环break是跳出for循环 } printf(■); } //printf(i  %d\n,i); system(pause); return 0; 
} ●输出结果 
■■■■■■■■■■■请按任意键继续. . . 实验四十九的代码需要注意两点 1变量i的定义放在了for语句内那么变量i的作用域仅限for语句块。如果将下面的语句“printf(i  %d\n,i);”屏蔽去掉编译时会提示错误信息“error C2065: “i”: 未声明的标识符”。在C语言中变量是区分作用域的也就是有效范围。如果是全局变量则保存在全局区在整个程序范围内都是有效的。如果局部变量定义在函数内则保存在函数堆栈中仅在函数内有效函数运行结束后释放堆栈空间局部变量也就消失了。此外还有两种类型的变量static静态变量和STL线程存储变量这里暂不做介绍。 在C语言中不同作用域的变量名可以相同因为作用域不同编译器编译时也会自动区分。 2for语句的表达式2是一个条件表达式其作用是控制循环结束条件。实验四十九中的代码将表达式2放置在循环语句块内使用if语句判断条件当i10时执行break语句退出循环。break语句的作用就是退出循环我们将在本章7.5节继续介绍break语句的使用方法。 
【注】for语句中的表达式2分号不可以缺省。 接下来我们再看一下反汇编代码 for (int i  0;; i)//变量i的作用域仅限for语句块内 
00B01838  mov         dword ptr [ebp-8],0  ;表达式1[ebp-8]为局部变量i for (int i  0;; i)//变量i的作用域仅限for语句块内 
00B0183F  jmp         main3Ah (0B0184Ah)   
00B01841  mov         eax,dword ptr [ebp-8]  ;表达式3 
00B01844  add         eax,1   
00B01847  mov         dword ptr [ebp-8],eax   { if (i  10);表达式2 
00B0184A  cmp         dword ptr [ebp-8],0Ah   
00B0184E  jle         main42h (0B01852h)   { break;//如果i10则跳出循环break是跳出for循环 
00B01850  jmp         main51h (0B01861h)  ;退出循环 } printf(■);循环语句 
00B01852  push        offset string \xa1\xf6 (0B07B30h)   
00B01857  call        _printf (0B0104Bh)   
00B0185C  add         esp,4   } 
00B0185F  jmp         main31h (0B01841h)  ;跳转到表达式3 //printf(i  %d\n,i); system(pause); 
00B01861  mov         esi,esp   上述反汇编代码中一个明显的变化就是变量i使用[ebp-8]来代替。为了进一步验证变量i的作用域我们再做一个实验。 实验五十 for语句循环变量作用域 
在VS中新建项目7-3-4.c 
/* for语句循环变量作用域 
*/ 
#include stdio.h 
#include stdlib.h int main(void) { int i  20;//作用域main函数内有效 for (int i  0;; i)//变量i的作用域仅限for语句块内 { printf(\ni  %d, i); if (i  10) { break;//如果i10则跳出循环break是跳出for循环 } printf(■); } printf(\ni  %d\n,i);//i20 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  20 
请按任意键继续. . . 
上述代码中存在两个变量i编译时并没有报错说明变量名符号没有冲突。再看打印信息for语句内打印的变量i从0~11。最后一个打印输出的变量i的值为20显然是第一个变量i的值。 
请读者查看一下反汇编代码第一个变量i使用“[i]”表示而第二个变量i使用“[ebp-14h]表示”。其余内容和使用四十九相同不再赘述。 实验五十一 for语句形式3 
在VS中新建项目7-3-5.c 
/* for语句形式3:缺省表达式3 
*/ 
#include stdio.h 
#include stdlib.h int main(void) { for (int i  0; i  10;) { printf(■); i; } system(pause); return 0; 
} 
●输出结果 
■■■■■■■■■■请按任意键继续. . . 
上述代码将表达式3放置在循环语句块的结尾处这应该和我们预想的一样之前for语句的反汇编代码的执行流程就是如此。接下来我们再看一下反汇编代码的执行流程。 
●反汇编代码 for (int i  0; i  10;) 
002D1838  mov         dword ptr [ebp-8],0  ;表达式1 for (int i  0; i  10;) 
002D183F  cmp         dword ptr [ebp-8],0Ah  ;表达式2 
002D1843  jge         main4Dh (02D185Dh)  ;i10跳出循环 { printf(■);循环语句 
002D1845  push        offset string \xa1\xf6 (02D7B30h)   
002D184A  call        _printf (02D104Bh)   
002D184F  add         esp,4   i;表达式3 
002D1852  mov         eax,dword ptr [ebp-8]   
002D1855  add         eax,1   
002D1858  mov         dword ptr [ebp-8],eax   } 
002D185B  jmp         main2Fh (02D183Fh)  ;继续循环 system(pause); 
002D185D  mov         esi,esp 
上述反汇编代码和之前示例中的反汇编代码执行流程稍有不同表达式3按照正常的代码编写顺序放置在printf语句之后。其实之前的反汇编代码也是按照代码的编写顺序进行编译的。 
【注意】for (int i  0; i  10;)语句内的最后一个表达式’;’可以缺省。 实验五十二 for语句形式4 
在VS中新建项目7-3-6.c 
/* for语句形式4:缺省全部3个表达式 
*/ 
#include stdio.h 
#include stdlib.h int main(void) { for (;;) { printf(■); } system(pause); return 0; 
} 
●输出结果 
不停的打印■陷入死循环。 
为什么会是这样呢因为当表达式全部置空时相当于没有初始值没有控制语句没有后置表达式那么它将永远的循环下去直到内存溢出这就是所谓的死循环。 ●反汇编代码 for (;;) { printf(■); 
011E1838  push        offset string \xa1\xf6 (011E7B30h)   for (;;) { printf(■); 
011E183D  call        _printf (011E104Bh)   
011E1842  add         esp,4   } 
011E1845  jmp         main28h (011E1838h)  ;跳到for语句块起始位置 system(pause); 
011E1847  mov         esi,esp   上述反汇编代码可以看到程序中没有循环控制变量没有循环结束条件因此陷入了无限循环。我们应绝对避免此类情况的发生。 
练习 
1、递增显示从0到输入的正整数为止的各个整数for语句。 
2、输入一个整数连续显示出该整数个*for语句。 
3、输入规定数量个整数并显示出它们的合计值和平均值。 
4、编写一个程序求1到n的和。n的值从键盘输入。 
5、根据输入的整数循环显示1234567890显示的位数和输入的整数值相同。 
请输入一个整数25 
1234567890123456789012345 
6、显示输入的整数值的所有约数。 
7、编写一段程序输入一个整数值显示该数值以下的所有奇数。 
整数值15 
1 3 5 7 9 11 13 15 
8、编写一段程序输入一个整数值显示该整数值个‘*。每显示5个就进行换行。 
显示多少个*12 
***** 
***** 
**  
* 本文摘自编程达人系列教材《汇编的角度——C语言》。