1、变量的声明?什么是赋值?
//全局变量的声明
int a,b,c; //全局变量的声明
void Fun()
{
a = 10; //全局变量的赋值
b = 20;
c = a;
}
void Fun()
{
int a,b,c; //局部变量的声明
a = 10; //局部变量的赋值
b = 20;
c = a;
}
声明变量就是告诉计算机,我要用一块内存,你给我留着,宽度和存储格式有数据类型决定.
计算机什么时候把这块内存给你,取决于变量的作用范围,如果是全局变量,在程序编译完就已经分配了空间,如果是局部变量,只有
所在的程序被调用的时候,才会分配空间.
全局变量如果不赋初始值,默认是0,但局部变量在使用前一定要赋初值.
全局变量在程序运行之初就已经有自己的内存空间了 但是局部变量只有在函数运行时候才会又堆栈分配内存
2、类型转换
// C语言正向.cpp : Defines the entry point for the console application.
//
#include "stdafx.h"
int main(int argc, char* argv[])
{
char i= 1;
int j = i;
return 0;
}
反汇编代码:
8: char i= 1;
00401028 mov byte ptr [ebp-1],1
9: int j = i;
0040102C movsx eax,byte ptr [ebp-1]
00401030 mov dword ptr [ebp-8],eax
10: return 0;
00401033 xor eax,eax
把一个char类型的变量赋值给int,这种是隐式类型转换(提升),先把char提升到32位,可以看汇编代码的movsx(先符号扩展再传送) 也就是有符号扩展 ,那如果是char和int 做加法?
8: char i= 1;
00401028 mov byte ptr [ebp-1],1
9: short m = 2;
0040102C mov word ptr [ebp-4],2
10: int j = i+m;
00401032 movsx eax,byte ptr [ebp-1]
00401036 movsx ecx,word ptr [ebp-4]
0040103A add eax,ecx
0040103C mov dword ptr [ebp-8],eax
可以看到加法也同样提升到了int在进行相加
隐式提升:
只要下面这些类型出现在表达式中(作为操作数):
char / signed char / unsigned char
short / unsigned short
就一定会先执行整型提升。
赋值运算符 = 的右边会提升;左边不会提升(左边变量是什么类型就保持什么类型)
如何提升:
分为有符号提升和无符号提升
MOVSX 先符号扩展,再传送.
MOV AL,0FF
MOVSX CX,AL
MOV AL,80
MOVSX CX,AL
MOVZX 先零扩展,再传送.
MOV AL,0FF
MOVZX CX,AL
MOV AL,80
MOVSX CX,AL
举例:
//有符号小转大
void Function1()
{
char ci = 0xFF;
short si = ci;
int ni = ci;
}
我们先自己写出来汇编是个什么情况 然后去VC6里面看一下
movsx ax,[ebp-1]
mov [ebp-4],ax
movsx eax,[ebp-4]
mov [ebp-8],eax
8: char ci = 0xFF;
00401028 mov byte ptr [ebp-1],0FFh
9:
10: short si = ci;
0040102C movsx ax,byte ptr [ebp-1]
00401031 mov word ptr [ebp-4],ax
11:
12: int ni = ci;
00401035 movsx eax,byte ptr [ebp-1]
00401039 mov dword ptr [ebp-8],eax
13: }
0040103C xor eax,eax
无符号也是同样的无非是 movsx换成了movzx无符号扩展 小类型转大类型 从低位向高位截断
3、什么是表达式?表达式的结果
有符号数和无符号数出现在一个表达式 按无符号数的形式计算
无符号数的用法
小容器往大容器放 大容器和小容器的符号不一样 以谁的符号为统一 符号扩展以自身 不一定有符号的就是符号位是1 比如char i = 1 就是00000001 扩展任然是1
void Fun()
{ char ci = 1;
unsigned int ni = 0XFFFFFFFE+ci;
printf("%d",ni);
}
ci先扩展int也就是00000001 int继续扩展 unsigned int 然后相加 至于海哥举的例子输出也是我们自己定义的 实际这些数值在内存中是没区别的 以我们的意志为转移
3、什么是语句?什么是程序块?
语句和表达式的区别 语句会影响内存分配或者CPU 表达式不会
声明语句
赋值语句
流程控制语句IF...ELSE...是个整体
什么是程序块?为什么要有程序块?
C 语言里,被一对大括号 { } 包裹起来的全部代码,就是一个程序块(代码块)
1.打包多条语句,当成一条语句使用
2.定义局部作用域,管控局部变量(最核心)
3.变量同名时,内层块可以屏蔽外层变量
4、什么时候需要用到参数?
比如:能够计算任意两个整数的和 需要我们为程序传入外部数据
5、什么时候需要用到返回值?
没有返回值的函数的反汇编
0040100F int 3
--- D:\VC6.0 完整绿色版\MyProjects\C语言正向\C语言正向.cpp -------------------------------------------------------------------------------------------------------------------------------
1: // C语言正向.cpp : Defines the entry point for the console application.
2: //
3:
4: #include "stdafx.h"
5: void Fun()
6: {
00401010 push ebp
00401011 mov ebp,esp
00401013 sub esp,48h
00401016 push ebx
00401017 push esi
00401018 push edi
00401019 lea edi,[ebp-48h]
0040101C mov ecx,12h
00401021 mov eax,0CCCCCCCCh
00401026 rep stos dword ptr [edi]
7: char ci = 1;
00401028 mov byte ptr [ebp-1],1
8: unsigned int ni = 0XFFFFFFFE+ci;
0040102C movsx eax,byte ptr [ebp-1]
00401030 sub eax,2
00401033 mov dword ptr [ebp-8],eax
9:
10: }
00401036 pop edi
00401037 pop esi
00401038 pop ebx
00401039 mov esp,ebp
0040103B pop ebp
0040103C ret
有返回值的函数的反汇编
--- D:\VC6.0 完整绿色版\MyProjects\C语言正向\C语言正向.cpp -------------------------------------------------------------------------------------------------------------------------------
1: // C语言正向.cpp : Defines the entry point for the console application.
2: //
3:
4: #include "stdafx.h"
5: int Fun()
6: {
00401020 push ebp
00401021 mov ebp,esp
00401023 sub esp,48h
00401026 push ebx
00401027 push esi
00401028 push edi
00401029 lea edi,[ebp-48h]
0040102C mov ecx,12h
00401031 mov eax,0CCCCCCCCh
00401036 rep stos dword ptr [edi]
7: char ci = 1;
00401038 mov byte ptr [ebp-1],1
8: unsigned int ni = 0XFFFFFFFE+ci;
0040103C movsx eax,byte ptr [ebp-1]
00401040 sub eax,2
00401043 mov dword ptr [ebp-8],eax
9: return ni;
00401046 mov eax,dword ptr [ebp-8]
10:
11: }
00401049 pop edi
0040104A pop esi
0040104B pop ebx
0040104C mov esp,ebp
0040104E pop ebp
0040104F ret
如何使用函数的返回值
函数的返回值存放在了eax 里面 ,如果要使用返回值 需要在调用函数的前面用一个变量承接
15: int res = Fun();
00401078 call @ILT+5(Fun) (0040100a)
0040107D mov dword ptr [ebp-4],eax
可以看到eax给了调用承接变量的
代码演示:编写一个函数,能够得到任意5个整数的和,并返回结果.
// C语言正向.cpp : Defines the entry point for the console application.
//
#include "stdafx.h"
int Fun(int a,int b,int c,int d,int e)
{
return a+b+c+d+e;
}
int main(int argc, char* argv[])
{
int res = Fun(1,2,3,4,5);
getchar();
}
6、关系运算符:“==”、“!=”、“>=”、“<=”、“>”、“<”
7、逻辑运算符:&& || !
void Fun(int x,int y,int z)
{
if(x>1 && y>1 && z>1)
{
printf("OK");
}
else
{
printf("Error");
}
}
void Fun(int x,int y,int z)
{
if(x>1 || y>1 || z>1)
{
printf("OK");
}
else
{
printf("Error");
}
}
与或非就不用多说了,都懂,无非就是内存表现形式
if(x>1 && y>1 && z>1)
0040DB58 cmp dword ptr [ebp+8],1
0040DB5C jle Fun+39h (0040db79)
0040DB5E cmp dword ptr [ebp+0Ch],1
0040DB62 jle Fun+39h (0040db79)
0040DB64 cmp dword ptr [ebp+10h],1
0040DB68 jle Fun+39h (0040db79)
8: {
9: printf("OK");
0040DB6A push offset string "OK" (0042212c)
0040DB6F call printf (0040de40)
0040DB74 add esp,4
10: }
11: else
0040DB77 jmp Fun+46h (0040db86)
12: {
13: printf("Error");
0040DB79 push offset string "Error" (00422168)
0040DB7E call printf (0040de40)
0040DB83 add esp,4
14: }
15: }
有点像if的汇编代码 小于等于就跳转到error 不小于就往下判断执行 printf("OK");
if(x>1 || y>1 || z>1)
0040DB58 cmp dword ptr [ebp+8],1
0040DB5C jg Fun+2Ah (0040db6a)
0040DB5E cmp dword ptr [ebp+0Ch],1
0040DB62 jg Fun+2Ah (0040db6a)
0040DB64 cmp dword ptr [ebp+10h],1
0040DB68 jle Fun+39h (0040db79)
8: {
或只要有一个满足就会跳转OK 且有一个不满足就跳转error
8、单目运算符
void Fun(int x,int y,int z)
{
int i = 10;
int k = ++i;
printf("%d-%d\n",i,k);
}
9: int k = ++i;
0040DB5F mov eax,dword ptr [ebp-4]
0040DB62 add eax,1
0040DB65 mov dword ptr [ebp-4],eax
0040DB68 mov ecx,dword ptr [ebp-4]
0040DB6B mov dword ptr [ebp-8],ecx
i先加一然后赋值给k
void Fun(int x,int y,int z)
{
int i = 10;
int k = i++;
printf("%d-%d\n",i,k);
}
9: int k = i++;
0040DB5F mov eax,dword ptr [ebp-4]
0040DB62 mov dword ptr [ebp-8],eax
0040DB65 mov ecx,dword ptr [ebp-4]
0040DB68 add ecx,1
0040DB6B mov dword ptr [ebp-4],ecx
i先赋值给k然后自加1 总结++i先自增再参与运算 i++先参与运算再自加
9、三目运算符|
void Fun(int x,int y)
{
int r = x>y?x:y;
printf("%d\n",r);
}
7: int r = x>y?x:y;
0040DB58 mov eax,dword ptr [ebp+8]
0040DB5B cmp eax,dword ptr [ebp+0Ch]
0040DB5E jle Fun+28h (0040db68)
0040DB60 mov ecx,dword ptr [ebp+8]
0040DB63 mov dword ptr [ebp-48h],ecx
0040DB66 jmp Fun+2Eh (0040db6e)
0040DB68 mov edx,dword ptr [ebp+0Ch]
0040DB6B mov dword ptr [ebp-48h],edx
0040DB6E mov eax,dword ptr [ebp-48h]
0040DB71 mov dword ptr [ebp-4],eax
如果x>y成立 r=x 否则r = y 很像if else 的汇编语句
10、语句的执行条件
测试指令 TEST
格式: TEST OPRD1,OPRD2
功能: 其中OPRD1、OPRD2的含义同AND指令一样,也是对两个操作数进行按位的'与'运算, ---- 唯一不同之处是不将'与'的结果送目的操作数,即本指令对两个操作数 的内容均不进行修改,仅是在逻辑与操作后,对标志位重新置位.

汇编代码
7: if(1)
0040DED8 mov eax,1
0040DEDD test eax,eax
0040DEDF je Fun+2Eh (0040deee)
8: {
9: printf("1\n");
0040DEE1 push offset string "1\n" (00422fbc)
0040DEE6 call printf (0040de40)
0040DEEB add esp,4
10: }
11: if(2)
0040DEEE mov eax,2
0040DEF3 test eax,eax
0040DEF5 je Fun+44h (0040df04)
12: {
13: printf("2\n");
0040DEF7 push offset string "2\n" (00422fb8)
0040DEFC call printf (0040de40)
0040DF01 add esp,4
14: }
15: if(-1)
0040DF04 or eax,0FFFFFFFFh
0040DF07 test eax,eax
0040DF09 je Fun+58h (0040df18)
16: {
17: printf("3\n");
0040DF0B push offset string "3\n" (00422fb4)
0040DF10 call printf (0040de40)
0040DF15 add esp,4
18: }
19: if(0)
0040DF18 xor eax,eax
0040DF1A test eax,eax
0040DF1C je Fun+6Bh (0040df2b)
20: {
21: printf("4\n");
0040DF1E push offset string "4\n" (00422fb0)
0040DF23 call printf (0040de40)
0040DF28 add esp,4
22: }
23: if(x>y)
0040DF2B mov eax,dword ptr [ebp+8]
0040DF2E cmp eax,dword ptr [ebp+0Ch]
0040DF31 jle Fun+80h (0040df40)
24: {
25: printf("5\n");
0040DF33 push offset string "5\n" (00422fac)
0040DF38 call printf (0040de40)
0040DF3D add esp,4
26: }
27: if(x=2)
0040DF40 mov dword ptr [ebp+8],2
0040DF47 cmp dword ptr [ebp+8],0
0040DF4B je Fun+9Ah (0040df5a)
28: {
29: printf("6\n");
0040DF4D push offset string "OK" (0042212c)
0040DF52 call printf (0040de40)
0040DF57 add esp,4
30: }
31: if(x==2)
0040DF5A cmp dword ptr [ebp+8],2
0040DF5E jne Fun+0ADh (0040df6d)
32: {
33: printf("7\n");
0040DF60 push offset string "7\n" (00422fa8)
0040DF65 call printf (0040de40)
0040DF6A add esp,4
34: }
35: }
test eax,eax 按位与 == 1,会影响ZF标志位,用来判断结果是否为0 所以ZF = 0 所以je并不跳转 区别cmp
再看if(-1)
0040DF04 or eax,0FFFFFFFFh
0040DF07 test eax,eax
0040DF09 je Fun+58h (0040df18)
OR是按位或影响操作数 任何比特位或上 1 → 结果一定等于 1 也就是eax = 0xffffffff
结果非0 所以ZF = 0不跳继续执行 所以je并不跳转
再看if(0)
0040DF18 xor eax,eax
0040DF1A test eax,eax
0040DF1C je Fun+6Bh (0040df2b)
xor异或 不相同为1 相同为0 对寄存器清零 并且影响第一个操作数 eax = 0 test 0,0
eax = 1 ZF = 1 所以je不跳
if(x=2)
27: if(x=2)
0040DF40 mov dword ptr [ebp+8],2
0040DF47 cmp dword ptr [ebp+8],0
0040DF4B je Fun+9Ah (0040df5a)
28: {
cmp 2,0 ZF = 0 不跳
也就是1 2 3 6 7 最后一个也是不为0 不跳转