2012-10-01 77 views
0

我想問的函數簽名的順序,可以調用和定義Order函數簽名,呼籲和定義

一樣,哪一個會在電腦看第一,第二和第三

所以:

#include <iostream> 
using namespace std; 

void max(void); 
void min(void); 

int main() { 

max(); 

min(); 

return; 
} 

void max() { 

return; 
} 

void min() { 

return; 
} 

所以這是我的想法,

計算機將前往主,並期待在函數調用,那麼它會看

函數簽名,最後它會查看定義。

是對嗎?

謝謝

+0

您是否在詢問計算機在編譯期間或運行期間的功能? –

+0

它可能會徹底擺脫所有功能的痕跡。 – chris

+0

我在運行期間詢問,哪些東西(簽名,調用和定義)會使計算機看起來第一個和第二個... –

回答

1

大家可能知道,所述編譯器(通過若干中間步驟)的程序成機器代碼轉換。下面是在Windows 8在調試模式下的Visual Studio 2012進行編譯時爲main()機器代碼的dissassembly:

int main() { 
00C24400 push  ebp       # Setup stack frame 
00C24401 mov   ebp,esp 
00C24403 sub   esp,0C0h 
00C24409 push  ebx 
00C2440A push  esi 
00C2440B push  edi 
00C2440C lea   edi,[ebp-0C0h]    # Fill with guard bytes 
00C24412 mov   ecx,30h      
00C24417 mov   eax,0CCCCCCCCh 
00C2441C rep stos dword ptr es:[edi] 

max(); 
00C2441E call  max (0C21302h)    # Call max 

min(); 
00C24423 call  min (0C2126Ch)    # Call min 

return 0; 
00C24428 xor   eax,eax 
} 
00C2442A pop   edi       # Restore stack frame  
00C2442B pop   esi 
00C2442C pop   ebx 
00C2442D add   esp,0C0h 
00C24433 cmp   ebp,esp 
} 
00C24435 call  __RTC_CheckEsp (0C212D5h) # Check for memory corruption 
00C2443A mov   esp,ebp 
00C2443C pop   ebp 
00C2443D ret 

的具體細節將因編譯器和操作系統的操作系統而有所不同。如果min()或max()具有參數或返回值,則會根據體系結構適當傳遞它們。關鍵是編譯器已經計算出參數和返回值是什麼,並創建了剛剛通過或接受它們的機器代碼。

如果您希望幫助進行調試或進行低級調用,但要注意發出的機器代碼可能變化很大,您可以瞭解更多詳細信息。例如,這裏是(上即與優化)編譯在釋放模式在同一系統中相同的代碼:

return 0; 
01151270 xor   eax,eax 
} 
01151272 ret 

正如你可以看到,它已檢測到min()max()什麼也不做,完全刪除它們。由於現在沒有用於設置和恢復的堆棧幀,所以沒有了,只剩下一條指令將eax設置爲0,然後返回(因爲返回值位於eax寄存器中)。

+0

我對彙編並不太熟悉,但我認爲發行版的「noop」只是通過將它與自身異或,然後通過一條ret指令從該程序返回0來創建eax 0。 – chris

+0

@chris是正確的。我在問題中進一步解釋了它。 – akton

1

是對嗎?

您需要了解函數的聲明和函數定義,編譯之間的差異,鏈接和執行,以及非虛擬和虛擬功能之間的差異之間的差異。

函數聲明
這是一個函數聲明:void max(void);。它並沒有告訴編譯器關於函數的功能。它所做的是告訴編譯器如何調用該函數以及如何解釋結果。當編譯器編譯某個函數的主體時,將其稱爲函數A,編譯器不需要知道其他函數做了什麼。它需要知道的是如何處理函數A調用的函數。編譯器可能會生成彙編中的代碼或與您的C++函數調用相對應的某種中間語言。或者它可能會拒絕你的C++代碼,因爲你的代碼沒有意義。

確定您的代碼是否有意義是這些函數聲明的另一個關鍵目的。這在C++中特別重要,其中多個函數可以具有相同的名稱。如果不知道這些函數,編譯器如何知道函數調用哪個函數?當您的C++代碼調用某個函數時,編譯器必須找到其中一個函數聲明的最佳匹配(可能涉及類型轉換)。如果編譯器根本找不到匹配,或者找到多個匹配,但無法將其中一個匹配爲最佳匹配,那麼您的代碼沒有意義。

當編譯器確實找到最佳匹配,將所生成的代碼將在一個未定義的外部參照該函數的調用的形式。函數所在的位置不是編譯器的工作。

函數定義
void max(void)是一個函數聲明。相應的void max() {...}是該函數的定義。當編譯器正在處理void max() {...}時,它不必擔心其他函數調用它。它只需要擔心處理void max() {...}。此函數的主體變成彙編或中間語言代碼,插入到某個編譯的目標文件中。編譯器將這個生成的代碼的入口點地址標記爲這樣。

編制與聯
到目前爲止,我已經談到了編譯器做什麼。它會生成與您的C++代碼相對應的低級代碼塊。由於這些外部引用,生成的代碼尚未準備好進入黃金時段。解決那些未定義的外部引用是鏈接器的工作。鏈接器是從多個目標文件和多個庫構建可執行文件的。它會跟蹤它將這些代碼塊放在可執行文件中的位置。那些未定義的外部引用呢?如果鏈接器已經將該引用放置在可執行文件中,則鏈接器只是填充該引用的佔位符。如果鏈接器沒有遇到該引用的定義,它會將引用和佔位符放到仍然未解析的引用的列表中。每當鏈接器向可執行文件添加一段代碼時,它都會檢查該列表以查看它是否可以修復任何尚未解析的引用。最後,您要麼解決了所有參考,要麼您仍然會有一些未完成的參考。後者是一個錯誤。前者意味着你有一個可執行文件。

執行
當你的代碼運行時,這些函數調用實際上只是一些堆棧管理各地的機器語言等同邪goto聲明的包裹。沒有檢查你的函數聲明;這些代碼在執行時根本不存在。返回?那也是goto

非虛擬對虛擬函數
我上面說涉及非虛函數。運行時調度確實發生在虛擬功能上。運行時調度與檢查函數聲明無關。這些虛擬功能可能是另一個問題的問題。

最後一件事:
走出using namespace std;認爲它是類似於吸菸的習慣。這是一個壞習慣。