精華區beta Programming 關於我們 聯絡資訊
你的觀念有點問題喔, C++ 是個 identifier 要使用前必得先宣告的語言, identifier 的中文通常翻成「識別字」, 也就是變數名、函式名、類別名稱、型別名稱等等; compiler 是由上往下看的, 所以: void foo() { int x; x = 5; } 這樣合法, 因為 int x 宣告了 x 是 int 型別 (同時也定義了 x 的實體,但在此不是重點), 所以當 compiler 看到 x = 5; 時知道 x 是什麼東西。 換句話說「宣告」是為了讓 compiler 知道你的 identifier 是什麼東西, compiler 必須知道它是什麼東西才能通過編譯。 struct/class 的話也是一樣, 但需要知道的詳細程度會根據你 identifier 的性質而定, 定義 object 或是存取 object 的 members 需要 struct/class 的完整定義, 但定義 object 的 pointer 或 reference 則只需要 struct/class 的宣告, 我想這是相當容易理解的。 #include 這類 # 開頭的 directives 是 preprocessor 在處理的, 我想教科書應該都會告訴你, C 程式在送進 compiler 之前, 會先送進 preprocessor 做一些 text processing, 如果你不知道它在做什麼, 可以用 gcc -E test.cxx > test_output 觀察 test_output 檔的內容, 它就是經過 preprocessor 後真正餵進 compiler 的 code, compiler 看到的其實是這份 code, 在這裡面不會存在 #include 和 #define 這些 preprocessor directives。 就如同教科書說的, preprocessor 做的事情就是文字處理, #include 其實就是 copy/paste, #include <xxx.h> 就是把 xxx.h 的內容複製下來貼到這個位置上, #define X 10 就是一種有點聰明的 replace, 它會將 source code 中所有叫做 X 的 tokens 代換成 10, 如果你聽不懂我說什麼, 就寫個小程式用 gcc -E 去處理一下觀察結果就會知道了。 所以說, 我們都會把「宣告」放在 header files 內, 然後在 source code 的最上方 #include 它們, 也就是把這些「宣告」貼在最上面, 好讓 compiler 能知道後面各個 identifier 是什麼東西, 好讓 compile 的動作能順利完成不會有錯誤, 事情就是這麼的單純。 根據 C/C++ 的基本規則, 「宣告」可以重複 (但不能不一致), 但「定義」不能重複, 原先 header guard 只是為了避免 compiler 處理到重複的宣告影響編譯速度, 後來也間接的有了擋住 struct/class 定義式重複出現的效果。 先大概說到這, 有很多定義和宣告的細節以及特例我沒有講, 但是 C++ Primer 其實都有寫了, 建議你好好看完它。 下面還有逐段回你的, 別看漏了。 ※ 引述《[email protected] (10/100天自我觀察)》之銘言: > my explaination about why we need to 宣告: > ==================================== > 因為如果我們有寫 A* a的話 > 那麼對於編譯器來說,因為只要是指標的東西,都有它固定的byte數 > 所以他只要把這固定byte數的空間配置出來,用來表示a這個變數即可 > 但是對於編譯器而言,他會不認識 "A" 這個東西是啥, > 所以寫程式的人必須要加個class A; 在最前面, 如此一來編譯器在未來中讀到 > 跟A有關的東西時才會認識,是這樣子嗎? >"< 當你寫了 A *a; 時, 它代表了「定義」兼「宣告」的動作, 由於「定義」會配置實體空間 (class/struct 定義除外), 所以就如同你所說的, compiler 需要知道需要知道 a 的 size 以分配空間給 a。 但是後面你說錯了, 當你寫了 class A; 時, 因為你已經「宣告」了 A 是一個 class name, compiler 已經知道 A 是一個 class, 只是它還不知道 A 裡面有什麼 members (除非看到 class 定義式), 所以並非你說的「但是對於編譯器而言,他會不認識 "A" 這個東西是啥」, 如同前面所述, compiler 看到一個 identifier 就要知道它是啥, 不然你連 compile 都不會過。 > ====================================== > my explaination about why we need to include: > ========================================== > 譬如一個xxx.h檔與一個xxx.cpp檔會變成xxx.o檔, 事實上,xxx.h 會被貼到 xxx.cpp 的 #include 那行的位置裡, 所以實際參與 compile 過程的只有 xxx.cpp。 只不過 preprocessor 會加入一些 line mapping 的 info 進 .cpp, 所以當你 .h 寫錯時你 compile .cpp 檔, compiler 會告訴你是 .h 檔的哪裡寫錯, 這個你用 gcc -E 觀察一下就會發現了。 > 所以xxx.o檔裡放的就是一堆run下去後機器可以執行的程式碼 還有叫做 symbol table 的東西, 這個在 linking time 很重要, 也牽涉到 extern "C" 等等的意義, 以及「宣告」和「定義」上最明顯的差距。 不過定義 auto variable 只會配置 stack slot, 不會出現在 symbol table 裡, 要觀察的話請用 global variables 和 functions。 你可以用 nm 指令觀察 .o 的 symbol table, 看看裡面各個 symbol name 以及它的性質, 只有「宣告」沒有「定義」的 symbols 會被標為 undefined (也就是字母 U), 而當你呼叫 linker 將所有 .o link 成執行檔時, 若所有的 .o 裡那個 symbol 都是 undefined, linker 就會吐出 linking time error 告訴你找不到 symbol 的定義。 > 而這些程式碼就是xxx.cpp裡面所寫的那一堆碼 Ok 還不 ok, 還有 .h 裡面的一些 inline functions 的 instance, 如果你的 .h 裡有 template function, 而 .cpp 裡面有 instantiate 它的某些 instances, 那這個 .o 檔裡面還會有這些已定義的實體。 > 那麼什麼時候是要 include xxx.h的時候呢? > 就是當我們要真的使用到 xxx.o 裡面的程式碼時 不對, 是你需要 identifiers 的「宣告」以便「通過編譯」的時候, 僅此而已, 注意這只是為了「通過編譯」, 並沒有說能保證通過連結。 > 譬如上面您提到的例子,我們因為在物件參數、宣告實體 都會用到建構子呀, > 而用到這建構子也就是代表說你有使用到 xxx.o裡的程式碼,故 我們必須要 > 去include xxx.h檔,在得知此.h檔的資訊後,我們才有辦法去正確呼叫到 xxx.o裡的 > 程式碼 你要這樣說其實也沒錯 (但太籠統), 實際上 #include "xxx.h" 的主要目的是通過編譯, 是否能正確呼叫到 xxx.o 或其它 libraries 如 xxx.a xxx.so, 則完全是看你的 linker 來決定, compiler 根本不知道這些事情。 你用的是 GCC 所以我能稍微跟你解釋一下, 你用的 gcc 其實是一個前端程式 (術語上叫 driver), 它「大體上」 (中間一些如 collect2 等等的你目前不需要知道) 會分別呼叫: cpp (C preprocessor) cc1plus (C++ compiler) 或 cc1 (C compiler) ld (linker) 來完成你的整個編譯和連結動作, 對一個 C/C++ programmer 來說, 必須要有區分這三者責任的能力, 才有辦法徹底理解多檔編譯的原理及實務方法, 雖然說觀念的部分計概都有教了 (我是說 editor ----> preprocessor -> compiler ----> ... 的那張圖), 但是很多人沒有融會貫通就開始被抓去寫 code, 這也是我們大學教育十分奇怪的地方。 甚至, 有極多數的學生會將 #include 理解成 link library 的動作, 這都是老師亂教所造成的錯誤理解, 事實上, 還有不少老師的教材就是這樣寫的。 > ========================================== > 我覺得很多方法我好像就是在閉門造車,才會不斷的在try 在摸索 > 但我不知道我到底觀念有沒有正確,到底摸索到的東西到底有沒有偏差 > 所以還是得需要各位先進們的指導了, 謝謝你們^__^y -- Name: Tseng, Ling-hua E-mail Address: [email protected] School: National Tsing Hua University Department: Computer Science Interesting: C++, Compiler, PL/PD, OS, VM, Large-scale software design Researching: Software pipelining for VLIW architectures Homepage: https://it.muds.net/~uranus -- ╔═══╗ ┼────────────────────────╮ 狂狷 Origin:[ 狂 狷 年 少 ] whshs.cs.nccu.edu.tw ╰─╮ 年少 ┼╮ < IP:140.119.164.252 > ╰─╮ ╚╦═╦╝ From:61-230-218-171.dynamic.hinet.net ─╨─╨─ KGBBS 遨翔"BBS"的狂狷不馴;屬於年少的輕狂色彩 [修改]tinlans:61-230-218-171.dynamic.hinet.net 06/12/23 6:26:36