青青草原综合久久大伊人导航_色综合久久天天综合_日日噜噜夜夜狠狠久久丁香五月_热久久这里只有精品

posts - 319, comments - 22, trackbacks - 0, articles - 11
  C++博客 :: 首頁 :: 新隨筆 :: 聯系 :: 聚合  :: 管理

函數調用約定和堆棧(轉載)

Posted on 2011-05-12 22:00 RTY 閱讀(483) 評論(0)  編輯 收藏 引用 所屬分類: 編程常識轉載隨筆

1 什么是堆棧

編譯器一般使用堆棧實現函數調用。堆棧是存儲器的一個區域,嵌入式環境有時需要程序員自己定義一個數組作為堆棧。Windows為每個線程自動維護一個堆棧,堆棧的大小可以設置。編譯器使用堆棧來堆放每個函數的參數、局部變量等信息。

函數調用經常是嵌套的,在同一時刻,堆棧中會有多個函數的信息,每個函數占用一個連續的區域。一個函數占用的區域被稱作幀(frame)。

編譯器從高地址開始使用堆棧。 假設我們定義一個數組a[1024]作為堆棧空間,一開始棧頂指針指向a[1023]。如果棧里有兩個函數a和b,且a調用了b,棧頂指針會指向函數b的幀。如果函數b返回。棧頂指針就指向函數a的幀。如果在棧里放了太多東西造成溢出,破壞的是a[0]上面的東西。

在多線程(任務)環境,CPU的堆棧指針指向的存儲器區域就是當前使用的堆棧。切換線程的一個重要工作,就是將堆棧指針設為當前線程的堆棧棧頂地址。

不同CPU,不同編譯器的堆棧布局、函數調用方法都可能不同,但堆棧的基本概念是一樣的。

2 函數調用約定

函數調用約定包括傳遞參數的順序,誰負責清理參數占用的堆棧等,例如 :

 參數傳遞順序誰負責清理參數占用的堆棧
__pascal從左到右調用者
__stdcall從右到左被調函數
__cdecl從右到左調用者

調用函數的代碼和被調函數必須采用相同的函數的調用約定,程序才能正常運行。在Windows上,__cdecl是C/C++程序的缺省函數調用約定。

在有的cpu上,編譯器會用寄存器傳遞參數,函數使用的堆棧由被調函數分配和釋放。這種調用約定在行為上和__cdecl有一個共同點:實參和形參數目不符不會導致堆棧錯誤。

不過,即使用寄存器傳遞參數,編譯器在進入函數時,還是會將寄存器里的參數存入堆棧指定位置。參數和局部變量一樣應該在堆棧中有一席之地。參數可以被理解為由調用函數指定初值的局部變量。

3 例子:__cdecl和__stdcall

不同的CPU,不同的編譯器,堆棧的布局可能是不同的。本文以x86,VC++的編譯器為例。

VC++編譯器的已經不再支持__pascal, __fortran, __syscall等函數調用約定。目前只支持__cdecl和__stdcall。

采用__cdecl或__stdcall調用方式的程序,在剛進入子函數時,堆棧內容是一樣的。esp指向的棧頂是返回地址。這是被call指令壓入堆棧的。下面是參數,左邊參數在上,右邊參數在下(先入棧)。

如前表所示,__cdecl和__stdcall的區別是:__cdecl是調用者清理參數占用的堆棧,__stdcall是被調函數清理參數占用的堆棧。

由于__stdcall的被調函數在編譯時就必須知道傳入參數的準確數目(被調函數要清理堆棧),所以不能支持變參數函數,例如printf。而且如果調用者使用了不正確的參數數目,會導致堆棧錯誤。

通過查看匯編代碼,__cdecl函數調用在call語句后會有一個堆棧調整語句,例如:

    a = 0x1234;
    b = 0x5678;
    c = add(a, b);

對應x86匯編:

    mov dword ptr [ebp-4],1234h
    mov dword ptr [ebp-8],5678h
    mov eax,dword ptr [ebp-8]
    push eax
    mov ecx,dword ptr [ebp-4]
    push ecx
    call 0040100a
    add esp,8
    mov dword ptr [ebp-0Ch],eax


__stdcall的函數調用則不需要調整堆棧:

    call 00401005
    mov dword ptr [ebp-0Ch],eax

函數

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

產生以下匯編代碼(Debug版本):

    push ebp
    mov ebp,esp
    sub esp,40h
    push ebx
    push esi
    push edi
    lea edi,[ebp-40h]
    mov ecx,10h
    mov eax,0CCCCCCCCh
    rep stos dword ptr [edi]
    mov eax,dword ptr [ebp+8]
    add eax,dword ptr [ebp+0Ch]
    pop edi
    pop esi
    pop ebx
    mov esp,ebp
    pop ebp
    ret // 跳轉到esp所指地址,并將esp+4,使esp指向進入函數時的第一個參數

再查看__stdcall函數的實現,會發現與__cdecl函數只有最后一行不同:

    ret 8 // 執行ret并清理參數占用的堆棧

對于調試版本,VC++編譯器在“直接調用地址”時會增加檢查esp的代碼,例如:

    ta = (TAdd)add; // TAdd定義:typedef int (__cdecl *TAdd)(int a, int b);
    c = ta(a, b);

產生以下匯編代碼:

    mov [ebp-10h],0040100a
    mov esi,esp
    mov ecx,dword ptr [ebp-8]
    push ecx
    mov edx,dword ptr [ebp-4]
    push edx
    call dword ptr [ebp-10h]
    add esp,8
    cmp esi,esp
    call __chkesp (004011e0)
    mov dword ptr [ebp-0Ch],eax

__chkesp 代碼如下。如果esp不等于函數調用前保存的值,就會轉到錯誤處理代碼。

    004011E0 jne __chkesp+3 (004011e3)
    004011E2 ret
    004011E3 ;錯誤處理代碼

__chkesp的錯誤處理會彈出對話框,報告函數調用造成esp值不正確。 Release版本的匯編代碼要簡潔得多。也不會增加 __chkesp。如果發生esp錯誤,程序會繼續運行,直到“遇到問題需要關閉”。

3 補充說明

函數調用約定只是“調用函數的代碼”和被調用函數之間的關系。

假設函數A是__stdcall,函數B調用函數A。你必須通過函數聲明告訴編譯器,函數A是__stdcall。編譯器自然會產生正確的調用代碼。

如果函數A是__stdcall。但在引用函數A的地方,你卻告訴編譯器,函數A是__cdecl方式,編譯器產生__cdecl方式的代碼,與函數A的調用約定不一致,就會發生錯誤。

以delphi調用VC函數為例,delphi的函數缺省采用__pascal約定,VC的函數缺省采用__cdecl約定。我們一般將VC的函數設為__stdcall,例如:

    int __stdcall add(int a, int b);

在delphi中將這個函數也聲明為__stdcall,就可以調用了:

    function add(a: Integer; b: Integer): Integer;
    stdcall; external 'a.dll';

因為考慮到可能被其它語言的程序調用,不少API采用__stdcall的調用約定。

青青草原综合久久大伊人导航_色综合久久天天综合_日日噜噜夜夜狠狠久久丁香五月_热久久这里只有精品
  • <ins id="pjuwb"></ins>
    <blockquote id="pjuwb"><pre id="pjuwb"></pre></blockquote>
    <noscript id="pjuwb"></noscript>
          <sup id="pjuwb"><pre id="pjuwb"></pre></sup>
            <dd id="pjuwb"></dd>
            <abbr id="pjuwb"></abbr>
            欧美日韩a区| 黄色成人免费观看| 国产精品99久久久久久人| 亚洲精品一区二区三区蜜桃久| 六十路精品视频| 亚洲精品在线一区二区| 亚洲精品视频中文字幕| 欧美日韩福利| 欧美伊人久久久久久久久影院| 亚洲综合精品| 在线精品高清中文字幕| 亚洲高清在线观看| 欧美裸体一区二区三区| 亚洲欧美成人一区二区三区| 欧美一级视频免费在线观看| 狠狠色丁香婷婷综合| 亚洲国产精品123| 国产精品国色综合久久| 久久综合精品国产一区二区三区| 农夫在线精品视频免费观看| 亚洲一区二区三区欧美| 久久精品视频在线观看| 99re热这里只有精品免费视频| 亚洲一区二区三区视频| 亚洲高清av| 亚洲伊人一本大道中文字幕| 在线不卡中文字幕| 一区二区三区欧美在线观看| 樱花yy私人影院亚洲| 亚洲美女在线观看| 精品粉嫩aⅴ一区二区三区四区| 91久久国产综合久久91精品网站| 国产精品乱码久久久久久| 欧美国产一区视频在线观看 | 久久久久国产精品厨房| 免费试看一区| 久久精品国产一区二区三区免费看| 老鸭窝91久久精品色噜噜导演| 亚洲一区二区三区免费在线观看| 美女久久网站| 欧美中文字幕在线视频| 欧美日韩国产大片| 久久尤物视频| 国产伦精品一区二区三区视频黑人| 欧美99久久| 国产亚洲精品bt天堂精选| 日韩午夜免费视频| 最新日韩在线| 久久久久久久久久久成人| 欧美一区1区三区3区公司| 欧美激情视频在线播放| 欧美成人精品在线| 黑人一区二区| 欧美在线free| 久久精品30| 国产精品入口66mio| aaa亚洲精品一二三区| 最新日韩av| 欧美成人福利视频| 欧美高清在线视频观看不卡| 黄色av一区| 久久久女女女女999久久| 久久噜噜噜精品国产亚洲综合| 国产精品一区二区三区观看| 一区二区三区欧美日韩| 中文一区字幕| 国产精品久久久久国产a级| 日韩午夜中文字幕| 亚洲永久在线| 国产乱码精品一区二区三区忘忧草 | 免费成人av资源网| 黄色一区二区三区四区| 久久久av毛片精品| 欧美jizzhd精品欧美巨大免费| 狠狠爱综合网| 久久亚洲一区二区| 亚洲福利av| 亚洲校园激情| 国产九色精品成人porny| 性色av一区二区三区红粉影视| 久久精品国产第一区二区三区最新章节| 国产精品永久免费在线| 欧美一区在线看| 免费观看不卡av| 亚洲精选一区二区| 欧美视频在线视频| 亚洲欧美国产va在线影院| 久久久精品国产99久久精品芒果| 伊大人香蕉综合8在线视| 欧美成人一区二区在线| 日韩亚洲欧美一区| 欧美在线免费视频| 亚洲大片精品永久免费| 欧美日本亚洲视频| 亚洲欧美一区二区精品久久久| 毛片精品免费在线观看| 亚洲乱码一区二区| 国产精品永久免费观看| 久久日韩精品| 亚洲一区二区三区成人在线视频精品 | 国语自产精品视频在线看| 久久综合网色—综合色88| 日韩视频国产视频| 久久婷婷国产综合国色天香| av成人动漫| 国产一区二区三区的电影| 欧美高潮视频| 欧美影视一区| 亚洲免费观看在线视频| 美女精品在线| 亚洲欧美在线免费观看| 亚洲第一色在线| 国产精品亚洲网站| 欧美女同视频| 久久久人人人| 性久久久久久久久| 亚洲美女色禁图| 欧美成人午夜激情在线| 久久国产精品久久国产精品| 亚洲精品永久免费| 激情五月***国产精品| 国产精品美女视频网站| 欧美国产日韩一二三区| 久久精品99无色码中文字幕| 亚洲视频电影在线| 亚洲精品一区二区三区四区高清| 久久久噜噜噜久久中文字幕色伊伊| 亚洲视频在线一区观看| 亚洲精品视频在线观看免费| 国产综合自拍| 国产精品一二三四| 欧美亚州一区二区三区| 欧美激情一区二区三区四区 | 亚洲日本va午夜在线电影| 久久亚洲风情| 久久久久久香蕉网| 欧美专区一区二区三区| 亚洲欧美国产毛片在线| 亚洲午夜精品| 亚洲网友自拍| 亚洲午夜视频在线| 亚洲色图综合久久| 亚洲小说区图片区| 亚洲一区二区在线观看视频| 中日韩美女免费视频网址在线观看 | 久久综合九色综合网站| 久久国产视频网| 欧美专区在线| 久久久夜夜夜| 蜜臀av国产精品久久久久| 久久久精品2019中文字幕神马| 午夜综合激情| 久久久.com| 裸体丰满少妇做受久久99精品 | 99视频一区| 亚洲午夜精品网| 亚洲女人天堂av| 久久成人18免费网站| 久久综合九色综合网站| 欧美黄色大片网站| 欧美日韩在线直播| 国产欧美日韩一级| 在线成人激情| 中文日韩欧美| 久久精品国语| 欧美高清在线视频观看不卡| 亚洲日本理论电影| 午夜精品久久久久久久99樱桃 | 久久米奇亚洲| 欧美另类女人| 国产人妖伪娘一区91| 曰韩精品一区二区| 中文一区二区| 久久久久国色av免费看影院| 亚洲丰满少妇videoshd| 中文精品99久久国产香蕉| 久久精品一区四区| 欧美日韩国产一区| 国产在线视频不卡二| 亚洲日产国产精品| 欧美一区日韩一区| 亚洲高清二区| 欧美一区二区视频在线| 欧美成人性生活| 国产在线日韩| 亚洲一区二区三区免费视频| 久久综合色播五月| 一本久道久久久| 免费黄网站欧美| 国产视频亚洲精品| 一区二区三区免费在线观看| 久久久国际精品| 野花国产精品入口| 蜜臀久久久99精品久久久久久| 国产精品私人影院| 一本到高清视频免费精品| 久久免费黄色| 亚洲一区日韩| 国产精品99一区二区| 亚洲精品麻豆|