我知道存在許多解決同一問題的問題,但我一直無法找到可以回答我的問題的問題。我理解編譯和鏈接之間的大圖差異,前者將每個源檔案轉換為目標檔案形式的機器代碼,后者將這些目標檔案“鏈接”在一起,形成一個可執行檔案。
然而,一旦我們將預處理投入到這種混合中,我的困惑就會出現。我的理解是,如果我們匯入另一個庫,那么所有這個庫本質上都是復制并粘貼到我們的代碼中。因此,即使我們正在處理多個檔案并在另一個檔案中呼叫一個,內容是否仍然轉儲到另一個檔案中?如果是這種情況(我確信這是不正確的并且我在某處有誤解)為什么鏈接甚至是必要的?我們不是已經在處理一個巨大的聚合源檔案了嗎?
uj5u.com熱心網友回復:
預處理發生在編譯之前。前處理器接受一個或多個源檔案,并輸出另一個源檔案,然后編譯該檔案。前處理器是一個文本到文本的轉換器,它與鏈接無關。
從概念上講,可以使用前處理器將所有內容轉儲到一個源檔案中,然后將其直接編譯為可執行檔案,跳過生成目標檔案并將它們鏈接在一起的階段。然而,這在實踐中會非常不方便。想象一個 100,000,000 行代碼程式(這包括所有標準庫和所有平臺庫以及所有第三方庫)。您需要更改一行。您是否愿意再次編譯所有 100,000,000 行?當你在那一行出錯時,再做一次(一次又一次,一次又一次)?
一些庫完全作為頭檔案分發。它們不需要任何二進制檔案,并且每次編譯程式時都會與您的程式一起編譯。但并不是所有的圖書館都這樣。有些太大了,不能每次都編譯。有些不是用 C 或 C 撰寫的(例如,它們需要一些匯編語言,或者可能是 Fortran)。有些不能作為源代碼分發,因為供應商出于著作權原因不愿意這樣做。在所有這些情況下,解決方案是將庫編譯為目標檔案,然后將這些目標檔案與僅包含它們公開的函式和變數的介面(沒有定義的宣告)的頭檔案一起分發。
<iostream>你提到的是一個混合包。在大多數實作中,它包含每次編譯程式時編譯的函式定義(模板和小行內函式),以及外部函式的宣告,其定義由供應商編譯并作為預編譯庫分發。
uj5u.com熱心網友回復:
我的理解是,如果我們匯入另一個庫,那么所有這個庫本質上都是復制并粘貼到我們的代碼中。
這通常是不正確的。
在 C 和 C 中,#include指令主要用于匯入頭檔案,而不是所有庫。頭檔案主要用于宣告函式,有時也用于宣告物件,而不定義它們。在通用語言中,頭檔案中的宣告描述函式但不定義它們。
例如,這是一個宣告:
double square(double x);
這表示“square是一個接受double引數并回傳double值的函式”,但它不包含代碼square,也沒有告訴我們做什么square。(另外,引數名 ,x可以在宣告中省略。)宣告只是告訴我們呼叫函式需要什么:我們必須double在適當的位置放置一個值來傳遞引數,然后我們呼叫例程,我們期望double回傳一個值。編譯器無法從此宣告生成函式的匯編代碼。
定義包含函式的代碼:
double square(double x)
{
return x*x;
}
在大多數庫中,定義位于單獨編譯的源檔案中。這些編譯的結果可以進行各種形式的“庫”檔案,如可用.a,.so,.dylib,或其他檔案。該庫還提供僅包含宣告而不包含定義的頭檔案。
使用該庫的程式#include用于包含頭檔案。這為編譯器提供了呼叫庫例程所需的資訊,但它不會匯入所有庫。定義保持獨立,編譯到上面提到的其他檔案中。要制作一個完整的程式,程式的普通目標模塊必須與庫的庫檔案相鏈接。
uj5u.com熱心網友回復:
編譯將每個翻譯單元轉換為表示屬于單個翻譯單元的二進制代碼的特殊格式,以及將多個單元連接在一起的一些附加資訊。
鏈接:在程式的所有模塊或所有功能之間建立鏈接以繼續執行程式稱為鏈接。
轉載請註明出處,本文鏈接:https://www.uj5u.com/caozuo/348686.html
上一篇:如何按升序排列結構陣列中的結構?
下一篇:將小塊資料寫入閃存頁面
