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

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

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

Posted on 2011-05-12 22:00 RTY 閱讀(480) 評論(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>
            噜噜噜久久亚洲精品国产品小说| 亚洲欧美日韩综合一区| 亚洲视频图片小说| 99国内精品久久| 日韩视频在线播放| 亚洲视频在线观看免费| 一区二区三区日韩精品视频| 亚洲深夜福利在线| 亚洲欧美经典视频| 久久久99免费视频| 亚洲大片av| 亚洲区在线播放| 亚洲精品小视频在线观看| 中文亚洲视频在线| 久久久五月天| 欧美日韩二区三区| 国产视频一区在线观看一区免费 | 亚洲综合色激情五月| 亚洲一区二区三区成人在线视频精品| 亚洲欧美成人综合| 你懂的网址国产 欧美| 国产精品极品美女粉嫩高清在线 | 欧美精品在线观看播放| 国产精品久久久久久福利一牛影视| 国产一区二区三区不卡在线观看| 亚洲人成人一区二区三区| 性欧美大战久久久久久久免费观看| 久久一综合视频| 亚洲视频在线一区| 欧美精品大片| 国内精品久久久久国产盗摄免费观看完整版 | 欧美有码视频| 欧美视频免费在线| 亚洲国产精品嫩草影院| 性欧美暴力猛交另类hd| 最新高清无码专区| 久久精品夜夜夜夜久久| 国产精品视频yy9099| 亚洲日本一区二区| 久久婷婷丁香| 亚洲在线中文字幕| 欧美日韩在线大尺度| 亚洲欧洲精品一区二区三区不卡| 久久久久久国产精品mv| 一区二区三区欧美视频| 久久影视三级福利片| 国语自产精品视频在线看8查询8| 亚洲在线观看视频网站| 亚洲午夜精品一区二区| 亚洲欧美一区二区原创| 亚洲三级影片| 欧美成人蜜桃| 亚洲黄色在线观看| 欧美成人精品一区二区| 久久综合成人精品亚洲另类欧美| 一区二区电影免费在线观看| 欧美精品在线一区| 国产综合欧美| 久久精品国内一区二区三区| 亚洲欧美日韩天堂| 国产午夜精品在线| 久久精品五月| 久久久精品日韩欧美| 国产自产2019最新不卡| 久久久免费av| 另类av一区二区| 日韩一区二区精品视频| 亚洲美女精品成人在线视频| 欧美日韩美女| 亚洲一本视频| 亚洲免费视频观看| 黑人巨大精品欧美黑白配亚洲| 久久综合一区二区三区| 美女精品一区| 在线视频亚洲| 亚洲欧美国产日韩中文字幕| 一区视频在线| 亚洲国产日韩欧美在线99| 欧美精品少妇一区二区三区| 亚洲一区二区欧美| 欧美一区二区日韩| 亚洲第一黄网| 亚洲精选在线观看| 国产精品亚洲综合久久| 久久视频在线视频| 欧美激情一区在线| 欧美一区二区三区在线免费观看 | 欧美国产日韩亚洲一区| 欧美黄色日本| 欧美在线国产| 欧美福利一区二区| 久久经典综合| 欧美国产日韩精品| 久久9热精品视频| 嫩草国产精品入口| 欧美一区二区精美| 欧美xart系列在线观看| 欧美在线视频免费观看| 欧美日本亚洲| 久热精品在线视频| 国产精品毛片高清在线完整版| 能在线观看的日韩av| 欧美深夜福利| 欧美1区视频| 国产精品一区2区| 91久久精品一区| 国内精品久久久久久久果冻传媒 | 可以免费看不卡的av网站| 欧美日韩国产首页| 免费中文字幕日韩欧美| 国产精品美女主播| 日韩网站免费观看| 最新中文字幕一区二区三区| 亚洲男女毛片无遮挡| 欧美日韩福利视频| 亚洲高清电影| 黄色国产精品| 亚洲欧美一区二区精品久久久| 日韩视频在线观看免费| 久久深夜福利免费观看| 久久人人九九| 国产欧美一区二区三区国产幕精品| 亚洲日本欧美| 日韩网站在线看片你懂的| 麻豆成人在线播放| 免费观看成人| 精久久久久久久久久久| 欧美一区二区久久久| 欧美在线免费播放| 国产精品揄拍500视频| 午夜精品视频在线观看| 亚洲一区二区在线免费观看| 欧美精品一区三区在线观看| 亚洲高清av在线| 亚洲精品一区二区在线观看| 欧美国产一区二区在线观看| 欧美黑人一区二区三区| 亚洲国产精品悠悠久久琪琪| 巨乳诱惑日韩免费av| 欧美激情一区二区三区在线视频 | 99精品视频网| 欧美成人午夜免费视在线看片| 欧美国产先锋| 日韩亚洲一区二区| 欧美日韩中文精品| 亚洲私拍自拍| 久久人人97超碰精品888| 在线看片日韩| 欧美激情一区二区三级高清视频| 亚洲美女视频在线观看| 午夜精品久久久久久久久久久久久| 国产精品亚洲片夜色在线| 午夜日韩福利| 免费在线国产精品| 一区二区不卡在线视频 午夜欧美不卡在 | 亚洲午夜视频| 国产精品家庭影院| 欧美在线看片| 欧美搞黄网站| 亚洲一区二区在线| 黄色一区二区三区| 欧美精品一区二区三区一线天视频| 99av国产精品欲麻豆| 久久九九国产精品怡红院| 在线日韩成人| 国产精品www994| 久久成人免费视频| 亚洲欧洲在线免费| 欧美在线视频不卡| 亚洲日本va午夜在线影院| 国产精品成人在线观看| 久久精品国产99国产精品| 亚洲电影免费观看高清完整版在线观看 | 久久精品中文| 亚洲激情av在线| 国产精品日日摸夜夜摸av| 蜜臀久久99精品久久久画质超高清| 一本一道久久综合狠狠老精东影业 | 亚洲人体1000| 国产模特精品视频久久久久| 鲁大师影院一区二区三区| 亚洲视频一区在线| 欧美激情一区三区| 久久精品日韩欧美| 在线一区视频| 亚洲国产精品成人精品| 国产日韩精品一区| 欧美视频一区在线| 免费在线观看精品| 久久成人国产| 一区二区三区四区精品| 亚洲国产精品成人va在线观看| 欧美在线视频播放| 亚洲一区日韩在线| 一本大道av伊人久久综合| 在线观看日韩精品| 国产原创一区二区| 国产欧美一区二区三区久久| 欧美视频在线观看一区二区| 欧美激情第五页|