1.循环语句
while循环 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 #include <stdio.h> int main () { int a = 10 ; while (a < 20 ) { printf ("a 的值: %d\n" , a); a++; if (a > 15 ) { break ; } } return 0 ; }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 int a = 10; 00007FF72566520B mov dword ptr [a],0Ah /* while 循环执⾏ */ while (a < 20) 00007FF725665212 cmp dword ptr [a],14h 00007FF725665216 jge main+49h (07FF725665239h) { printf("a 的值: %d\n", a); 00007FF725665218 mov edx,dword ptr [a] 00007FF72566521B lea rcx,[string "a \xb5\xc4\xd6\xb5\xa3\xba %d\n" (07FF725669C10h)] 00007FF725665222 call printf (07FF72566118Bh) a++; 00007FF725665227 mov eax,dword ptr [a] 00007FF72566522A inc eax 00007FF72566522C mov dword ptr [a],eax if (a > 15) 00007FF72566522F cmp dword ptr [a],0Fh 00007FF725665233 jle main+47h (07FF725665237h) { /* 使⽤ break 语句终⽌循环 */ break; 00007FF725665235 jmp main+49h (07FF725665239h) } } 00007FF725665237 jmp main+22h (07FF725665212h) return 0; 00007FF725665239 xor eax,eax }
break 后直接跳出循环
for循环 1 2 3 4 5 6 7 8 9 10 #include <stdio.h> int main () { for (int a = 10 ; a < 20 ; a = a++) { printf ("a 的值: %d\n" , a); } return 0 ; }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 /* for 循环执⾏ */ for (int a = 10; a < 20; a = a++) 00EC1865 mov dword ptr [ebp-8],0Ah 00EC186C jmp __$EncStackInitStart+31h (0EC187Dh) 00EC186E mov eax,dword ptr [ebp-8] 00EC1871 mov dword ptr [ebp-8],eax 00EC1874 mov ecx,dword ptr [ebp-8] 00EC1877 add ecx,1 00EC187A mov dword ptr [ebp-8],ecx 00EC187D cmp dword ptr [ebp-8],14h 00EC1881 jge __$EncStackInitStart+4Ah (0EC1896h) { printf("a 的值: %d\n", a); 00EC1883 mov eax,dword ptr [ebp-8] 00EC1886 push eax 00EC1887 push offset string "a \xb5\xc4\xd6\xb5\xa3\xba %d\n" (0EC7B30h) 00EC188C call _printf (0EC10CDh) 00EC1891 add esp,8 } 00EC1894 jmp __$EncStackInitStart+22h (0EC186Eh) return 0; 00EC1896 xor eax,eax }
do….while循环 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 #include <stdio.h> int main () { int a = 10 ; do { if (a == 15 ) { a = a + 1 ; continue ; } printf ("a 的值: %d\n" , a); a = a++; } while (a < 20 ); return 0 ; }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 /* 局部变量定义 */ int a = 10; 00261865 mov dword ptr [a],0Ah /* do 循环执⾏,在条件被测试之前⾄少执⾏⼀次 */ do { if (a == 15) 0026186C cmp dword ptr [a],0Fh 00261870 jne __$EncStackInitStart+31h (026187Dh) { /* 跳过迭代 */ a = a + 1; 00261872 mov eax,dword ptr [a] 00261875 add eax,1 00261878 mov dword ptr [a],eax continue; 0026187B jmp __$EncStackInitStart+51h (026189Dh) } printf("a 的值: %d\n", a); 0026187D mov eax,dword ptr [a] 00261880 push eax 00261881 push offset string "a \xb5\xc4\xd6\xb5\xa3\xba %d\n" (0267B30h) 00261886 call _printf (02610CDh) 0026188B add esp,8 a = a++; 0026188E mov eax,dword ptr [a] 00261891 mov dword ptr [a],eax 00261894 mov ecx,dword ptr [a] 00261897 add ecx,1 0026189A mov dword ptr [a],ecx } while (a < 20); 0026189D cmp dword ptr [a],14h 002618A1 jl __$EncStackInitStart+20h (026186Ch) return 0; 002618A3 xor eax,eax }
这里的循环都很简单,主要是要自己写出来后去反汇编一下。多看看这些反汇编后的代码。
2.结构体 结构体? 结构体就是数组升级版….可以自定义其中的数据类型。
定义 用 struct
1 2 3 4 5 6 7 8 struct tag { member-list member-list member-list ... } variable-list ; 也可以在后续中 struct tag lll ;
tag 是结构体的标签。也就是一个大名字
member-list 标准的变量定义。比如:int.. float.. char..
variable-list 结构体变量。【对象?】
比如:
1 2 3 4 5 6 7 struct Books { char title[50 ]; char author[50 ]; char subject[100 ]; int book_id; } book;
1 2 3 4 5 6 7 8 9 struct { int a; char b; double c; } s1;
1 2 3 4 5 6 7 8 struct SIMPLE { int a; char b; double c; }; struct SIMPLE t1 , t2 [20], *t3 ;
1 2 3 4 5 6 7 8 9 typedef struct { int a; char b; double c; } Simple2; Simple2 u1, u2[20 ], *u3;
结构体里面可以包含结构体哦~
1 2 3 4 5 6 struct COMPLEX { char string [100 ]; struct SIMPLE a ; };
结构体里面可以包含自己结构体类型的指针
1 2 3 4 5 6 struct NODE { char string [100 ]; struct NODE *next_node ; };
如果2给结构体相互包含,则需要对其中一个结构体进行不完整声明
1 2 3 4 5 6 7 8 9 10 11 12 struct B ; struct A { struct B *partner ; }; struct B { struct A *partner ; };
结构体的初始化 1 2 3 4 5 6 7 8 9 10 11 12 13 14 #include <stdio.h> struct Books { char title[50 ]; char author[50 ]; char subject[100 ]; int book_id; } book = {"C 语⾔" , "RYH" , "编程语⾔" , 123456 }; int main () { printf ("title : %s\nauthor: %s\nsubject: %s\nbook_id: %d\n" , book.title, book.author, book.subject, book.book_id); }
访问结构成员 访问的时候,用:结构体变量名.成员名字
结构题变量的储存原理 1.数据成员对齐。 在内存中各类型的数据会按照一定的规律在内存中存放,这样就是对齐。结构体所占的内存空间就是每个成员对齐后存放的时候所占用的字节数。
how?
数据的起始地址的值要求是某个数K的倍数,而K就是该数据类型的对齐模数。
why?
加快速度,提升读取速度。
2.计算结构体大小的计算方法 对齐模数是 #pragma pack 指定的数值与该数据成员自身长度相比较得到的数值较小者。该数据相对起始位置应该是对齐数模的整数倍。
数模A是:#pragma pack指定的数值和结构体内最大的基本数据类型成员长度相比得到的数值较小者
然后结构体的长度应该是数模A的整数倍
#pragma pack指令的作用就是改变编译器的默认对齐方式
实例 1.
实例2 .
分析一手:第一题:
这样就只占有了8个字节的位置。
int型开始的时候,需要是0,4,8,…就是比较后小的的倍数
char 啥都可以
short 的 是2个字节,所以需要是 0 2 4 6….【2的倍数】,这也就是这个5为什么要跳过的原因
第二题:
这里 int a 开始的时候需要是数模的倍数【int 是4 默认是8 较小的是4 , 所以需要4的倍数。也就是这里空了3个格子的原因】
short c 这里开始的时候刚好的8【2的倍数】所以就不需要空。
从0~9整体是10。这个时候结构体也需要对齐。结构体的数模是:定义的最大和默认比较中最小的【也就是int 和默认的比较 也就是4】需要是这个数模的倍数,也就是4的倍数。所以需要扩大2个字节,变成12。
所以整个结构体的占有的空间是12个字节
3.函数调用约定 这个函数调用的约定,其实,我们再win32中已经有接触过。这里来讲一讲C语言的
在C语言中,假设我们有这样的一个函数
1 int function (int a,int b)
在调用的时候,只要用result = function(1,2)这样的方式就可以使用这个函数了。
在CPU中,计算机是如何知道这函数调用的参数呢?这里计算机就提供了一个称为栈的数据结构来支持参数传递
栈的简述:是一种先进后出的数据结构。这里就不再累赘了。
再参数的传递中,有2个很重要的问题
当参数多于1个的时候,按照什么样的顺序压入堆栈【从左到右?从右到左?】
函数调用会,由谁来把堆栈恢复原来的状态
函数几种调用约定
stdcall 【pascal】
cdecl
fastcall
thiscall
naked call
stdcall【pascal】 Standard Call 的缩写 ,C++的标准调用方式
再微软C++系类的C/C++编译器中,常用PASCAL宏来声明这个调用约定,类似的宏还有WINAPI和CALLBACK
一般WIN32的函数都是__stdcall
声明的语法为
1 2 3 4 5 int __stdcall function (int a, int b)
例子:
1 2 3 4 int __stdcall Plus (int a, int b) { return a + b; }
对应的汇编代码:
1 2 3 4 5 6 7 push 2 push 1 call @ILT+10(Plus) (0040100f) 函数内部: ret 8 ;这句是call对应的,内平栈
cdecl C Declaration 的缩写 ,C语言缺省的调用约定【C语言默认调用约定】
声明语法:
1 2 3 4 5 6 7 int function (int a ,int b) int __cdecl function (int a,int b)
例子:
1 2 3 4 int __cdecl Plus (int a, int b) { return a + b; }
对应的汇编代码:
1 2 3 4 push 2 push 1 call @ILT+15(Plus) (00401014) add esp,8 ;这个就是外⾯平衡堆栈,传⼊两个参数,且为int型4byte,所以是8
fastcall 快速的call , 所以会用寄存器
声明语法:
1 2 3 4 5 6 int fastcall function (int a,int b)
例字1:
1 2 3 4 int __fastcall Plus (int a, int b) { return a + b; }
对应的汇编代码:
1 2 3 4 5 mov edx,2 ;因为传⼊寄存器不需要平衡堆栈 mov ecx,1 call @ILT+0(Plus) (00401005) ret
例子2:
1 2 3 4 int __fastcall Plus4 (int a, int b, int c, int d) { return a + b + c + d; }
对应的汇编:
1 2 3 4 5 6 7 push 4 ;当参数⼤于两个时,编译器会倒着存⼊堆栈,剩两个存⼊寄存器⾥ push 3 mov edx,2 mov ecx,1 call @ILT+5(Plus) (0040100a) ret 8 ;外平栈,平衡传入参数的一部分
thiscall C++类成员函数缺省的调用约定
thiscall是唯一一个不能明确指定的函数修饰,因为thiscaall不是关键字。由于成员函数调用还有一个this指针。所以需要特殊处理
参数从右向左⼊栈
如果参数个数不确定,this指针在所有参数压栈后被压⼊堆栈。如果参数个数确定,
this指针通过ecx传递给被调⽤者。
如果参数个数不确定,调⽤者清理堆栈,否则函数⾃⼰清理堆栈
对于参数个数固定情况下,类似于stdcall,不定时则类似cdecl
naked call 很少见,自己百度。一般用于实模式驱动程序设计
函数调用约定的常见问题 如果定义的约定和使用的约定不一致,则将导致堆栈被破坏,导致严重问题
函数原型声明和函数体定义不一致
DLL【动态链接库】导入函数的时候声明了不同的函数约定
调用函数的代码和被调用函数必须采用相同的函数的调用约定,这样程序才能正常的运行。