日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

c和汇编---函数

發布時間:2023/12/1 编程问答 28 豆豆
生活随笔 收集整理的這篇文章主要介紹了 c和汇编---函数 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

環境:VC++

作用:
函數是完成特定任務的獨立程序代碼單元

1、創建和使用函數

  • 函數原型:聲明函數是什么類型,指明函數的返回值和函數接收的參數類型,函數和變量一樣,有多種類型,任何程序在使用函數之前都要聲明該函數的類型
  • 函數調用:表明在此處執行函數,執行到函數調用的語句時,程序會找到該函數的定義并執行其中的內容,執行完返回調用函數繼續執行下一行
  • 函數定義:詳細說明函數要干啥
#include "stdio.h"int add(int a,int b); //函數原型int main(void) {int a=1,b=1,sum=0;sum=add(a,b); //函數調用printf("sum=%d\n",sum);return 0; }//函數定義 int add(int a,int b) {return a+b; }

我們看看反匯編:
函數原型:

我們可以看出,函數原型這里沒有生成機器碼,這個是給編譯器看得,告訴編譯器這個函數的返回值和函數接收的參數類型,并在別處查看該函數類型,機器碼是給CPU執行的,所以CPU執行到這里,不會干任何事情
函數調用:

8: sum=add(a,b); //函數調用 0040104D 8B 45 F8 mov eax,dword ptr [ebp-8] 00401050 50 push eax 00401051 8B 4D FC mov ecx,dword ptr [ebp-4] 00401054 51 push ecx 00401055 E8 AB FF FF FF call @ILT+0(add) (00401005) 0040105A 83 C4 08 add esp,8 0040105D 89 45 F4 mov dword ptr [ebp-0Ch],eax

函數調用之前,我們可以看到會先把參數存放到棧里面,也就是a,b的值,然后到00401005地址執行,這個地址有個jmp語句,會跳轉到函數的定義出執行

函數定義:

13: //函數定義 14: int add(int a,int b) 15: { 004010A0 55 push ebp 004010A1 8B EC mov ebp,esp 004010A3 83 EC 40 sub esp,40h 004010A6 53 push ebx 004010A7 56 push esi 004010A8 57 push edi 004010A9 8D 7D C0 lea edi,[ebp-40h] 004010AC B9 10 00 00 00 mov ecx,10h 004010B1 B8 CC CC CC CC mov eax,0CCCCCCCCh 004010B6 F3 AB rep stos dword ptr [edi] 16: return a+b; 004010B8 8B 45 08 mov eax,dword ptr [ebp+8] 004010BB 03 45 0C add eax,dword ptr [ebp+0Ch] 17: } 004010BE 5F pop edi 004010BF 5E pop esi 004010C0 5B pop ebx 004010C1 8B E5 mov esp,ebp 004010C3 5D pop ebp 004010C4 C3 ret

從上面的程序我們可以看出,函數定義會先把esp存放到棧里面,然后把esp的值給ebp,接著開辟一個40h的棧,然后把ebx、 esi、 edi存放到棧里面,接著在一些連續地址存放0CCCCCCCCh,把這些做好后,再執行函數定義里的語句。

16: return a+b; 004010B8 8B 45 08 mov eax,dword ptr [ebp+8] 004010BB 03 45 0C add eax,dword ptr [ebp+0Ch]

我們看看函數的最后

004010BE 5F pop edi 004010BF 5E pop esi 004010C0 5B pop ebx 004010C1 8B E5 mov esp,ebp 004010C3 5D pop ebp 004010C4 C3 ret

函數的最后,把函數開始存放這些寄存器的內容又給了他們,ebp的值給esp,ebp恢復函數之前的ebp,接著返回。與函數調用的作用是一樣的

函數的作用只完成特定任務,其他什么都沒變,從函數調用到函數定義,最后返回,看起來是只對了a和b的值進行了操作,其他啥都沒變

總結:
函數原型沒有生成機器碼,告訴編譯器我的參數是那些和返回值是那些,函數調用會把參數先壓入棧,接著執行call到一個地址執行,這個地址有一個jmp命令,會到函數定義出執行,函數定義會先把一些寄存器先壓入棧,然后給一些內存賦值,在最后又會把這些寄存器給彈出,恢復成原值,執行ret命令,返回調用函數繼續執行下一行。

2、傳值和傳址的區別

首先我們要認識幾個小知識

  • &運算符:取變量的存儲地址
  • *間接運算符:取存儲在指針指向地址上的值,也可以用來聲明指針
  • 聲明指針變量:類型 * 變量名,聲明指針變量必須指定指針所指向變量的類型,因為不同變量類型占用不同的存儲空間
#include "stdio.h"int add1(int a,int b); //函數原型 int add2(int *a,int *b); //函數原型 int main(void) {int a=1,b=1,sum1,sum2;sum1=add1(a,b); //函數調用printf("sum1=%d\n",sum1);sum2=add2(&a,&b); //函數調用printf("sum2=%d\n",sum2);return 0; }//函數定義 int add1(int a,int b) {return a+b; }int add2(int *a,int *b) {return *a+*b; }

傳值:

8: sum1=add1(a,b); //函數調用 0040D786 8B 45 F8 mov eax,dword ptr [ebp-8] 0040D789 50 push eax 0040D78A 8B 4D FC mov ecx,dword ptr [ebp-4] 0040D78D 51 push ecx 0040D78E E8 7C 38 FF FF call @ILT+10(add) (0040100f) 0040D793 83 C4 08 add esp,8 0040D796 89 45 F4 mov dword ptr [ebp-0Ch],eax

值傳給eax寄存器,然后入棧

傳址:

10: sum2=add2(&a,&b); //函數調用 0040D7AA 8D 45 F8 lea eax,[ebp-8] 0040D7AD 50 push eax 0040D7AE 8D 4D FC lea ecx,[ebp-4] 0040D7B1 51 push ecx 0040D7B2 E8 5D 38 FF FF call @ILT+15(add2) (00401014) 0040D7B7 83 C4 08 add esp,8 0040D7BA 89 45 F0 mov dword ptr [ebp-10h],eax

把地址傳給eax,然后入棧
我們知道傳值不可以修改變量的值,而傳址卻可以,從匯編角度看,我們可以更加的清晰明白,傳值只是將值傳過去了,函數調用是去函數定義處執行,不知道變量在哪里,所以沒辦法修改,傳地址到函數定義時就知道變量的地址在哪里了,所以能修改變量得內容

總結

以上是生活随笔為你收集整理的c和汇编---函数的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。