C語言程序在存儲及運行兩種狀態下的內存分區
圖片基於黑馬訓練營day1中圖片修改
C語言程序編譯過程
階段 | 作用 | 中間文件 | 生成命令 |
---|---|---|---|
源代碼 | C/C++源代碼文件或者頭文件 | .c/.cpp/.h | / |
預處理 | 處理宏定義與條件編譯,包含文件展開(#include),去除註釋 | .i/.ii | gcc -E |
編譯 | 語法檢查並生成彙編代碼,這一階段所有函數的地址已經確定並存入文件,函數調用轉化爲call指令,call指令後面的地址爲指向某條jmp指令,這條jmp後面跟的地址纔是原來真正函數的地址 | .s | gcc -S |
彙編 | 彙編代碼轉機器碼 | .o | gcc -c |
鏈接 | 鏈接運行所需的庫 | .o | gcc |
運行 | 最終的可執行程序 | .exe |
鏈接
鏈接方式 | 文件 | 方法 | 特點 |
---|---|---|---|
靜態鏈接 | .h+.cpp——>.lib (鏈接時用,稱之爲靜態庫,包含有實際執行代碼、符號表等等) | #include<testlib.h> #pragma comment(lib,“testlib.lib”) |
在生成可執行文件後靜態庫可以丟棄,因爲所需函數代碼已經在編譯時置於目標程序中,這也導致目標程序運行時佔用內存較大 |
動態鏈接 | .h+.cpp ——> .lib(鏈接時用,稱之爲導入庫,只包含了地址符號表)+.dll(運行時用,稱之爲動態庫,包含實際的執行代碼) | 方法1:LoadLibarary() GetProcAddress() FreeLibrary() 方法2:#include<winsock2.h> #pragma comment(lib,“Ws2_32”) |
可執行程序在運行過程中根據需要從動態庫中導入相關函數,爲此動態庫不可丟棄,同時可執行程序運行時佔用內存相對較少 |
函數調用約定
函數的調用約定指的是函數傳遞參數的方式和棧協同工作的技術細節。不同的函數調用約定對應着不同函數參數的入棧順序及堆棧平衡的維護者。
下表展示了幾種函數調用約定之間的差異:
C | SysCall | StdCall | BASIC | FORTRAN | PASCAL | |
---|---|---|---|---|---|---|
參數入棧順序 | 右→左 | 右→左 | 右→左 | 左→右 | 左→右 | 左→右 |
堆棧平衡的維護者 | 母函數 | 子函數 | 子函數 | 子函數 | 子函數 | 子函數 |
Visual C++支持的三種函數調用約定如下:
調用約定 | 參數入棧順序 | 恢復棧平衡的位置 |
---|---|---|
__cdecl | 右→左 | 母函數 |
__fastcall | 右→左 | 子函數 |
__stdcall | 右→左 | 子函數 |
Visual C++默認使用__stdcall調用方式