導航:首頁 > 源碼編譯 > c編譯器幀指針教學

c編譯器幀指針教學

發布時間:2023-04-01 22:06:32

1. C語言函數調用時棧中內存的變化

這是執行到g裡面畝州的i=5這條液握語句之時的堆棧迅埋蔽

2. cmake編譯後的程序不自動釋放

您好,路徑問題。Cmake中往往需要填寫路徑,但是許多人填寫桐早並的路徑極其不規范,比如路徑「C:\Program Files (x86)\新建文件夾 (2)」,這個路徑會有三個問題:第一,有中文,雖然現在很多軟體都支持中文,但是出現中文會增加失敗的風險;第二,空格,這是很多人忽略的問題,還有睜隱其他的奇怪的符號,至於用哪些符號比較保險,請參考《Unix高級環境編程》第一章;第三, 路徑局跡分隔符,這是Cmake中非常常見的問題,Cmake是開源軟體,開源軟體請盡量按照Unix/linux的標准來使用,比如Linux中的路徑為「/home/hello」,都是以「/」做為分隔符,而Windows中以「\」作為分隔符。如果使用了「\」, 那麼路徑「C:\Demo」會將 「\D」當成轉義字元!

3. c語言數組在內存中是怎麼分配的

C語言使用的內存是虛擬內存。按照功能的不同在C語言中又將虛擬內存為分三類:棧區、堆區、靜態數據區,不管是單一變數還是數組,其內存分配都是這樣分的。

在棧區、靜態數據區、堆區會有編譯器負責分配、操作系統負責管理,程序員可以在堆區使用malloc()來動態分配堆內存的問題。

(3)c編譯器幀指針教學擴展閱讀

內存的分配和釋放注意事項:

1、malloc和free是庫函數,不是系統調用

2、malloc實際分配的內存可能會比請求的多---有些編譯器分配時是以4位元組為單元的

3、不能依賴於不同平台的下的malloc

4、當請求的動態內存無法滿足時malloc返回的是NULL

5、當free的參數為NULL時,函數直接返回

4. c語言中命令幀是什麼意思

是一個運算符,其左側的變數,要求必須是一個結構休或者類的指針,如果是c那就必須是結構體的指針,而右側必須是結構體中的成員。注意左側一定要是指針才正確。記住:->運算符除了用於結構體的指針訪問其成員之外,沒有其他的用處。比如
struct
c{int
a;};
void
main()
{stuct
c
cc;
struct
c
*p=&cc;
p->a=1;
printf("%d",p->a);
strcut
c
s;
s->a;
//錯誤,左側一定要是結構類型的指針;
p->b;
//錯誤,右側一定要是指針指向的結構體中的成員
int
*i;
i->a;
//錯誤,左側一定要是結構體的指針,其他類型的指針都是錯誤的。
}

5. linux 用g++編譯c++代碼的問題

*

運行 gcc/egcs
*

gcc/egcs 的主要選項
*

gdb
*

gdb 的常用命令
*

gdb 使用範例
*

其他程序/庫工具 (ar, objmp, nm, size, strings, strip, ...)
* 創建和使用靜態庫
* 創建和使用共享庫
* 使用高級共享庫特性

1.7.1 運行 gcc/egcs

Linux 中最重要的軟體開發工具是 GCC。GCC 是 GNU 的 C 和 C++ 編譯器。實際上,GCC 能夠編譯三種語言:C、C++ 和 Object C(C 語言的一種面向對象擴展)。利用 gcc 命令可同時編譯並連接 C 和 C++ 源程序。

#DEMO#: hello.c

如果你有兩個或少數幾個 C 源文件,也可以方便地利用 GCC 編譯、連接並生成可執行文件。例如,假設你有兩個源文件 main.c 和 factorial.c 兩個源文件,現在要編譯生成一個計算階乘的程序。

-----------------------
清單 factorial.c
-----------------------
#include <stdio.h>
#include <stdlib.h>

int factorial (int n)
{
if (n <= 1)
return 1;

else
return factorial (n - 1) * n;
}
-----------------------

-----------------------
清單 main.c
-----------------------
#include <stdio.h>
#include <stdlib.h>

int factorial (int n);

int main (int argc, char **argv)
{
int n;

if (argc < 2) {
printf ("Usage: %s n\n", argv [0]);
return -1;
}
else {
n = atoi (argv[1]);
printf ("Factorial of %d is %d.\n", n, factorial (n));
}

return 0;
}
-----------------------

利用如下的命令可編譯生成可執行文件,並執行程序:
$ gcc -o factorial main.c factorial.c
$ ./factorial 5
Factorial of 5 is 120.

GCC 可同時用來編譯 C 程序和 C++ 程序。一般來說,C 編譯器通過源文件的後綴名來判斷是 C 程序還是 C++ 程序。在 Linux 中,C 源文件的後綴名為 .c,而 C++ 源文件的後綴名為 .C 或 .cpp。

但是,gcc 命令只能編譯 C++ 源文件,而不能自動和 C++ 程序使用的庫連接。因此,通常使用 g++ 命令來完成 C++ 程序的編譯和連接,該程序會自動調用 gcc 實現編譯。假設我們有一個如下的 C++ 源文件(hello.C):

#include <iostream.h>

void main (void)
{
cout << "Hello, world!" << endl;
}

則可以如下調用 g++ 命令編譯、連接並生成可執行文件:

$ g++ -o hello hello.C
$ ./hello
Hello, world!

1.7.2 gcc/egcs 的主要選項

表 1-3 gcc 命令的常用選項
選項 解釋
-ansi 只支持 ANSI 標準的 C 語法。這一選項將禁止 GNU C 的某些特色,
例如 asm 或 typeof 關鍵詞。
-c 只編譯並生成目標文件。
-DMACRO 以字元串「1」定義 MACRO 宏。
-DMACRO=DEFN 以字元串「DEFN」定義 MACRO 宏。
-E 只運行 C 預編譯器。
-g 生成調試信息。GNU 調試器可利用該信息。
-IDIRECTORY 指定額外的頭文件搜索路徑DIRECTORY。
-LDIRECTORY 指定額外的函數庫搜索路徑DIRECTORY。
-lLIBRARY 連接時搜索指定的函數庫LIBRARY。
-m486 針對 486 進行代碼優化。
-o FILE 生成指定的輸出文件。用在生成可執行文件時。
-O0 不進行優化處理。
-O 或 -O1 優化生成代碼。
-O2 進一步優化。
-O3 比 -O2 更進一步優化,包括 inline 函數。
-shared 生成共享目標文件。通常用在建立共享庫時。
-static 禁止使用共享連接。
-UMACRO 取消對 MACRO 宏的定義。
-w 不生成任何警告信息。
-Wall 生成所有警告信息。

#DEMO#

MiniGUI 的編譯選項
1.7.3 gdb

GNU 的調試器稱為 gdb,該程序是一個互動式工具,工作在字元模式。在 X Window 系統中,
有一個 gdb 的前端圖形工具,稱為 xxgdb。gdb 是功能強大的調試程序,可完成如下的調試
任務:
* 設置斷點;
* 監視程序變數的值;
* 程序的單步執行;
* 修改變數的值。
在可以使用 gdb 調試程序之前,必須使用 -g 選項編譯源文件。可在 makefile 中如下定義
CFLAGS 變數:
CFLAGS = -g
運行 gdb 調試程序時通常使用如下的命令:
gdb progname

在 gdb 提示符處鍵入help,將列出命令的分類,主要的分類有:
* aliases:命令別名
* breakpoints:斷點定義;
* data:數據查看;
* files:指定並查看文件;
* internals:維護命令;
* running:程序執行;
* stack:調用棧查看;
* statu:狀態查看;
* tracepoints:跟蹤程序執行。
鍵入 help 後跟命令的分類名,可獲得該類命令的詳細清單。

#DENO#
1.7.4 gdb 的常用命令

表 1-4 常用的 gdb 命令
命令 解釋
break NUM 在指定的行上設置斷點。
bt 顯示所有的調用棧幀。該命令可用來顯示函數的調用順序。
clear 刪除設置在特定源文件、特定行上的斷點。其用法為:clear FILENAME:NUM。
continue 繼續執行正在調試的程序。該命令用在程序由於處理信號或斷點而
導致停止運行時。
display EXPR 每次程序停止後顯示表達式的值。表達式由程序定義的變數組成。
file FILE 裝載指定的可執行文件進行調試。
help NAME 顯示指定命令的幫助信息。
info break 顯示當前斷點清單,包括到達斷點處的次數等。
info files 顯示被調試文件的詳細信息。
info func 顯示所有的函數名稱。
info local 顯示當函數中的局部變數信息。
info prog 顯示被調試程序的執行狀態。
info var 顯示所有的全局和靜態變數名稱。
kill 終止正被調試的程序。
list 顯示源代碼段。
make 在不退出 gdb 的情況下運行 make 工具。
next 在不單步執行進入其他函數的情況下,向前執行一行源代碼。
print EXPR 顯示表達式 EXPR 的值。

1.7.5 gdb 使用範例

-----------------
清單 一個有錯誤的 C 源程序 bugging.c
-----------------
#include <stdio.h>
#include <stdlib.h>

static char buff [256];
static char* string;
int main ()
{

printf ("Please input a string: ");
gets (string);

printf ("\nYour string is: %s\n", string);
}
-----------------
上面這個程序非常簡單,其目的是接受用戶的輸入,然後將用戶的輸入列印出來。該程序使用了
一個未經過初始化的字元串地址 string,因此,編譯並運行之後,將出現 Segment Fault 錯誤:
$ gcc -o test -g test.c
$ ./test
Please input a string: asfd
Segmentation fault (core mped)
為了查找該程序中出現的問題,我們利用 gdb,並按如下的步驟進行:
1.運行 gdb bugging 命令,裝入 bugging 可執行文件;
2.執行裝入的 bugging 命令;
3.使用 where 命令查看程序出錯的地方;
4.利用 list 命令查看調用 gets 函數附近的代碼;
5.唯一能夠導致 gets 函數出錯的因素就是變數 string。用 print 命令查看 string 的值;
6.在 gdb 中,我們可以直接修改變數的值,只要將 string 取一個合法的指針值就可以了,為
此,我們在第 11 行處設置斷點;
7.程序重新運行到第 11 行處停止,這時,我們可以用 set variable 命令修改 string 的取值;
8.然後繼續運行,將看到正確的程序運行結果。

#DEMO#

1.7.6 其他程序/庫工具

strip:

nm:

size:

string:

1.7.7 創建和使用靜態庫

創建一個靜態庫是相當簡單的。通常使用 ar 程序把一些目標文件(.o)組合在一起,成為一個單獨的庫,然後運行 ranlib,以給庫加入一些索引信息。

1.7.8 創建和使用共享庫

特殊的編譯和連接選項

-D_REENTRANT 使得預處理器符號 _REENTRANT 被定義,這個符號激活一些宏特性。
-fPIC 選項產生位置獨立的代碼。由於庫是在運行的時候被調入,因此這個
選項是必需的,因為在編譯的時候,裝入內存的地址還不知道。如果
不使用這個選項,庫文件可能不會正確運行。
-shared 選項告訴編譯器產生共享庫代碼。
-Wl,-soname -Wl 告訴編譯器將後面的參數傳遞到連接器。而 -soname 指定了
共享庫的 soname。

# 可以把庫文件拷貝到 /etc/ld.so.conf 中列舉出的任何目錄中,並以
root 身份運行 ldconfig;或者
# 運行 export LD_LIBRARY_PATH='pwd',它把當前路徑加到庫搜索路徑中去。

1.7.9 使用高級共享庫特性

1. ldd 工具

ldd 用來顯示執行文件需要哪些共享庫, 共享庫裝載管理器在哪裡找到了需要的共享庫.

2. soname

共享庫的一個非常重要的,也是非常難的概念是 soname——簡寫共享目標名(short for shared object name)。這是一個為共享庫(.so)文件而內嵌在控制數據中的名字。如前面提到的,每一個程序都有一個需要使用的庫的清單。這個清單的內容是一系列庫的 soname,如同 ldd 顯示的那樣,共享庫裝載器必須找到這個清單。

soname 的關鍵功能是它提供了兼容性的標准。當要升級系統中的一個庫時,並且新庫的 soname 和老的庫的 soname 一樣,用舊庫連接生成的程序,使用新的庫依然能正常運行。這個特性使得在 Linux 下,升級使用共享庫的程序和定位錯誤變得十分容易。

在 Linux 中,應用程序通過使用 soname,來指定所希望庫的版本。庫作者也可以通過保留或者改變 soname 來聲明,哪些版本是相互兼容的,這使得程序員擺脫了共享庫版本沖突問題的困擾。

查看/usr/local/lib 目錄,分析 MiniGUI 的共享庫文件之間的關系

3. 共享庫裝載器

當程序被調用的時候,Linux 共享庫裝載器(也被稱為動態連接器)也自動被調用。它的作用是保證程序所需要的所有適當版本的庫都被調入內存。共享庫裝載器名字是 ld.so 或者是 ld-linux.so,這取決於 Linux libc 的版本,它必須使用一點外部交互,才能完成自己的工作。然而它接受在環境變數和配置文件中的配置信息。

文件 /etc/ld.so.conf 定義了標准系統庫的路徑。共享庫裝載器把它作為搜索路徑。為了改變這個設置,必須以 root 身份運行 ldconfig 工具。這將更新 /etc/ls.so.cache 文件,這個文件其實是裝載器內部使用的文件之一。

可以使用許多環境變數控制共享庫裝載器的操作(表1-4+)。

表 1-4+ 共享庫裝載器環境變數
變數 含義
LD_AOUT_LIBRARY_PATH 除了不使用 a.out 二進制格式外,與 LD_LIBRARY_PATH 相同。
LD_AOUT_PRELOAD 除了不使用 a.out 二進制格式外,與 LD_PRELOAD 相同。
LD_KEEPDIR 只適用於 a.out 庫;忽略由它們指定的目錄。
LD_LIBRARY_PATH 將其他目錄加入庫搜索路徑。它的內容應該是由冒號
分隔的目錄列表,與可執行文件的 PATH 變數具有相同的格式。
如果調用設置用戶 ID 或者進程 ID 的程序,該變數被忽略。
LD_NOWARN 只適用於 a.out 庫;當改變版本號是,發出警告信息。
LD_PRELOAD 首先裝入用戶定義的庫,使得它們有機會覆蓋或者重新定義標准庫。
使用空格分開多個入口。對於設置用戶 ID 或者進程 ID 的程序,
只有被標記過的庫才被首先裝入。在 /etc/ld.so.perload 中指定
了全局版本號,該文件不遵守這個限制。

4. 使用 dlopen

另外一個強大的庫函數是 dlopen()。該函數將打開一個新庫,並把它裝入內存。該函數主要用來載入庫中的符號,這些符號在編譯的時候是不知道的。比如 Apache Web 伺服器利用這個函數在運行過程中載入模塊,這為它提供了額外的能力。一個配置文件控制了載入模塊的過程。這種機制使得在系統中添加或者刪除一個模塊時,都不需要重新編譯了。

可以在自己的程序中使用 dlopen()。dlopen() 在 dlfcn.h 中定義,並在 dl 庫中實現。它需要兩個參數:一個文件名和一個標志。文件名可以是我們學習過的庫中的 soname。標志指明是否立刻計算庫的依賴性。如果設置為 RTLD_NOW 的話,則立刻計算;如果設置的是 RTLD_LAZY,則在需要的時候才計算。另外,可以指定 RTLD_GLOBAL,它使得那些在以後才載入的庫可以獲得其中的符號。

當庫被裝入後,可以把 dlopen() 返回的句柄作為給 dlsym() 的第一個參數,以獲得符號在庫中的地址。使用這個地址,就可以獲得庫中特定函數的指針,並且調用裝載庫中的相應函數。

6. C語言如何給指針分配內存

1, 找到VS的cl.exe所在目錄,把這目錄復制下來:
我的VS2008的CL.EXE目錄是在E:\Program Files\Microsoft Visual Studio 9.0\VC\bin,
VS2010可以類似的找到..

在'我的電腦'上點右鍵,
選右鍵菜單'屬性'->'高級'->'環境變數',
在彈出的環境變數設置框里找"PATH"這個變數, (在鄭伏用戶變數或系統變數里都可以)
然後在"PATH"的值後面,用分號分隔,
把將才找到的路徑帆殲串復制進去,選確定.

2, 重新運行CMD開啟新的命令窗.

3, 輸入cl回車檢查PATH路徑是否生效.

//以上步聚是設置環境變數,只需設一次以後就好用了.以後每次要命令行下編譯C++程序,就從下面第4步開始.

4, 輸入vcvars32 ,運行cl.exe同一路徑下的vcvars32.bat,設置其它環境變數.

5, 寫一個helloworld程序,保存成hello.cpp, cl hello.cpp回車試試編譯正常不. 如果成功,則生成hello.exe文件.

//-----------------------------------------------------------

C/C++ 編譯器選項

-優化-

/O1 最小化空間 /Op[-] 改善浮點數一致性
/O2 最大化速度 /Os 優選代碼空間
/Oa 假設沒有別名 /Ot 優選代碼速度
/Ob<n> 內聯展開(默認 n=0) /Ow 假設交叉函數別名
/Od 禁用優化(默認值) /Ox 最大化選項。(/Ogityb2 /Gs)
/Og 啟用全局優化 /Oy[-] 啟用框架指針省略
/Oi 啟用內部函數

-代碼生成-

/G3 為 80386 進行優化 /Gh 啟用 _penter 函數調用
/G4 為 80486 進行優化 /GH 啟用 _pexit 函數調用
/G5 為 Pentium 進行優化 /GR[-] 啟用 C++ RTTI
/G6 對 PPro、P-II、P-III 進行優化 /GX[-] 啟用 C++ EH (與 /EHsc 相同)
/G7 對 Pentium 4 或 Athlon 進行喊轎攜優化 /EHs 啟用 C++ EH (沒有 SEH 異常)
/GB 為混合模型進行優化(默認) /EHa 啟用 C++ EH(w/ SEH 異常)
/Gd __cdecl 調用約定 /EHc extern "C" 默認為 nothrow
/Gr __fastcall 調用約定 /GT 生成纖維安全 TLS 訪問
/Gz __stdcall 調用約定 /Gm[-] 啟用最小重新生成
/GA 為 Windows 應用程序進行優化 /GL[-] 啟用鏈接時代碼生成
/Gf 啟用字元串池 /QIfdiv[-] 啟用 Pentium FDIV 修復
/GF 啟用只讀字元串池 /QI0f[-] 啟用 Pentium 0x0f 修復
/Gy 分隔鏈接器函數 /QIfist[-] 使用 FIST 而不是 ftol()
/GZ 啟用堆棧檢查(/RTCs) /RTC1 啟用快速檢查(/RTCsu)
/Ge 對所有函數強制堆棧檢查 /RTCc 轉換為較小的類型檢查
/Gs[num] 控制堆棧檢查調用 /RTCs 堆棧幀運行時檢查
/GS 啟用安全檢查 /RTCu 未初始化的本地用法檢查
/clr[:noAssembly] 為公共語言運行庫編譯
noAssembly - 不產生程序集
/arch:<SSE|SSE2> CPU 結構的最低要求,以下內容之一:
SSE - 啟用支持 SSE 的 CPU 可用的指令
SSE2 - 啟用支持 SSE2 的 CPU 可用的指令

-輸出文件-

/Fa[file] 命名程序集列表文件 /Fo<file> 命名對象文件
/FA[sc] 配置程序集列表 /Fp<file> 命名預編譯頭文件
/Fd[file] 命名 .PDB 文件 /Fr[file] 命名源瀏覽器文件
/Fe<file> 命名可執行文件 /FR[file] 命名擴展 .SBR 文件
/Fm[file] 命名映射文件

-預處理器-

/AI<dir> 添加到程序集搜索路徑 /Fx 將插入的代碼合並到文件
/FU<file> 強制使用程序集/模塊 /FI<file> 命名強制包含文件
/C 不抽出注釋 /U<name> 移除預定義宏
/D<name><text> 定義宏 /u 移除所有預定義宏
/E 預處理到 stdout /I<dir> 添加到包含搜索路徑
/EP 預處理到 stdout,沒有 #line /X 忽略「標准位置」
/P 預處理到文件

-語言-

/Zi 啟用調試信息 /Ze 啟用擴展(默認)
/ZI 啟用「編輯並繼續」調試信息 /Zl 省略 .OBJ 中的默認庫名
/Z7 啟用舊式調試信息 /Zg 生成函數原型
/Zd 僅有行號調試信息 /Zs 只進行語法檢查
/Zp[n] 在 n 位元組邊界上包裝結構 /vd 禁用/啟用 vtordisp
/Za 禁用擴展(暗指 /Op) /vm<x> 指向成員的指針類型
/Zc:arg1[,arg2] C++ 語言一致性,這里的參數可以是:
forScope - 對范圍規則強制使用標准 C++
wchar_t - wchar_t 是本機類型,不是 typedef

- 雜項 -

@<file> 選項響應文件 /wo<n> 發出一次警告 n
/?, /help 列印此幫助消息 /w<l><n> 為 n 設置警告等級 1-4
/c 只編譯,不鏈接 /W<n> 設置警告等級(默認 n=1)
/H<num> 最大外部名稱長度 /Wall 啟用所有警告
/J 默認 char 類型是 unsigned /Wp64 啟用 64 位埠定位警告
/nologo 取消顯示版權消息 /WX 將警告視為錯誤
/showIncludes 顯示包含文件名 /WL 啟用單行診斷
/Tc<source file> 將文件編譯為 .c /Yc[file] 創建 .PCH 文件
/Tp<source file> 將文件編譯為 .cpp /Yd 將調試信息放在每個 .OBJ 中
/TC 將所有文件編譯為 .c /Yl[sym] 為調試庫插入 .PCH 引用
/TP 將所有文件編譯為 .cpp /Yu[file] 使用 .PCH 文件
/V<string> 設置版本字元串 /YX[file] 自動 .PCH
/w 禁用所有警告 /Y- 禁用所有 PCH 選項
/wd<n> 禁用警告 n /Zm<n> 最大內存分配(默認為 %)
/we<n> 將警告 n 視為錯誤

-鏈接-

/MD 與 MSVCRT.LIB 鏈接 /MDd 與 MSVCRTD.LIB 調試庫鏈接
/ML 與 LIBC.LIB 鏈接 /MLd 與 LIBCD.LIB 調試庫鏈接
/MT 與 LIBCMT.LIB 鏈接 /MTd 與 LIBCMTD.LIB 調試庫鏈接
/LD 創建 .DLL /F<num> 設置堆棧大小
/LDd 創建 .DLL 調試庫 /link [鏈接器選項和庫]

如果對您有幫助,請記得採納為滿意答案,謝謝!祝您生活愉快!

7. 函數調用的實際實現

EBP
EBP是所謂的幀指針,指向當前活動記錄的上方(上一個活動記錄的最下方)
ESP
ESP是所謂的棧指針,指向當前活動記錄的最下枝飢者方(下一個將要插入的活動記錄的最上方)
這兩個指針的值規定了當猛薯前活動記錄的位置 將函數參數壓棧:mov eax,dword ptr [n] ;(n為參數變元)
push eax 函數調用將執行如下操作:
⒈將幀指針壓入棧中:push ebp
⒉使得幀指針等於棧指針:肢塌mov ebp,esp
⒊使棧指針自減,自減得到的內存地址應當能夠(足夠)用來存儲被調用函數的本地狀態:sub esp,0CCh
注意:0CCh為0xCC,隨著具體函數的不同而不同。 push ebx ;保存ebx寄存器的值
push esi ;保存esi寄存器的值
push edi ;保存edi寄存器的值 lea edi,[ebp-0CCh] ;0cch是當前活動記錄的大小。
EDI是目的變址寄存器。 00411417 pop edi
00411418 pop esi
pop ebx 當函數返回時,編譯器和硬體將執行如下操作:
⒈使棧指針等於幀指針: mov esp,ebp
⒉從棧中將舊的幀指針彈出: pop ebp
⒊返回:ret ;void function(int n);{push ebp
mov ebp,esp
sub esp,0CCh
push ebx
push esi
push edi
lea edi,[ebp-0CCh]
mov ecx,33h
mov eax,0CCCCCCCCh
rep stos dword ptr es:[edi]
;char a=1;
mov byte ptr [a],1
;if(n==0)return;
cmp dword ptr [n],0
jne function+2Ah (4113CAh)
jmp function+77h (411417h)
;printf(%d (0x%08x) ,n,&n);
mov esi,esp
lea eax,[n]
push eax
mov ecx,dword ptr [n]
push ecx
push offset string %d (0x%08x) (415750h)
call dword ptr [__imp__printf (4182B8h)]
add esp,0Ch
cmp esi,esp
call @ILT+305(__RTC_CheckEsp) (411136h)
;function(n-1);
mov eax,dword ptr [n]
sub eax,1
push eax
call function (411041h)
add esp,4
;printf(----%d (0x%08x) ,n,&n);
mov esi,esp
lea eax,[n]
push eax
mov ecx,dword ptr [n]
push ecx
push offset string ----%d (0x%08x) (41573Ch)
call dword ptr [__imp__printf (4182B8h)]
add esp,0Ch
cmp esi,esp
call @ILT+305(__RTC_CheckEsp) (411136h);}
pop edi
pop esi
pop ebx
add esp,0CCh
cmp ebp,esp
call @ILT+305(__RTC_CheckEsp) (411136h)
mov esp,ebp
pop ebp
ret 117: bR = t1(p);
匯編代碼如下:
00401FB8 mov ecx,dword ptr [ebp-8] ;將參數放入ecx寄存器
00401FBB push ecx ;參數入棧
00401FBC call @ILT+10(t1) (0040100f) ;函數調用,下一行地址00401FC1入棧
00401FC1 add esp,4 ;函數返回,堆棧指針加4,復原為00401FB8時的值
00401FC4 mov dword ptr [ebp-10h],eax ;從eax中取出高級語言中的函數返回值,放入bR變數中
其中t1函數如下:
125: BOOL t1(void* p)
126: {
00402030 push ebp ;ebp入棧
00402031 mov ebp,esp ;ebp指向此時堆棧的棧頂
00402033 sub esp,44h ;esp減少一個值,空出一段存儲區
00402036 push ebx ;將三個寄存器的值入棧,以便在函數中使用它
00402037 push esi ;
00402038 push edi ;
00402039 lea edi,[ebp-44h] ;
0040203C mov ecx,11h ;
00402041 mov eax,0CCCCCCCCh ;
00402046 rep stos dword ptr [edi] ;
127: int* q = (int*)p; ;
00402048 mov eax,dword ptr [ebp+8] ;ebp+8指向函數輸入參數的最低位地址;
;如果是ebp+4則指向函數返回地址00401FC1的最低位,值為C1
0040204B mov dword ptr [ebp-4],eax ;
128: return 0;
0040204E xor eax,eax ;返回值放入eax寄存器中
129: }
00402050 pop edi ;三個寄存器出棧
00402051 pop esi ;
00402052 pop ebx ;
00402053 mov esp,ebp ;esp復原
00402055 pop ebp ;ebp出棧,它的值也復原了
00402056 ret ;返回到此時棧頂存儲的代碼地址:00401FC1
;故而如果不幸被修改了返回地址,程序就會出現意外
以上匯編代碼由VC++6.0編譯得到。
堆棧在EBP入棧後的情況:
低位 高位
↓ ↓
內存地址 堆棧
┆ ┆
0012F600├────────┤← edi = 0012F600
│ │
0012F604├─┄┄┄ ┄─┤
│ │
│ │
┆ 44h的空間 ┆
┆ ┆
│ │
│ │
0012F640├─┄┄┄┄─┤
│ │
0012F644├────────┤← ebp被賦值後指向該單元,此時ebp=0012F644
│AC F6 12 00 │ebp賦值為esp之前的值
0012F648├────────┤
│C1 1F 40 00 │返回地址
0012F64C├────────┤← ebp + 8
│A0 F6 12 00 │函數實參p的值;
0012F650├────────┤
│ │
├────────┤
┆ ┆
註:存儲器存儲空間堆棧按從高到低的排列,左邊標注的地址是其右下方存儲單元的最低位地址。如0012F644指向0012F6AC的AC位元組,AC在棧頂。圖中存儲器中的內容按從低到高位書寫,「AC F6 12 00」= 0x0012F6AC
說明
(1)一個c程序由一個或多個程序模塊組成,每一個程序模塊作為一個源程序文件。對較大的程序,一般不希望把所有內容全放在一個文件中,而是將它們分別放在若干個源文件中,由若干個源程序文件組成一個c程序。這樣便於分別編寫和編譯,調高調試效率。一個源程序文件可以為多個c程序公用。
(2)一個源程序文件由一個或多個函數以及其他有關內容(如指令,數據聲明與定義等)組成。一個源程序文件是一個編譯單位,子啊程序編譯時是以源程序文件為單位進行編譯的,而不是以函數為單位進行編譯的。
(3)c程序的執行是從main函數開始的,如果在main函數中調用其他函數,在調用後流程返回main函數,在main函數中結束整個程序的進行。
(4)所有函數都是平行的,即在定義函數時是分別進行的,是互相獨立的。一個函數並不從屬於另一個函數,即函數不能嵌套定義。函數間可以互相調用,但不能調用main函數。main函數是被操作系統調用的。
(5)從用戶的角度來看函數分為兩種
a:庫函數,它是由系統提供的,用戶不必自己定義,可直接使用它們。應該說明,不同的c語言編譯系統提供的庫函數的數量和功能會有一些不同,當然許多基本的函數是共同的。
b:用戶自己定義的函數。它是以解決用戶專門需求的函數。
(6)從函數的形式來看,函數分為兩類。
a:無參函數。無參函數可以帶回或不帶回函數值,但一般不帶回函數值較多。
b:有參函數。在調用函數時,主調函數在調用被調函數時,通過參數向被調函數傳遞數據。一般情況下,執行調用函數時會得到一個函數值,供主調函數使用。

8. c語言怎麼用微軟的軟體編譯(過程)

DOS下面是沒有cl的,cl是Windows下命令行方式的編譯工具,IDE也是調用它編譯的。直接cl.exe /help就能看到幫助,貼給你吧 VS2010的(2012的可以交叉編譯ARM架構的目標代碼)

用於 80x86 的 Microsoft (R) 32 位 C/C++ 優化編譯器 16.00.40219.01 版
版權所有(C) Microsoft Corporation。保留所有權利。

C/C++ 編譯器選項

-優化-

/O1 最小化空間 /O2 最大化速度
/Ob<n> 內聯擴展(默認 n=0) /Od 禁用優化(默認)
/Og 啟用全局優化 /Oi[-] 啟用內部函數
/Os 優選代碼空間 /Ot 優選代碼速度
/Ox 最大化優化 /Oy[-] 啟用幀指針省略

-代碼生成-

/GF 啟用只讀字元串池 /Gm[-] 啟用最小重新生成
/Gy[-] 分隔鏈接器函數 /GS[-] 啟用安全檢查
/GR[-] 啟用 C++ RTTI /GX[-] 啟用 C++ EH (與 /EHsc 相同)
/EHs 啟用 C++ EH (沒有 SEH 異常) /EHa 啟用 C++ EH (w/ SEH 異常)
/EHc 外部「C」默認為 nothrow
/fp:<except[-]|fast|precise|strict> 選擇浮點模式:
except[-] - 在生成代碼時考慮浮點異常
fast -「fast」浮點模式;結果可預測性比較低
precise -「precise」浮點模式;結果可預測
strict -「strict」 浮點模式(意味著 /fp:except)
即使使用 /fp:except,/Qfast_transcendentals 也生成內聯內部 FP
/GL[-] 啟用鏈接時代碼生成 /GA 為 Windows 應用程序進行優化
/Ge 對所有函數強制堆棧檢查 /Gs[num] 控制堆棧檢查調用
/Gh 啟用 _penter 函數調用 /GH 啟用 _pexit 函數調用
/GT 生成纖程安全 TLS 訪問 /RTC1 啟用快速檢查(/RTCsu)
/RTCc 轉換為較小的類型檢查 /RTCs 堆棧幀運行時檢查
/RTCu 未初始化的局部用法檢查
/clr[:option] 為公共語言運行時編譯,其中 option 是:
pure - 生成只包含 IL 的輸出文件(沒有本機可執行代碼)
safe - 生成只包含 IL 的可驗證輸出文件
oldSyntax - 接受 Visual C++ 2002/2003 的託管擴展語法
initialAppDomain - 啟用 Visual C++ 2002 的初始 AppDomain 行為
noAssembly - 不產生程序集 /Gd __cdecl 調用約定
/Gr __fastcall 調用約定 /Gz __stdcall 調用約定
/GZ 啟用堆棧檢查(/RTCs) /QIfist[-] 使用 FIST 而不是 ftol()
/hotpatch 確保可熱修補映像的函數填充
/arch:<SSE|SSE2|AVX> CPU 架構的最低要求,以下選項之一:
SSE - 啟用支持 SSE 的 CPU 可用的指令
SSE2 - 啟用支持 SSE2 的 CPU 可用的指令
AVX - 支持使用 Intel(R) 高級矢量擴展指令
/Qimprecise_fwaits 僅在「try」邊界而不是「try」內部生成 FWAITs
/Qsafe_fp_loads 生成安全 FP 負載

-輸出文件-

/Fa[file] 命名程序集列表文件 /FA[scu] 配置程序集列表
/Fd[file] 命名 .PDB 文件 /Fe<file> 命名可執行文件
/Fm[file] 命名映射文件 /Fo<file> 命名對象文件
/Fp<file> 命名預編譯頭文件 /Fr[file] 命名源瀏覽器文件
/FR[file] 命名擴展 .SBR 文件 /Fi[file] 命名預處理的文件
/doc[file] 處理 XML 文檔注釋,並可選擇命名 .xdc 文件

-預處理器-

/AI<dir> 添加到程序集搜索路徑 /FU<file> 強制使用程序集/模塊
/C 不抽出注釋 /D<name>{=|#}<text> 定義宏
/E 預處理到 stdout /EP 預處理到 stdout,無行號
/P 預處理到文件 /Fx 將插入的代碼合並到文件中
/FI<file> 命名強制包含文件 /U<name> 移除預定義的宏
/u 移除所有預定義的宏 /I<dir> 添加到包含搜索路徑
/X 忽略「標准位置」

-語言-

/Zi 啟用調試信息 /Z7 啟用舊式調試信息
/Zp[n] 在 n 位元組邊界上包裝結構 /Za 禁用擴展
/Ze 啟用擴展(默認) /Zl 省略 .OBJ 中的默認庫名
/Zg 生成函數原型 /Zs 只進行語法檢查
/vd{0|1|2} 禁用/啟用 vtordisp /vm<x> 指向成員的指針類型
/Zc:arg1[,arg2] C++ 語言合規性,這里的參數可以是:
forScope[-] - 對范圍規則強制使用標准 C++
wchar_t[-] - wchar_t 是本機類型,不是 typedef
auto[-] - 對 auto 強制使用新的標准 C++ 含義
trigraphs[-] - 啟用三元祖(默認為關閉)
/ZI 啟用「編輯並繼續」調試信息 /openmp 啟用 OpenMP 2.0 語言擴展

- 雜項 -

@<file> 選項響應文件 /?, /help 列印此幫助消息
/bigobj 生成擴展的對象格式 /c 只編譯,不鏈接
/errorReport:option 將內部編譯器錯誤報告給 Microsoft
none - 不發送報告 prompt - 提示立即發送報告
queue - 在下一次管理員登錄時,提示發送報告(默認)
send - 自動發送報告 /FC 診斷中使用完整路徑名
/H<num> 最大外部名稱長度 /J 默認 char 類型是 unsigned
/MP[n] 最多使用「n」個進程進行編譯 /nologo 取消顯示版權信息
/showIncludes 顯示包含文件名 /Tc<source file> 將文件編譯為 .c
/Tp<source file> 將文件編譯為 .cpp /TC 將所有文件編譯為 .c
/TP 將所有文件編譯為 .cpp /V<string> 設置版本字元串
/w 禁用所有警告 /wd<n> 禁用警告 n
/we<n> 將警告 n 視為錯誤 /wo<n> 發出一次警告 n
/w<l><n> 為 n 設置警告等級 1-4 /W<n> 設置警告等級(默認 n=1)
/Wall 啟用所有警告 /WL 啟用單行診斷
/WX 將警告視為錯誤 /Yc[file] 創建 .PCH 文件
/Yd 將調試信息放在每個 .OBJ 中 /Yl[sym] 為調試庫插入 .PCH 引用
/Yu[file] 使用 .PCH 文件 /Y- 禁用所有 PCH 選項
/Zm<n> 最大內存分配(默認為 %) /Wp64 啟用 64 位埠定位警告

-鏈接-

/LD 創建 .DLL /LDd 創建 .DLL 調試庫
/LN 創建 .netmole /F<num> 設置堆棧大小
/link [鏈接器選項和庫] /MD 與 MSVCRT.LIB 鏈接
/MT 與 LIBCMT.LIB 鏈接 /MDd 與 MSVCRTD.LIB 調試庫鏈接
/MTd 與 LIBCMTD.LIB 調試庫鏈接

-代碼分析-

/analyze[:WX-] 啟用代碼分析
WX- - 即使調用了 /WX,也不應將代碼分析警告視為錯誤

9. c語言中如何播放mp3數據幀

可以使用PlaySound()函數播放mp3聲音,該函數原型位於windows.h中,
函數原型為:
BOOL PlaySound(LPCSTR pszSound, HMODULE hmod,DWORD fdwSound);

參數pszSound是指定了要播放聲音的字元串,該參數可以是MP3文件的名字,或是MP3資源的名字,或是內存中聲音數據的指針,或是在系統注冊表WIN.INI中定義的系統事件聲音。如果該參數為NULL則停止正在播放的聲音。

參數hmod是應用程序的實例句柄,當播放MP3資源時要用到該參數,否則它必須為NULL。

參數fdwSound是標志的組合,各種可選的標志及意義如下啟頃所示。若成功則函數返回TRUE,否則返回FALSE。

播放標志以及含義:

SND_APPLICATION
用應用程序指定的關聯來播放聲音。

SND_ALIAS
pszSound參數指定了注冊表或WIN.INI中的系統事件的別名。

SND_ALIAS_ID
pszSound參數指定了預定義的聲音標識符。

SND_ASYNC
用非同步方式播放聲音,PlaySound函數在神舉開始播放後立即返回。

SND_FILENAME
pszSound參數指定了MP3文件名。

SND_LOOP
重復播放聲音,必須與SND_ASYNC標志一塊使用。

SND_MEMORY
播放載入到內存中的聲音,此時pszSound是指向聲音數據的指針。

SND_NODEFAULT
不播放預設聲音,若無此標志悄瞎陸,則PlaySound在沒找到聲音時會播放預設聲音。

SND_NOSTOP
PlaySound不打斷原來的聲音播出並立即返回FALSE。

SND_NOWAIT
如果驅動程序正忙則函數就不播放聲音並立即返回。

SND_PURGE
停止所有與調用任務有關的聲音。若參數pszSound為NULL,就停止所有的聲音,否則,停止pszSound指定的聲音。

SND_RESOURCE
pszSound參數是WAVE資源的標識符,這時要用到hmod參數。

SND_SYNC
同步播放聲音,在播放完後PlaySound函數才返回。

************************************************************

例如我想播放在C:\WINDOWS\Media目錄中的 Windows XP 啟動.MP3文件
程序如下:

#include <windows.h>
#include <stdlib.h>
int main(int argc, char* argv[])
{
PlaySound("C:\\WINDOWS\\Media\\Windows XP 啟動.MP3", NULL, SND_FILENAME | SND_ASYNC);
system("pause");
return 0;
}
*/:)))))))))))))))))))))))))))))))

10. C語言函數調用棧

程序的執行過程可看作連續的函數調用。當一個函數執行完畢時,程序要回到調用指令的下一條指令(緊接call指令)處繼續執行。函數調用過程通常使用堆棧實現,每個用戶態進程對應一個調用棧結構(call stack)。編譯器使用堆棧傳遞函數參數、保存返回地址、臨時保存寄存器原有值(即函數調用的上下文)以備恢復以及存儲本地局部變數。

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

寄存器是處理器加工數據或運行程序的重要載體,用於存放程序執行中用到的數據和指令。因此函數調用棧的實現與處理器寄存器組密切相關。

AX(AH、AL):累加器。有些指令約定以AX(或AL)為源或目的寄存器。輸入/輸出指令必須通過AX或AL實現,例如:埠地址為43H的內容讀入CPU的指令為INAL,43H或INAX,43H。目的操作數只能是AL/AX,而不能是其他的寄存器。 [5]

BX(BH、BL): 基址寄存器 。BX可用作間接定址的地址寄存器和 基地址寄存器 ,BH、BL可用作8位通用數據寄存器。 [5]

CX(CH、CL):計數寄存器。CX在循環和串操作中充當計數器,指令執行後CX內容自動修改,因此稱為計數寄存器。 [5]

DX(DH、DL):數據寄存器。除用作通用寄存器外,在 I/O指令 中可用作埠 地址寄存器 ,乘除指令中用作輔助累加器。 [5]

2.指針和 變址寄存器

BP( Base Pointer Register):基址指針寄存器。 [5]

SP( Stack Pointer Register): 堆棧指針寄存器 。 [5]

SI( Source Index Register):源變址寄存器。 [5]

DI( Destination Index Register):目的變址寄存器。 [5]

函數調用棧的典型內存布局如下圖所示:

圖中給出主調函數(caller)和被調函數(callee)的棧幀布局,"m(%ebp)"表示以EBP為基地址、偏移量為m位元組的內存空間(中的內容)。該圖基於兩個假設:第一,函數返回值不是結構體或聯合體,否則第一個參數將位於"12(%ebp)" 處;第二,每個參數都是4位元組大小(棧的粒度為4位元組)。在本文後續章節將就參數的傳遞和大小問題做進一步的探討。 此外,函數可以沒有參數和局部變數,故圖中「Argument(參數)」和「Local Variable(局部變數)」不是函數棧幀結構的必需部分。
其中,主調函數將參數按照調用約定依次入棧(圖中為從右到左),然後將指令指針EIP入棧以保存主調函數的返回地址(下一條待執行指令的地址)。進入被調函數時,被調函數將主調函數的幀基指針EBP入棧,並將主調函數的棧頂指針ESP值賦給被調函數的EBP(作為被調函數的棧底),接著改變ESP值來為函數局部變數預留空間。此時被調函數幀基指針指向被調函數的棧底。以該地址為基準,向上(棧底方向)可獲取主調函數的返回地址、參數值,向下(棧頂方向)能獲取被調函數的局部變數值,而該地址處又存放著上一層主調函數的幀基指針值。本級調用結束後,將EBP指針值賦給ESP,使ESP再次指向被調函數棧底以釋放局部變數;再將已壓棧的主調函數幀基指針彈出到EBP,並彈出返回地址到EIP。ESP繼續上移越過參數,最終回到函數調用前的狀態,即恢復原來主調函數的棧幀。如此遞歸便形成函數調用棧。

EBP指針在當前函數運行過程中(未調用其他函數時)保持不變。在函數調用前,ESP指針指向棧頂地址,也是棧底地址。在函數完成現場保護之類的初始化工作後,ESP會始終指向當前函數棧幀的棧頂,此時,若

閱讀全文

與c編譯器幀指針教學相關的資料

熱點內容
魔獸世界懷舊服會長移交命令 瀏覽:99
中文字幕在線觀看的網站 瀏覽:473
主角上自己女兒的小說 瀏覽:112
javaextjs文件上傳 瀏覽:28
有哪些佛教電影 瀏覽:149
成人劇情小說 瀏覽:113
國外免費小電影網站 瀏覽:909
怎麼把文件夾百度網盤 瀏覽:788
韓國頂級愛情電影 瀏覽:489
美國電影紅 瀏覽:946
框架梁與次梁哪條梁需要加密 瀏覽:6
俄羅斯版越戰英豪電影 瀏覽:970
寶塔上的網站源碼怎麼打包 瀏覽:282
采補爐鼎小說 瀏覽:892
窗口輪廓菜單命令的快捷鍵 瀏覽:300
怎麼看安卓版本是幾位 瀏覽:641
貓撲天涯的情感類小說 瀏覽:428
正能量電影中國 瀏覽:276
妖神記免費全集小說 瀏覽:536
喜虎棋牌源碼 瀏覽:909