C语言05_正向基础

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 不跳转

上一篇 C语言04_IF语句逆向分析下
因特叁

因特叁管理员

动物世界

本月创作热力图

目录