c檔案 h檔案 定義 宣告(詳解)

2021-08-29 16:29:04 字數 3694 閱讀 4122

最近在重新學習程式設計,拿乙個「掃雷」的遊戲練練手,碰到問題

為便於全文的展開,先看看我的「掃雷」工程的內容

我們知道些程式會用到各種變數名,結構體名,函式名,程式是我們寫的,我們當然知道這些識別符號的含義,但是編譯器看到識別符號是一臉懵逼的,比如如果沒有各種宣告,編譯器編譯「掃雷」遊戲時就有疑問了,init_mine識別符號是什麼東西?它是函式還是變數?是變數的話,它是什麼型別的?占用幾個位元組?是函式的話?它有沒有傳入引數?有幾個?什麼型別?有沒有返回值?裡面用到了全域性變數了嗎?什麼型別?什麼型別?會不會是結構體或者聯合、列舉型?

變數定義:用於為變數分配儲存空間,還可為變數指定初始值。程式中,變數有且僅有乙個定義。

變數宣告:用於向程式表明變數的型別和名字(這句話可以理解成,變數宣告告訴編譯器某個符號的型別)。

定義也是宣告:當定義變數時我們宣告了它的型別和名字。

extern是宣告不是定義:通過使用extern關鍵字宣告變數名而不定義它,表示extern變數的定義不在這個檔案中,請到其它.c檔案中尋找。

變數在使用前必須被被定義或者宣告。

在乙個程式中,變數只能定義一次,卻可以宣告多次。

定義分配儲存空間,而宣告不會。

函式的宣告和定義區別比較簡單,帶有的就是定義,否則就是宣告。

函式定義的時候自帶宣告的性質

函式的宣告告訴編譯器,函式有哪些傳入引數,分別是什麼型別,函式有返回值嗎,是什麼型別。函式的定義告訴編譯器函式如何實現

編譯器預處理

詞法與語法分析

編譯首先編譯成純彙編語句,再將之彙編成跟cpu相關的二進位製碼,生成各個目標檔案 (.obj檔案)

由於模組包含外部呼叫,即指向其他模組中的資料或指令的位址,或包含對庫函式的引用,編譯程式或匯程式設計序負責記錄引用發生位置,其處理結果將產生相應的多個目標模組,每個目標模組都附有供引用使用的內部符號表和外部符號表。符號表中依次給出各個符號名及在本目標模組中的名字位址,在模組被鏈結時進行轉換

程式編譯時,不會找某個呼叫模組的實現,只有在鏈結時才進行這個工作

我們在.c檔案中include.h檔案實際是引入裡面的宣告,使得編譯通過,編譯器不關心某個函式在**實現,怎麼實現,關心的是函式的傳入引數是什麼型別,返回值是什麼型別

編譯器在編譯時是以c檔案為單位進行的(也稱編譯單元),也就是說如果你的專案中乙個c檔案都沒有,那麼你的專案將無法編譯,所謂的編譯單元,是指乙個.c檔案以及它所include的所有.h檔案

鏈結鏈結程式的作用是根據目標模組之間的呼叫和依賴關係,將主呼叫模組、被呼叫模組以及所用到的庫函式裝配和鏈結成乙個可裝載執行模組

以上通俗的解釋:把編譯過程中的符號換成乙個個實實在在的「**段」和變數儲存空間

通常,編譯器會在每個.obj檔案中都去找一下所需要的符號,而不是只在某個檔案中找或者說找到乙個就不找了。因此,如果在幾個不同檔案中實現了同乙個函式,或者定義了同乙個全域性變數,鏈結的時候就會提示"redefined"

鏈結器是以目標檔案為單位,它將乙個或多個目標檔案進行函式與變數的重定位,生成最終的可執行檔案

裝載涉及到重定位,此處關係不大,略

現實情況並不是嚴格按照上述五步進行的,類似於上學的「跳級」

有裝載時鏈結,一邊裝載一邊鏈結,裝載和鏈結合二為一,被呼叫**段在哪兒(記憶體或外存)就用哪兒的**

執行時裝載和鏈結,執行裝載鏈結三合一程式執行時,真正用到(指選擇語句)哪段**就去取哪段**來執行

鏈結時,鏈結器往往會發現工程中包含很多函式,鏈結的目的是生成可執行檔案,這麼多函式從**開始執行呢?人為規定從有識別符號main的函式處開始執行,main也叫做程式入口點

如果在c檔案中宣告巨集,結構體,函式等,那麼我要在另乙個c檔案中引用相應的巨集,結構體,就必須再做一次重複的工作,如果我改了乙個c檔案中的乙個宣告,那麼又忘了改其它c檔案中的宣告,這不就出了大問題了,程式的邏輯就變成了你不可想象的了,如果把這些公共的東東放在乙個標頭檔案中,想用它的c檔案就只需要引用乙個就ok了!!!這樣豈不方便,要改某個宣告的時候,只需要動一下頭檔案就行了

在標頭檔案中宣告結構體,函式等,當你需要將你的**封裝成乙個庫,讓別人來用你的**,你又不想公布原始碼,那麼人家如何利用你的庫呢?也就是如何利用你的庫中的各個函式呢??一種方法是公布原始碼,別人想怎麼用就怎麼用,另一種是提供標頭檔案,別人從頭檔案中看你的函式原型,這樣人家才知道如何呼叫你寫的函式,就如同你呼叫printf函式一樣,裡面的引數是怎樣的??你是怎麼知道的??還不是看人家的標頭檔案中的相關宣告啊!!!當然這些東東都成了c標準,就算不看人家的標頭檔案,你一樣可以知道怎麼使

標頭檔案能加強型別安全檢查。如果某個介面被實現或被使用時,其方式與標頭檔案中的宣告不一致,編譯器就會指出錯誤,這一簡單的規則能大大減輕程式設計師除錯、改錯的負擔

如果在標頭檔案中實現乙個函式體,那麼如果在多個c檔案中引用它,而且又同時編譯多個c檔案,將其生成的目標檔案連線成乙個可執行檔案,在每個引用此標頭檔案的c檔案所生成的目標檔案中,都有乙份這個函式的**,如果這段函式又沒有定義成區域性函式,那麼在連線時,就會發現多個相同的函式,就會報錯

如果在標頭檔案中定義全域性變數,並且將此全域性變數賦初值,那麼在多個引用此標頭檔案的c檔案中同樣存在相同變數名的拷貝,關鍵是此變數被賦了初值,所以編譯器就會將此變數放入data段,最終在連線階段,會在data段中存在多個相同的變數,它無法將這些變數統一成乙個變數,也就是僅為此變數分配乙個空間,而不是多份空間,假定這個變數在標頭檔案沒有賦初值,編譯器就會將之放入 bss段,聯結器會對bss段的多個同名變數僅分配乙個儲存空間

每個.h檔案都必須有乙個對應的同名.c檔案嗎?

不需要,c檔名可以任意定義,只要把.h檔案include進去就好了。但是通常我們在.h檔案寫函式宣告,在對應同名.c檔案寫函式實現

大家平時#include,那裡面的函式(如printf( ))的實現在**?

首先沒有.c檔案寫這些函式的實現,我推斷函式的實現已經編譯成目標檔案,放在編譯器裡或者作業系統裡

我們知道.h檔案裡內容一般會比較豐富,我們的程式裡並沒有用到這麼多函式,怎麼辦?有影響嗎?

定義有且只能有一次,宣告可以有很多次,沒用到的東西宣告也不會影響啥,就像「過馬路,左右兩邊都看」一樣

如果說難題最難的部分是基本概念,可能很多人都會持反對意見,但實際上也確實如此。我高中的時候學物理,老師抓的重點就是概念–概念一定要搞清,於是難題也成了容易題。如果你能分析清楚一道物理難題存在著幾個物理過程,每乙個過程都遵守那一條物理定律(比如動量守恆、牛ii定律、能量守恆),那麼就很輕鬆的根據定律列出這個過程的方程,n個過程必定是n個n元方程,難題也就迎刃而解。即便是高中的物理競賽難題,最難之處也不過在於:

(1)、混淆你的概念,讓你無法分析出幾個物理過程,或某個物理過程遵循的那條物理定律;

(2)、存在高次方程,列出方程也解不出。而後者已經是數學的範疇了,所以說,最難之處還在於掌握清晰的概念;

程式設計也是如此,如果概念很清晰,那基本上沒什麼難題(會難在數學上,比如演算法的選擇、時間空間與效率的取捨、穩定與資源的平衡上)。但是,要掌握清晰的概念也沒那麼容易。

搞清楚語法和概念說易也易,說難也難。竅門有三點: 不要暈著頭工作,要抽空多思考思考,多看看書;

看書要看好書,問人要問強人。爛書和爛人都會給你乙個錯誤的概念,誤導你;

勤能補拙是良訓,一分辛苦一分才;

概念清楚很重要,我們要概念清楚,那什麼是概念清楚呢?我的老師告訴我:「概念清楚,就是你對著乙個問題一直問,問下去,你都可以很清晰的理解和解釋,直到它再顯然不過了」

h檔案和 c檔案

1 h標頭檔案 h檔案中一般是宣告,包括 變數宣告 巨集定義 列舉宣告 結構體宣告 函式宣告等。h標頭檔案是對該模組 c檔案 介面的宣告,介面包括該模組提供給其他模組呼叫的外部函式以及外部全域性變數。其他模組訪問這些外部定義的變數和函式都需要在.h檔案中冠以extern關鍵字宣告 模組 c檔案 內的...

c語言中 c與 h檔案詳解

很多人對c語言中的 檔案包含 都不陌生了,檔案包含處理在程式開發中會給我們的模組化程式設計帶來很大的好處,通過檔案包含的方法把程式中的各個功能模組聯絡起來是模組化程式設計中的一種非常有利的手段。檔案包含處理是指在乙個原始檔中,通過檔案包含命令將另乙個原始檔的內容全部包含在此檔案中。在原始檔編譯時,連...

strcpy沒有宣告 c 標頭檔案詳解

2010 10 23 12 19 4971人閱讀 收藏舉報 c string iostream cmath.h語言 協議分發程式程式在公司機器上編譯執行良好,而我在我的虛擬機器上編譯,卻出下如下錯誤 msg.cpp 3101 error strcpy was not declared in this...