一個最終的可執行文件里,絕對不允許出現兩個同名的全局變量,也不允許出現同名的全局函數。
全局函數:只要不用 static 修飾符修飾的函數,全部都是全局的。
全局變量:函數外聲明定義,且不加 static 修飾符修飾的變量。
例如,one.c 里有一個函數 function, 那么你如果想讓 main.c 生成的 main.o 能夠鏈接 one.o 的話,那么 main.c 里就不能再有一個函數叫做 function 了。否則就會報重定義錯誤。
這就好像,你的班上有兩個人都叫 小明, 這個情況確實麻煩,你肯定會用各種辦法區分他們,比如說,大小明,小小明,這種方法,實際上,你已經為他們重新命名了。
你也許發現了一個問題,那就是:main.c 里 有一句
extern void function();
這一句不是跟 one.c 里的 function 重名了么。那么編譯器為啥不報錯呢?
想一想整個過程。
第一步:
gcc -c main.c -o main.o
這一步就是編譯 main.c 的過程,好吧,這一步,編譯器完全不用關心在某個地方還有個 one.c,這個源文件里有一個同名的function。
仔細解釋一下這句話:
extern void function();
你寫程序的時候,要有一種跟編譯器時刻交流的感覺。
這一句是你寫給編譯器看的,你就是要告訴編譯器這樣一個事實:
--Hey!編譯器!
--main.c 這個源文件里要用到一個函數 function,是void 類型的,沒有參數,你暫時不用管這個函數到底在哪,你先編譯通過,這個函數最終會被你的哥們鏈接器鏈接過來!
編譯器:
--ok!我相信我的哥們鏈接器!
這個過程就叫做“聲明”。如果沒有這一句,沒有這個過程,編譯器就會在 main.c 里遇見一個陌生的函數調用 function,嗯,結果就是,?。?/p>
而這個函數 function 的真正定義就是在 one.c 里。所謂定義,就是具體的實現,就是這個函數大括號里的東西。也可以這么說,沒有大括號(即使沒有 extern 修飾符)的地方就是聲明,有大括號(即使大括號里是空的)的地方,那就是定義。
還有一句,聲明可以多次,比如說,每一個用到了函數 function 的地方,都要聲明(你可以不用 extern 這個修飾符,試試吧)。但是,定義只有一個,這也符合上一小節說的:不能重定義!
這一節不準備總結的太細。
你寫好的源文件是放在硬盤上的,你編譯成的目標文件也是放在硬盤上的,鏈接成的可執行文件也是放在硬盤上的。
當你,運行這個可執行文件的時候,操作系統就會做一件事情:裝載。
可以簡單粗暴的假想,操作系統把你的可執行文件直接復制到內存的某個地方,然后,cpu開始在這個地方去找 main 函數,進而執行整個程序。
所以你的程序才會占內存的空間:變量會占,函數會占,動態開辟(例如malloc)的更會占。
你的同事十分能干,他用他閑暇的時間,積極地擴充他所維護的 one.c,使這個 one.c 更加豐富。比如說,他增加了一個函數
int add(int a, int b)
{
//省略代碼
}
可是,你知道,函數最終是要占內存空間的。并且,你完全用不到這個新加的函數 add。
問題就是,你鏈接的時候,已經把整個 one.o 鏈接到你的 可執行文件里了。這個可執行文件確實包含了 add 的具體實現的代碼,也就是說,最終運行的時候,內存里確實會有這一部分,而且是完全沒用的部分。
你僅僅用到了 one.o 里的一個函數 function 就要鏈接整個 one.o,這就好像,你到飯店,只想吃一個漢堡,卻不得不花錢買一份套餐,浪費。
你不得不跟你的同事說明一下這個問題,最終你們商量出了一個辦法。
你的同事決定,將他寫的每一個函數單獨放到一個源文件里去,比如說,function函數放到 function.c ,add函數放到 add.c。
這樣,一個函數對應一個源文件,也對應一個目標文件,也就是說,一個目標文件里只有一個函數,沒有其他的東西。
你使用的時候,就能夠隨便挑選,去鏈接哪個目標文件了。
比如說,你的同事已經有了這些:
one.c 包含了 void one(){} 函數
two.c 包含了 void two(){} 函數
three.c 包含了 void three(){} 函數
等等……。而且也提供了一份頭文件叫做 all.h, 這個all.h包含了所有他編寫的函數的聲明。
你使用的時候先包含這個all.h, 像這樣
#include "all.h"
然后,編譯;然后鏈接的時候,你用到了哪個,就在你的gcc命令里加上那個目標文件就行。例如,用到了 void three() 這個函數:
ld main.o three.o -o go
這樣就行了。
但是即便是這樣, 你還是覺得麻煩,你得在心里記錄一下,你用了哪些函數,并且去手動敲下命令進而鏈接,這樣容易出錯。
任何問題都是有可能解決的,這次也不例外。
新聞熱點
疑難解答