1.知识点
高精度运算
书籍推荐《计算机程序设计艺术》,第二章 高精度运算
scanf函数
函数原型:intscanf (const char * format [, argument]…);
返回值含义:返回输入成功的字段数,读但未分配则返回0,没有指定字段是EOF,在第一次尝试读取一个字符遇到文件尾字符或字符串结束字符返回EOF
如何确定一个数有几位?
循环对10取模
Log10的对数
Vc6.0常用快捷键及相关文件含义
F7 编译链接
Ctrl+F7编译不链接
ClassView 显示相关符号,例如函数名等
FlieView 显示参与编译的文件
.dsp 编译选项
.ncb 运行时相关符号高亮,例如函数名等
.dsw 工作空间,包含若干工程
.pcb 符号文件,一般逆向时有此文件可显示库函数名
Clear:一般没修改的源码不进行编译,可能造成错误,此时需要Clear
F5 调试运行
Ctrl+ F5 运行
F9 断点
Alt+ 3 Watch
Alt+ 7 Call Stack
Alt+ 6 Memory
Alt+ 5 Rigisters
Alt+ 8 Disassembly
F10单步不过
F11单步步入
Shift+ F11 单步跳出
F1msdn帮助
Ring3程序的真正入口是api函数CreateProcess
未初始化的变量在调试版中初始值为0xCC
goto 语句使用场景为退出多层循环等
Ctrl + G 跳转到指定地址处
If/else效率问题
一般根据条件在最坏的情况下需要对所有的条件都进行判断
Switch-case效率问题
满足判断的负载平衡,即无论有多少条件判断均只判断2次,第一次判断为检测case的最大值,第二次判断为查询表项,进行流程的跳转
switch-case实现机制有4种:
如果小于等于3项则按if/else进行优化,此时与if/else效率差异不大
如果大于3则按表进行存储相应机器码的地址,直接查表跳转
如果case之间的间隔太大且非线性表,保存default的地址则会造成空间的浪费,因此使用2张表,一张语句块表,一张地址索引表。
如果case之间间隔大于255,则同样造成浪费,因此使用判定树,将每一个case值作为一个中间值形成一个平衡二叉树。
Switch-case原理笔记
code:
/* switch-case 定位表的地址,找到对应case下语句的机器码地址,并分析是哪一种机制 switch-case实现机制有4种: 如果小于等于3项则按if/else进行优化,此时与if/else效率差异不大 如果大于3则按表进行存储相应机器码的地址,直接查表跳转 如果case之间的间隔太大且非线性表,保存defualt的地址则会造成空间的浪费,因此使用2张表,一张语句块表,一张地址索引表。 如果case之间间隔大于255,则同样造成浪费,因此使用判定树,将每一个case值作为一个中间值形成一个平衡二叉树。 */ #include <stdio.h> int main() { //1.当case为线性且小于等于3 int nIndex = 0; scanf("%d", &nIndex); switch(nIndex) { case 0: printf("nIndex == 0\r\n"); break; case 1: printf("nIndex == 1\r\n"); break; case 2: printf("nIndex == 2\r\n"); break; default: printf("default\r\n"); break; } //2.当case为线性且大于3 switch(nIndex) { case 0: printf("nIndex == 0\r\n"); break; case 1: printf("nIndex == 1\r\n"); break; case 2: printf("nIndex == 2\r\n"); break; case 4: printf("nIndex == 4\r\n"); break; case 5: printf("nIndex == 5\r\n"); break; case 6: printf("nIndex == 6\r\n"); break; default: printf("default\r\n"); break; } return 0; }Case小于等于3的情况:
1.按F9在switch语句处下断点
2.按F5调试运行
3.按Alt + 6 打开内存窗口,再按Alt+ 8打开反汇编窗口,
4.再按F10进行单步调试
如上图所示,地址0x0040DAA6处直接将switch-case优化成3个cmp指令,即if判断
对应case 0的机器码地址为0x0040daba
对应case 1的机器码地址为0x0040dac9
对应case 2的机器码地址为0x0040dad8
对应default的机器码地址为0x0040dae7
Case大于3的情况:
1.继续调试,如图所示:
cmp dword ptr [ebp-0Ch],6这条指令对nIndex进行比较,
jmp dword ptr [eax*4+40DB84h],这条指令表示跳转到switch表
对应的地址为0x0040db84
2.再在内存中跳转到该地址如下图所示:
对应case 0的机器码地址为0x0040db0a
对应case 1的机器码地址为0x0040db19
对应case 2的机器码地址为0x0040db28
对应case 4的机器码地址为0x0040db37
对应case 5的机器码地址为0x0040db46
对应case 6的机器码地址为0x0040db55
对应default的机器码地址为0x0040db64
观察发现,case索引没有case3,编译器将default的地址存到case3处,并不再单独存default地址了。
ex1.
// Hw01.cpp : Defines the entry point for the console application. // /************************************************************************/ /* 1. 输入两个数,判断两个数的关系是“大于”、“等于”或“小于”,并输出结果。 /************************************************************************/ #include "stdafx.h" #include <stdio.h> int main(int argc, char* argv[]) { int nNumber = 0; int nNumber2 = 0; printf("请输入两个数:"); scanf("%d%d", &nNumber, &nNumber2); if (nNumber < nNumber2) { printf("%d 小于 %d\r\n", nNumber, nNumber2); } else if (nNumber > nNumber2) { printf("%d 大于 %d\r\n", nNumber, nNumber2); } else { printf("%d 等于 %d\r\n", nNumber, nNumber2); } return 0; } ex2. // Hw02.cpp : Defines the entry point for the console application. // /************************************************************************/ /* 2. 从键盘任意输入三个整数,按从小到大顺序输出。 /************************************************************************/ #include "stdafx.h" #include <stdio.h> int main(int argc, char* argv[]) { int nNumA = 0; int nNumB = 0; int nNumC = 0; bool bNumABC = true; scanf("%d%d%d", &nNumA, &nNumB, &nNumC); while (bNumABC) { if (nNumA > nNumB) { nNumA ^= nNumB; nNumB ^= nNumA; nNumA ^= nNumB; } else if (nNumA > nNumC) { nNumA ^= nNumC; nNumC ^= nNumA; nNumA ^= nNumC; } else if (nNumB > nNumC) { nNumB ^= nNumC; nNumC ^= nNumB; nNumB ^= nNumC; } else { } if(nNumA < nNumB && nNumA < nNumC && nNumB < nNumC) { bNumABC = false; } } printf("%d, %d, %d", nNumA, nNumB, nNumC); return 0; } ex3. // Hw03.cpp : Defines the entry point for the console application. // /************************************************************************/ /* 3. 判断ax2+bx+c=0 (a!=0)方程的有几个解,如果有解则输出如果无解则输出无解。系数a、b、c由键盘输入。 /************************************************************************/ #include "stdafx.h" #include <stdio.h> #include <math.h> void TwoRoot(float fNumberA, float fNumberB, float fNumberC) { double dNumberX1; double dNumberX2; dNumberX1 = (-fNumberB + sqrt(fNumberB * fNumberB - 4 * fNumberA * fNumberC)) / (2 * fNumberA); dNumberX2 = (-fNumberB - sqrt(fNumberB * fNumberB - 4 * fNumberA * fNumberA)) / (2 * fNumberA); printf("方程的根是%.2lf和%.2lf\r\n", dNumberX1, dNumberX2); } void OneSameRoot(float fNumberA, float fNumberB, float fNumberC) { double dNumberX; dNumberX = (-fNumberB) / (2 * fNumberA); printf("方程的根为%.2lf",dNumberX); } void NoRoot() { printf("方程无实数根\n"); } int main(int argc, char* argv[]) { float fNumberA; float fNumberB; float fNumberC; printf("请输入a,b,c的值:\r\n"); scanf("%f%f%f",&fNumberA, &fNumberB, &fNumberC); if(fNumberB * fNumberB - 4 * fNumberA * fNumberC > 0) { TwoRoot(fNumberA, fNumberB, fNumberC); } if(fNumberB * fNumberB - 4 * fNumberA * fNumberC == 0) { OneSameRoot(fNumberA, fNumberB, fNumberC); } if(fNumberB * fNumberB - 4 * fNumberA * fNumberC < 0) { NoRoot(); } return 0; }ex4.
// Hw04.cpp : Defines the entry point for the console application. // /************************************************************************/ /* 4. 要求输出如下菜单 主菜单 ================================ 1. 输入功能 2. 按学号查找 3. 打印输出 0. 退出 ================================ 请选择 (0~3): 如果选择1,则显示“请输入”; 选择2,则显示“请输入查找学生学号”; 选择3,显示“正在输出”; 选择0,显示“谢谢使用”; 选择其它则显示“输入错误”。(用switch语句实现)。 */ /************************************************************************/ #include "stdafx.h" #include <stdio.h> void ShowMenu() { printf("\t主菜单\r\n"); printf("================================\r\n"); printf("1. 输入功能 \r\n"); printf("2. 按学号查找 \r\n"); printf("3. 打印输出 \r\n"); printf("0. 退出 \r\n"); printf("================================\r\n"); printf("请选择 (0~3):"); } int main(int argc, char* argv[]) { int nIndex = 0; bool bFlag = true; while(bFlag) { ShowMenu(); scanf("%d", &nIndex); switch(nIndex) { case 0: printf("谢谢使用\r\n\n"); bFlag = false; break; case 1: printf("请输入\r\n\n"); break; case 2: printf("请输入查找学生学号\r\n\n"); break; case 3: printf("正在输出\r\n\n"); break; default: printf("输入错误\r\n\n"); break; } } return 0; }ex5.
// Hw05.cpp : Defines the entry point for the console application. // /************************************************************************/ /* 实验1. 编写一个程序求c =a+ | b | , 输出c 的值, 其中a和b为整数 /* ( 绝对值函数请查函数库中的math.h 文件) /************************************************************************/ #include "stdafx.h" #include <stdio.h> #include <math.h> int main(int argc, char* argv[]) { int nNuma = 0; int nNumb = 0; int nNumc = 0; printf("please input a and b:"); scanf("%d%d", &nNuma, &nNumb); nNumc = nNuma + abs(nNumb); printf("%d = %d + | %d |\r\n", nNumc, nNuma, nNumb); return 0; }ex6.
// Hw06.cpp : Defines the entry point for the console application. // /************************************************************************/ /* 实验2. 写出下面程序的输出结果 #include <stdio.h> void main( ) { int x , y , z ; x = -3+4*5-6 ; printf(“x=%d\n”, x); y = -3*4%-6/5 ; printf(“y=%d\n”, y); z = (7+6)%5/2 ; printf(“z=%d\n”, z); x = 3 ; y = + +x -1; printf(“x=%d,y=%d\n”, x , y); z = x - - +1 ; printf(“x=%d,z=%d\n”, x , z); } */ /************************************************************************/ #include "stdafx.h" #include <stdio.h> int main(int argc, char* argv[]) { int x, y, z; x = -3 + 4 * 5 - 6; printf("x = %d\n", x); y = -3 * 4 % - 6 / 5; printf("y = %d\n", y); z = (7 + 6) % 5 / 2; printf("z = %d\n", z); x = 3; y = ++x - 1; printf("x = %d, y = %d\n", x, y); z = x-- + 1 ; printf("x = %d, z = %d\n", x, z); return 0; }ex7.
// Hw07.cpp : Defines the entry point for the console application. // /************************************************************************/ /* 实验3. 求出下列表达式的值 假设 x = 3, y = 4, z = 4 (1) (z >= y >= x)? 1 : 0 (2) z>=y && y>=x 假设 x = 3, y = 2, z = 1 (3) x < y ? x++ : y++ (4) z +=(x < y? x++ : y++) */ /************************************************************************/ #include "stdafx.h" #include <stdio.h> int main(int argc, char* argv[]) { int x = 3; int y = 4; int z = 4; int express = 0; int x1 = 3; int y1 = 2; int z1 = 1; int express1 = 0; //(1) express = (z >= y && y >= x)? 1 : 0; printf("(1)%d\r\n", express); //(2) express = (z>=y) && (y>=x); printf("(2)%d\r\n", express); //(3) express1 = (x < y) ? x++ : y++; printf("(3)%d\r\n", express1); //(4) z += (x < y? x++ : y++); express1 = z; printf("(4)%d\r\n", express1); return 0; }2.知识点
大数阶乘思路(当数量级到10万时),将数字分奇数偶数,2与5的乘积消掉,结果补0
4种问题:常量问题,线性问题,对数问题,指数问题
ex1.
// DrawRhombus.cpp : Defines the entry point for the console application. // /************************************************************************/ /* 编程输出如下菱形: * * * * * * * * * * * * */ /************************************************************************/ #include "stdafx.h" #include <stdio.h> int main(int argc, char* argv[]) { int i = 0; int j = 0; for (i = 0; i < 7; i++) { for (j = 0; j < 7; j++) { if (i == j + 3) { printf("* "); } else if (i == -j + 3) { printf("* "); } else if (i == -j + 9) { printf("* "); } else if (i == j - 3) { printf("* "); } else { printf(" "); } } printf("\r\n"); } return 0; }ex2.
// ReplaceLoop.cpp : Defines the entry point for the console application. // /* if 和 goto 实现 while ,do while , for */ #include "stdafx.h" #include <stdio.h> /* while(nCount < 5) { printf("ReplaceWhile"); nCount++; } */ void ReplaceWhile() { int nCount = 0; __While: if (nCount < 5) { printf("ReplaceWhile\r\n"); nCount++; goto __While; } } /* do { printf("ReplaceDoWhlie"); nCount++; }while(nCount < 5) */ void ReplaceDoWhile() { int nCount = 0; __DoWhile: printf("ReplaceWhile\r\n"); nCount++; if (nCount < 5) { goto __DoWhile; } } /* for(nCount = 0; nCount < 5; nCount++) { printf("ReplaceFor"); } */ void ReplaceFor() { int nCount = 0; bool bFlag = false; __For: if (bFlag) { nCount++; } if (nCount < 5) { bFlag = true; printf("ReplaceFor"); goto __For; } } int main(int argc, char* argv[]) { ReplaceWhile(); ReplaceDoWhile(); ReplaceFor(); return 0; }ex3.
// PrimeNuber.cpp : Defines the entry point for the console application. // /************************************************************************/ /* 100以内质数 筛选法 */ /************************************************************************/ #include "stdafx.h" #include <stdio.h> void PrimeNum() { int nNum = 0; int nTotal[10][10] = {0}; int i; int j; for (i = 0; i < 10; i++) { for (j = 0; j < 10; j++) { if ((i == 0 && j == 2) || (i == 0 && j == 3) || (i == 0 && j == 5) || (i == 0 && j == 7)) { nTotal[i][j] = i * 10 + j; } else if (i == 0 && j == 1) { nTotal[i][j] = 0; } else if (((i * 10 + j) % 2 != 0) && ((i * 10 + j) % 3 != 0) && ((i * 10 + j) % 5 != 0) && ((i * 10 + j) % 7 != 0)) { nTotal[i][j] = i * 10 + j; } else { nTotal[i][j] = 0; } } } for (i = 0; i < 10; i++) { for (j = 0; j < 10; j++) { if (nTotal[i][j] != 0) { printf("%d ", nTotal[i][j]); } } } } int main(int argc, char* argv[]) { PrimeNum(); return 0; }ex4.
// MonkeyEatPeach.cpp : Defines the entry point for the console application. // /************************************************************************/ /* 猴子吃桃:有一天,一只猴子摘了一些桃子。它吃掉了一半,又吃了一个, 第二天也是这样,吃了所有桃子的一半,又多吃了一个... 到了第十天,小猴子一看,只有1个桃子了。 求最早的时候,小猴子摘了几个桃子? 1534 用等比数列 an = a1 * q^(n-1) 4 10 22 46 94 190 382 766 1534 a1 = (1 + 1) * 2 a2 = (a1 + 1) * 2 a3 = (a2 + 1) * 2 ... an = (a[n-1] + 1) * 2 x-(5+1/2x+1/4x+1/8x+1/16x+1/32x)=1 a1(1-q^n) sn = -----------x 1-q a1 = 1/2x q = 1/2 x - (5 + x (1- q ^ n)) = 1 x - x(1-(1/2)^8) = 6; x = 6 / ((1/2)^8) */ /************************************************************************/ #include "stdafx.h" #include <stdio.h> #include <math.h> void UseFor() { int TotalPeach = 4; int i; for (i = 0; i < 8; i++) { TotalPeach = (TotalPeach + 1) * 2; } printf("%d", TotalPeach); } void UseMath() { int TotalPeach = 0; double z = pow(0.5, 8); TotalPeach = 6 / z - 2; printf("%d", TotalPeach); } int main(int argc, char* argv[]) { UseFor(); printf("\r\n"); UseMath(); printf("\r\n"); return 0; }ex5.
// DrawStar.cpp : Defines the entry point for the console application. // /************************************************************************/ /* 画五角星 */ /************************************************************************/ #include "stdafx.h" #include <stdio.h> int main(int argc, char* argv[]) { float x; float y; for (x = 0; x <= 20; x++) { for (y = 0; y <= 20; y++) { if (4 * x - 20 == y) { printf("**"); } else if (x - 5 == y) { printf("**"); } else if (y == 15) { printf("**"); } else if(-x + 15 == y) { printf("**"); } else if (-4 * x + 60 == y) { printf("**"); } else { printf(" "); } } printf("\r\n"); } return 0; }3.知识点
复习反码编码:
(1100)b = -4
(0100)b = 4
补码:
(0100)b = 4
(0100)b 取反 : (1011)b
(0100)b + (1011)b =(1111)b
(0100)b + (1011)b + 1= (1111)b + 1 =(0000)b
(1001)b = (-1)*8 + 0 + 0 +1*1 = - 7
(1010)b = (-1)*8 + 0 + 1*2 +0 = -6
公式:(-an)2^n + an-1 *2^n-1 … a0 * 2^0
复习浮点编码
(10.0010101)b -> 1. 00010101* 21
关心: 1.有效数字 2.指数 3.符号
ex1.l练习素材 l练习素材
// ChangeValue.cpp : Defines the entry point for the console application. // /************************************************************************/ /* 实现自动修改rpg文件 没有实现十进制值转十六进制值 */ /************************************************************************/ #include "stdafx.h" FILE *stream, *stream2; void ShowMenu() { printf("=============修改rpg文件=============\r\n"); printf("1.修改金钱\r\n"); printf("2.修改生命\r\n"); printf("3.修改体力\r\n"); printf("4.修改武力\r\n"); printf("5.修改灵力\r\n"); printf("6.修改吉运\r\n"); printf("0.退出\r\n"); printf("=====================================\r\n"); } //改金钱 bool ChangeGold(int chValue) { bool bFlag = false; if( (stream = fopen( "1.rpg", "r+" )) == NULL ) { printf( "Open 1.rpg failed!\r\n" ); } else { //printf( "The file 'data' was opened\n" ); int ToChangeValue = chValue; int result = fseek( stream, 0x0028L, SEEK_SET); if( result ) { perror( "find address failed!\r\n" ); } int fwriteRes = fwrite(&ToChangeValue, sizeof(int), 1, stream); if (fwriteRes) { printf("changed success!\r\n"); bFlag = true; } fclose(stream); } return bFlag; } //改生命 bool ChangeLife(short chValue) { bool bFlag = false; if( (stream = fopen( "1.rpg", "r+" )) == NULL ) { printf( "Open 1.rpg failed!\r\n" ); } else { //printf( "The file 'data' was opened\n" ); short ToChangeValue = chValue; int result = fseek( stream, 0x0268L, SEEK_SET); if( result ) { perror( "find address failed!\r\n" ); } int fwriteRes = fwrite(&ToChangeValue, sizeof(short), 1, stream); if (fwriteRes) { printf("changed success!\r\n"); bFlag = true; } fclose(stream); } return bFlag; } //改体力 bool ChangePH(short chValue) { bool bFlag = false; if( (stream = fopen( "1.rpg", "r+" )) == NULL ) { printf( "Open 1.rpg failed!\r\n" ); } else { //printf( "The file 'data' was opened\n" ); short ToChangeValue = chValue; int result = fseek( stream, 0x0274L, SEEK_SET); if( result ) { perror( "find address failed!\r\n" ); } int fwriteRes = fwrite(&ToChangeValue, sizeof(short), 1, stream); if (fwriteRes) { printf("changed success!\r\n"); bFlag = true; } fclose(stream); } return bFlag; } //改武力 bool ChangeForce(short chValue) { bool bFlag = false; if( (stream = fopen( "1.rpg", "r+" )) == NULL ) { printf( "Open 1.rpg failed!\r\n" ); } else { //printf( "The file 'data' was opened\n" ); short ToChangeValue = chValue; int result = fseek( stream, 0x02c8L, SEEK_SET); if( result ) { perror( "find address failed!\r\n" ); } int fwriteRes = fwrite(&ToChangeValue, sizeof(short), 1, stream); if (fwriteRes) { printf("changed success!\r\n"); bFlag = true; } fclose(stream); } return bFlag; } //改灵力 bool ChangeFlexible(short chValue) { bool bFlag = false; if( (stream = fopen( "1.rpg", "r+" )) == NULL ) { printf( "Open 1.rpg failed!\r\n" ); } else { //printf( "The file 'data' was opened\n" ); short ToChangeValue = chValue; int result = fseek( stream, 0x02d0L, SEEK_SET); if( result ) { perror( "find address failed!\r\n" ); } int fwriteRes = fwrite(&ToChangeValue, sizeof(short), 1, stream); if (fwriteRes) { printf("changed success!\r\n"); bFlag = true; } fclose(stream); } return bFlag; } //改吉运 bool ChangeLucky(short chValue) { bool bFlag = false; if( (stream = fopen( "1.rpg", "r+" )) == NULL ) { printf( "Open 1.rpg failed!\r\n" ); } else { //printf( "The file 'data' was opened\n" ); short ToChangeValue = chValue; int result = fseek( stream, 0x02f8L, SEEK_SET); if( result ) { perror( "find address failed!\r\n" ); } int fwriteRes = fwrite(&ToChangeValue, sizeof(short), 1, stream); if (fwriteRes) { printf("changed success!\r\n"); bFlag = true; } fclose(stream); } return bFlag; } int main(int argc, char* argv[]) { int nIndex = 0; int gold = 0x00; short life = 0x00; short PH = 0x00; short force = 0x00; short flexible = 0x00; short lucky = 0x00; while (1) { ShowMenu(); printf("请选择要修改的项目:"); scanf("%d", &nIndex); switch(nIndex) { case 0: printf("谢谢!\r\n"); return 0; case 1: printf("请输入要设置的值(十六进制填写):"); scanf("%x", &gold); ChangeGold(gold); break; case 2: printf("请输入要设置的值(十六进制填写):"); scanf("%x", &life); ChangeLife(life); break; case 3: printf("请输入要设置的值(十六进制填写):"); scanf("%x", &PH); ChangePH(PH); break; case 4: printf("请输入要设置的值(十六进制填写):"); scanf("%x", &force); ChangeForce(force); break; case 5: printf("请输入要设置的值(十六进制填写):"); scanf("%x", &flexible); ChangeFlexible(flexible); break; case 6: printf("请输入要设置的值(十六进制填写):"); scanf("%x", &lucky); ChangeLucky(lucky); break; default: printf("输入错误请重新输入\r\n"); break; } } return 0; }ex2.
// Sprintf.cpp : Defines the entry point for the console application. // /************************************************************************/ /* sprintf用法 int sprintf( char *buffer, const char *format [, argument] ... ); 格式化写入数据到字符串 buffer 存储到本地输出 format 格式化字符串 argument 参数 */ /************************************************************************/ #include "stdafx.h" int main(int argc, char* argv[]) { char buffer[200], s[] = "computer", c = 'l'; int i = 35, j; float fp = 1.7320534f; /* Format and print various data: */ j = sprintf( buffer, "\tString: %s\n", s ); j += sprintf( buffer + j, "\tCharacter: %c\n", c ); j += sprintf( buffer + j, "\tInteger: %d\n", i ); j += sprintf( buffer + j, "\tReal: %f\n", fp ); printf( "Output:\n%s\ncharacter count = %d\n", buffer, j ); return 0; }ex3.
// SscanfDemo.cpp : Defines the entry point for the console application. // /************************************************************************/ /* Sscanf用法 int sscanf( const char *buffer, const char *format [, argument ] ... ); 从一个字符串格式化读取数据 buffer 存储数据 format 格式化控制字符串 argument 参数 */ /************************************************************************/ #include "stdafx.h" int main(int argc, char* argv[]) { char tokenstring[] = "15 12 14..."; char s[81]; char c; int i; float fp; /* Input various data from tokenstring: */ sscanf( tokenstring, "%s", s ); sscanf( tokenstring, "%c", &c ); sscanf( tokenstring, "%d", &i ); sscanf( tokenstring, "%f", &fp ); /* Output the data read */ printf( "String = %s\n", s ); printf( "Character = %c\n", c ); printf( "Integer: = %d\n", i ); printf( "Real: = %f\n", fp ); return 0; } 4.知识点Windeff工具:源代码对比
标号代表地址
利用标号可以计算代码长度
函数特点:模块化设计
/P 查看预处理结果,生成.i文件,一般用于检查宏
注释:函数作用,参数,返回值,其他注意事项
函数三个内容:语法,原理,设计(强内聚,低耦合)
函数原理
调用者与被调用者需要进行约定
约定内容:参数如何传递(寄存器或者栈)
参数传递方向(右到左或者左到右)
返回值存哪里
平衡栈顶
4种调用约定:
__cdecl,默认,栈传递,从右到左,返回值在寄存器中(eax),调用方清理栈,支持不定参
__stdcall,/GZ,参数传递,方向,返回值,同C调用约定,被调用方(实现者),不支持不定参
__fastcall, /Gr,寄存器传递前2个参数,其余使用栈传递,从右到左,返回值在寄存器中,被调用方(实现者)平衡栈空间
thiscall ,C++中成员函数调用。
函数调用机制
1,按调用约定传递参数
2,保存返回值
3,转移流程到目标函数代码处
4,保存调用方栈信息(栈底)
5,将栈信息调整到新的地址
6,申请局部变量空间,(在/Od,/Zi选项中局部变量空间初始化为0xCC)
7,保存寄存器信息
9,执行函数代码
10,恢复寄存器信息
11,释放局部变量空间
12,取出返回地址流程转到返回地址(__stdcall与__fastcall释放参数空间)
13,__cdecl释放参数空间
函数栈布局笔记
code.
// Function.cpp : Defines the entry point for the console application. // /************************************************************************/ /* 分析函数调用机制 详细过程见笔记 */ /************************************************************************/ #include "stdafx.h" #include <stdlib.h> void ShowPrint(int Times) { int i = 0; for (; i < Times; i++) { printf("第%d次打印\r\n", Times); } } int main(int argc, char* argv[]) { int nVar = 0; scanf("%d", &nVar); printf("0xx\r\n", &nVar); ShowPrint(nVar); system("pause"); return 0; }1,按F10进入调试状态,按ALT+8进入反汇编窗口,代码如图1所示,内存状态如图2所示:
在运行之前,在内存图2中,紫色框为main函数参数,红色框为所返回地址,绿色框为第一条指令push ebp执行之后保存了栈底地址0x0018FF88,mov ebp,esp 和sub esp,44h执行后将建立main函数的栈空间,push 指令保存寄存器的信息,lea指令获取栈顶地址,循环赋值0xCC申请局部变量空间如图3所示:
图1 反汇编代码
图2 内存初始状态图
图3 执行完main函数初始化栈
2.scanf函数和printf函数执行完之后代码如图4所示
图4 执行完printf后
由调用者 add esp8 平衡堆栈,此时寄存器ebp可知ebp还原到0x0018ff48,即回到了main函数栈空间,如图所示:
图5 寄存器信息获取ebp地址
3.首先传递参数nVar,由指令push完成,如图所示内存为传递的参数信息,
图6 参数信息
4.保存返回mian函数的地址信息
图7 返回值
5.反汇编代码,此过程和mian函数过程一样,执行完后内存如图所示:
图8 反汇编代码
6.初始化完成后如图所示:
图9 内存信息如图所示
以上为一个函数调用后,在执行函数代码体之前的内存状态分布,函数体执行完之后由调用方平衡堆栈(当前调用方式),ebp = 0x0018FF48
ex1.
// Prime.cpp : Defines the entry point for the console application. // /************************************************************************/ /* 1. 写一个函数int prime(int x),如果x是素数返回值为1,否则返回0。并用主函数验证它。 */ /************************************************************************/ #include "stdafx.h" #include <math.h> int prime(int x) { int i; if (x == 2) { return 1; } if (x == 1 || x % 2 == 0) { return 0; } for (i = 3; i <= sqrt(x); i++) { if (x % i == 0) { return 0; } } return 1; } int main(int argc, char* argv[]) { int nNum = 0; printf("请输入一个数:"); scanf("%d", &nNum); if (1 == prime(nNum)) { printf("%d是素数!\r\n", nNum); } else { printf("%d不是素数!\r\n", nNum); } return 0; }ex2.
// Power.cpp : Defines the entry point for the console application. // /************************************************************************/ /* 2. 写一个函数power(double x, int n),其返回值为x的n次方,并用此函数计算1.5 3。 */ /************************************************************************/ #include "stdafx.h" double power(double x, int n) { double Res = 1; for (int i = 0; i < n; i++) { Res = Res * x; } return Res; } int main(int argc, char* argv[]) { printf("1.5的3次方为:%lf\r\n", power(1.5, 3)); return 0; }ex3.
// PerAndCom.cpp : Defines the entry point for the console application. // /************************************************************************/ /* 3. 求1到10共10个数中取出3个不同的数,共有多少种组合方式? 算法:使用数学中的组合公式,其中m=10,n=3。 1,2,3,4,C(4.2)表示4个数字中选2个,不考虑顺序 C(4.2)=4*3/1*2=6. 1,2,3,4,A(4.2)表示4个数字中选2个,考虑顺序. A(4.2)=4*3=12. C(10, 3) = 10 * 9 * 8 * 7/1 * 2 * 3 = 840 C(M.N)=M*(M-1)(M-2)……(M-N)/1*2*3……*N (M为下标,N为上标) A(M.N)=M*(M-1)(M-2)……(M-N) (M为下标,N为上标) */ /************************************************************************/ #include "stdafx.h" int PreAndCom(int mNumber, int nNumber) { int upFlag = 10; int downFlag = 1; for (int i = mNumber; i > (mNumber-nNumber); i--) { upFlag = upFlag * (i - 1); } for (int j = 1; j <= nNumber; j++) { downFlag = downFlag * j; } int Res = upFlag / downFlag; return Res; } int main(int argc, char* argv[]) { printf("1到10共10个数中取出3个不同的数,共有%d种组合\r\n", PreAndCom(10, 3)); return 0; }