File -> New -> Projects -> win32 Console Application -> Ok -> A "Hello, World!" application -> Finish -> Ok -> FileView -> test.cpp
F5 运行
F7 编译
F9 断点
shitg + F5 结束程序
File -> Close Workspace
File -> Open Workspace
在程序执行时,界面上面右键点击Registers
在程序执行时,界面上面右键点击Memory
在程序执行时,右键Go To Disassembly
再反汇编窗口右键点Source Annotation
再反汇编窗口右键点 Code Bytes
反汇编中进入某个程序按F11(相当于汇编中的F7)
F10(相当于汇编中的F8)
# void 代表这个函数什么都不返回,返回什么类型void就改成什么类型 void 函数名() { }
int Plus1(int x,int y) { return x + y; } int main(int argc, char* argv[]) { Plus1(1,2); return 0; }
#include "stdafx.h" int Plus1(int x,int y) { return x + y; } int Plus2(int x,int y,int z) { int t; int r; t = Plus1(x,y); r = Plus1(t,z); return r; } int main(int argc, char* argv[]) { Plus2(1,2,3); return 0; }
正常函数,编译器会自动帮我们生成一些东西,裸函数则是编译器啥都不生成
void __declspec(naked) Plus() { } int main(int argc, char* argv[]) { Plus(); return 0; }
编译可以正常编译,但是执行会报错,原因是函数执行之后没有返回地址,需要手动添加
#include "stdafx.h" void __declspec(naked) Plus() { # 在编译器中写汇编代码如下 __asm { ret } } int main(int argc, char* argv[]) { Plus(); return 0; }
正常函数编译器会自动帮我们生成,以下代码,裸函数需要我们手动添加以下代码
#include "stdafx.h" int __declspec(naked) Plus(int x,int y) { __asm { //保留调用函数前的栈底 push ebp //提升堆栈 mov ebp,esp sub esp,0x40 //保存现场 push ebx push esi push edi //填充缓冲区 mov eax,0xcccccccc mov ecx,0x10 Lea edi,dword ptr ds:[ebp-0x40] rep stosd //函数的核心功能,两个参数相加 mov eax,dword ptr ds:[ebp+0x8] add eax,dword ptr ds:[ebp+0xc] //恢复现场 pop edi pop esi pop ebx //降低堆栈 mov esp,ebp pop ebp ret } } int main(int argc, char* argv[]) { Plus(1,2); return 0; }
调用约定 | 参数压栈顺序 | 平衡堆栈 |
---|---|---|
__cdecl | 从右至左入栈 | 调用者清理栈 |
__stdcall | 从右至左入栈 | 自身清理堆栈 |
__fastcall | ECX/EDX传送前两个,剩下:从右至左入栈 | 自身清理堆栈 |
#include "stdafx.h" int __cdecl Plus1(int a, int b) { return a+b; } int main(int argc, char* argv[]) { Plus1(1,2); return 0; }
#include "stdafx.h" int __stdcall Plus2(int a, int b) { return a+b; } int main(int argc, char* argv[]) { Plus2(1,2); return 0; }
当传递的参数为2个时,用寄存器传递,速度较快,且不需要平栈
当参数多余两个时,与其它两种调用方式没啥区别了,在函数里面进行平栈
#include "stdafx.h" int __fastcall Plu3(int a, int b) { return a+b; } int main(int argc, char* argv[]) { Plus3(1,2); return 0; }
main是我们代码的入口的方法不是真正程序入口
int main(int argc, char* argv[]) { return 0; }
真正的程序入口
char 8BIT 1字节
short 16BIT 2字节
int 32BIT 4字节
long 32BIT 4字节
#include "stdafx.h" void Plus() { char i = 0xFF; unsigned char k = 0xFF; } int main(int argc, char* argv[]) { Plus(); return 0; }
1、先将这个实数的绝对值化为二进制格式 2、将这个二进制格式实数的小数点左移或右移n位,直到小数点移动到第一个有效数字的右边。 3、从小数点右边第一位开始数出二十三位数字放入第22到第0位。 4、如果实数是正的,则在第31位放入“0”,否则放入“1”。 5、如果n 是左移得到的,说明指数是正的,第30位放入“1”。如果n是右移得到的或n=0,则第30位放入“0”。 6、如果n是左移得到的,则将n减去1后化为二进制,并在左边加“0”补足七位,放入第29到第23位。如果n是右移得到的或n=0,则将n化为二进制后在左边加“0”补足七位,再各位求反,再放入第29到第23位。
标准ASCII码只有7位,最高位永远是0(表示128可能的字符,表示常用的字符a-z,A-Z,0-9,逗号等)
扩展ASCII码,最高位永远是1(扩展ASCI码允许将每个字符的第8位用于确定附加的128 个特殊符号字符、外来语字母和图形符号)
#include "stdafx.h" void Plus() { char i = 'A'; } int main(int argc, char* argv[]) { Plus(); return 0; }
GB2312需要用两个字节来表示,一个小于127的字符的意义与原来相同,但两个大于127的字符连在一起时,就表示一个汉字,前面的一个字节(他称之为高字节)从0xA1用到0xF7,,后面一个字节(低字节)从0xA1,到0xFE,这样我们就可以组合出大约7000多个简体汉字了
全局变量
全局变量在程序编译完成后地址就已经确定下来了,只要程序启动,全局变量就已经存在了,启动后里面是否有值取决于声明时是否给定了初始值,如果没有,默认为0
全局变量的值可以被所有函数所修改,里面存储的是最后一次修改的值.
全局变量所占内存会一直存在,知道整个进程结束.
全局变量的反汇编识别:
MOV 寄存器,byte/word/dword ptr ds:[0x12345678]
局部变量
局部变量在程序编译完成后并没有分配固定的地址.
在所属的方法没有被调用时,局部变量并不会分配内存地址,只有当所属的程序被调用了,才会在堆栈中分配内存.
当局部变量所属的方法执行完毕后,局部变量所占用的内存将变成垃圾数据.局部变量消失.
局部变量只能在方法内部使用,函数A无法使用函数B的局部变量.
局部变量的反汇编识别:
[ebp-4]
#include "stdafx.h" int a; void test(int x,int y) { if(x>y) { a = x; } } int main(int argc, char* argv[]) { test(3,5); return 0; }
// test.cpp : Defines the entry point for the console application. // #include "stdafx.h" int g; void test(int x,int y) { if(x>y) { g = x + g; }else { g = y + g; } } int main(int argc, char* argv[]) { test(3,5); return 0; }
特点分析
声明变量就是告诉计算机,我要用一块内存,你给我留着,宽度和存储格式有数据类型决定
计算机什么时候把这块内存给你,取决于变量的作用范围,如果是全局变量,在程序编译完就已经分配了空间,如果是局部变量,只有所在的程序被调用的时候,才会分配空间
全局变量如果不赋初始值,默认是0,但局部变量在使用前一定要赋初值,不然就是CCCCCCCC
MOVSX 先符号扩展,再传送(结果是FFFF,符号位是什么扩展的部分就是什么,MOVSX用于有符号) MOV AL,0FF MOVSX CX,AL MOV AL,80 MOVSX CX,AL MOVZX 先零扩展,再传送(结果是00FF,扩展的部分全填0,MOVZX用于无符号) MOV AL,0FF MOVZX CX,AL MOV AL,80
表达式无论多么复杂,都只有一个结果
只有表达式,可以编译通过,但并不生成代码,需要与赋值或者其他流程控制语句一起组合的时候才有意义.
当表达式中存在不同宽度的变量时,结果将转换为宽度最大的那个.
当表达式中同时存在有符号和无符号数的时候,表达式的结构将转换为无符号数
影响内存或者cpu的就叫语句
#include "stdafx.h" void test() { int x = 2; int y = 1; x = x + y; y = x - y; x = x - y; printf("%d:%d",x,y); } int main(int argc, char* argv[]) { test(); getchar(); return 0; }
#include "stdafx.h" void test() { int arr[5] = {2,4,1,5,3}; int i = 4; while(i>=0) { printf("%d\n",arr[i]); i--; } } int main(int argc, char* argv[]) { test(); getchar(); return 0; }
#include "stdafx.h" void test() { int arr[5] = {2,4,1,5,3}; int r = arr[0]; int i = 1; while(i<=4) { if(arr[i]>r) { r = arr[i]; } i++; } printf("%d",r); } int main(int argc, char* argv[]) { test(); getchar(); return 0; }
#include "stdafx.h" int test(int x) { int i = 2; while(i<x) { if(x%i == 0) { return 0; } i++; } return 1; } int main(int argc, char* argv[]) { int a = test(9); printf("%d",a); getchar(); return 0; }
#include "stdafx.h" void test() { int arr[5] = {2,5,1,4,3}; int t = 0; int i = 0; while(i<4) { if(arr[i] > arr[i+1]) { t = arr[i]; arr[i] = arr[i+1]; arr[i+1] = t; } i++; } int k = 0; while(k<5) { printf("%d\n",arr[k]); k++; } } int main(int argc, char* argv[]) { test(); getchar(); return 0; }
#include "stdafx.h" int test() { int arr[5] = {1,2,3,2,1}; int t = 4; int i = 0; while(i<t) { if(arr[i] != arr[t]) { return 0; } i++; t--; } return 1; } int main(int argc, char* argv[]) { int a =test(); printf("%d",a); getchar(); return 0; }
#include "stdafx.h" void test() { int arr[8] = {61,21,34,18,16,33,58,45}; int lenght = 8; int i =0; int k = 0; while(i<lenght-1) { for(k=0;k<lenght-1-i;k++) { if(arr[k] > arr[k+1]) { arr[k] = arr[k] + arr[k+1]; arr[k+1] = arr[k] - arr[k+1]; arr[k] = arr[k] - arr[k+1]; } } i++; } int m = 0; while(m<8) { printf("%d\n",arr[m]); m++; } } int main(int argc, char* argv[]) { test(); getchar(); return 0; }
如果本机是32位的,那么对32位的数据支持最好,如果是64位的,那么对64位的支持最好
整数类型的参数,一律使用int类型,char类型或者short类型的参数不但没有节省空间,反而浪费了多余的操作.
参数传递的本质:将上层函数的变量,或者表达式的值“复制一份”,传递给下层函数
小于32位的局部变量,空间在分配时,按32位分配.
使用时按实际的宽度使用.
不要定义char/short类型的局部变量.
参数与局部变量没有本质区别,都是局部变量,都在栈中分配.
完全可以把参数当初局部变量使用
#include "stdafx.h" void test() { } int main(int argc, char* argv[]) { test(); getchar(); return 0; }
#include "stdafx.h" void test() { char a = 1; char b = 2; } int main(int argc, char* argv[]) { test(); getchar(); return 0; }
#include "stdafx.h" __int64 Function() { __int64 x = 0x1234567890; return x; } int main(int argc, char* argv[]) { __int64 x = Function(); getchar(); return 0; }
char arr[4] :
void test() { char arr[4] = {0}; } int main(int argc, char* argv[]) { test(); return 0; }
char arr[3]:
#include "stdafx.h" void test() { char arr[3] = {0}; } int main(int argc, char* argv[]) { test(); return 0; }