1. 程式人生 > 實用技巧 >彙編角度看C語言資料型別

彙編角度看C語言資料型別



1、什麼是裸函式?

  void __declspec(naked) Function()
  {...}

  上面的函式呼叫時,為什麼會出錯?

  void __declspec(naked) Function()
  {

    __asm ret
  }

2、無引數無返回值的函式框架


  void __declspec(naked) Function()
  {
    __asm
  {
    push ebp
    mov ebp,esp
    sub esp,0x40
    push ebx
    push esi
    push edi
    lea edi,dword ptr ds:[ebp-0x40]
    mov eax,0xCCCCCCCC
    mov ecx,0x10
    rep stosd

    pop edi
    pop esi
    pop ebx
    mov esp,ebp
    pop ebp

    ret
  }
  }


3、有引數有返回值的函式框架

  int __declspec(naked) Function(int x,int y)
  {
    __asm
    {
      push ebp
      mov ebp,esp
      sub esp,0x40
      push ebx
      push esi
      push edi
      lea edi,dword ptr ds:[ebp-0x40]
      mov eax,0xCCCCCCCC
      mov ecx,0x10
      rep stosd

      mov eax,dword ptr ds:[ebp+8]
      add eax,dword ptr ds:[ebp+0xC]

      pop edi
      pop esi
      pop ebx
      mov esp,ebp
      pop ebp

      ret
    }
  }


4、帶區域性變數的函式框架


  int __declspec(naked) Function(int x,int y)
  {
    __asm
    {
      push ebp
      mov ebp,esp
      sub esp,0x40
      push ebx
      push esi
      push edi
      lea edi,dword ptr ds:[ebp-0x40]
      mov eax,0xCCCCCCCC
      mov ecx,0x10
      rep stosd
      mov dword ptr ds:[ebp-4],2
      mov dword ptr ds:[ebp-8],3

      mov eax,dword ptr ds:[ebp+8]
      add eax,dword ptr ds:[ebp+0xC]

      pop edi
      pop esi
      pop ebx
      mov esp,ebp
      pop ebp

      ret
    }
  }


常見的幾種呼叫約定:

  呼叫約定 引數壓棧順序 平衡堆疊

  __cdecl 從右至左入棧 呼叫者清理棧

  __stdcall 從右至左入棧 自身清理堆疊

  __fastcall ECX/EDX傳送前兩個 自身清理堆疊
  剩下:從右至左入棧


1int __cdecl Plus(int a, int b)    
{    
    return a+b;    
}    

    push 2    
    push 
1 call @ILT+15(Plus) (00401014) add esp,8


2int __stdcall Plus(int a, int b)    
    {    
        return a+b;    
    }        

    push 2    
    push 1    
    call @ILT+10(Plus) (0040100f)    


  函式內部:

  ret 8

3int __fastcall Plus(int
a, int b) { return a+b; } mov edx,2 mov ecx,1 call @ILT+0(Plus) (00401005)


函式內部:

ret 

4int __fastcall Plus4(int a, int b,int c,int d)    
{    
return a+b+c+d;    
}    

push 4    
push 3    
mov edx,2    
mov ecx,1    
call @ILT+5(Plus) (0040100a)    


函式內部:

ret 8



1、C語言中的資料型別:

整數型別

基本型別
浮點型別


陣列型別

C語言資料型別 構造型別 結構體型別

共用體(聯合)型別

指標型別


空型別(void)


2、學習資料型別的三個要素:

1、儲存資料的寬度

2、儲存資料的格式

3、作用範圍(作用域)


3、整數型別:char short int long

在反彙編中觀察,體驗資料寬度的概念
char 8BIT 1位元組 char cTemp = 0xFF;
short sTemp = 0xFF;
short 16BIT 2位元組 int nTemp = 0xFF;

int 32BIT 4位元組 char cTemp = 0x12345678;
short sTemp = 0x12345678;
long 32BIT 4位元組 int nTemp = 0x12345678;


整數型別分為有符號(signed)和無符號(unsigned)兩種:

char cTest = 0xFF; unsigned char i = 0xFF;
unsigned char j = 0x1;
unsigned char cuTest = 0xFF;
if(i>j)
printf("%d\n%d",cTest,cuTest); {
printf("i>j");
總結: }
else
1、在記憶體中儲存的方式完全一樣 {
printf("i<j");
2、在做運算的時候需要注意 }



4、浮點型別:float double

float和double在儲存方式上都是遵從IEEE的規範的

float的儲存方式如下圖所示:

31 30 22 0
1 8 23

符號位 指數部分 尾數部分

8.4
double的儲存方式如下圖所示:

63 62 51 0
1 11 52

符號位 指數部分 尾數部分

將一個float型轉化為記憶體儲存格式的步驟為:

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位。

舉例說明:

8.25轉成浮點儲存

整數部分8轉成2進位制

8/2 = 4 0
4/2 = 2 0
2/2 = 1 0
1/2 = 0 1
3 - 1 = 2 10
小數部分0.25轉成二進位制:
Float = 6
0.25*2 = 0.5 0
0.5*2 = 1.0 1 1 8 23
0 10000010 00001000000000000000000
8.25用二進位制表示可表示為1000.01

1000.01 = 1.00001 *2的3次方 小數點向左移動3位 指數為3


1 10000010 00001000000000000000000 0100 0001 0000 0100 0000 0000 0000 0000 = 41040000
41040000

-8.25轉成浮點數是多少呢? 1100 0001 0000 0100 0000 0000 0000 0000 = C1040000
C1040000

0.25轉成浮點儲存

整數部分0 0

0.25 * 2 = 0.5 0
0.5*2 = 1.0 1

0.01 = 1.0 * 2的2次方 右移動 注意:向右移動2個位 指數為-2


0 01111101 00000000000000000000000



5、英文字元儲存

char x = 'A';

char y = 65;
A 10101
參見ASCII編碼 B 11111
C 11110
, 101011

6、中文字元儲存


char* x = "啊";

char* y = "北";

參見GB2312-80編碼

這種編碼儲存在的問題