[c++][工程構建] c 和 c++ 關於編譯和鏈接的一些說明

前言:

從源代碼到目標二進制文件,中間經歷很多過程,其中之一就是編譯,如果我們將  “ 彙編 + 編譯 ” 看做一個整體,那麼編譯可以認爲是從 源代碼 到 二進制碼的一個轉變,這個實體的變動有很多附加動作,其中之一就是符號表的生成,這也是非常重要的一環,軟件調試正是依賴於符號表來重現函數調用棧的。

 

編譯的兼容性:

C++標準允許每個編譯器設計人員自定義名稱符號表(symbol table)中條目的格式,因此由不同編譯器對相同源文件實施編譯的最終結果可能是不一樣的,因爲符號表的格式不通,比如func函數,在通用編譯器輸出的符號表中是func,而在yk自定義的編譯器輸出的符號表中是func_yk,這樣在鏈接時就會出現互相找不到的問題,雖然他們代表的代碼邏輯都是一樣的。
因此,儘量整個工程編譯。如果無法整個工程編譯,那麼就用objdump命令逐條覈實二進制文件的符號表;亦或者,確認各個二進制文件的編譯器使用的是同一個。

 

C 和 C++混合編程需要注意:

語言的鏈接性(符號表的翻譯和查找規則) ,鏈接程序要求每個函數的函數名都是獨一無二的(這是編譯器要求的)。

  • 在C語言中,不存在函數的多態,因此事OK的;
  • 在c++中,存在函數的多態,這就會存在衝突,因此c++編譯器會爲相同的函數名打上不同的標籤。

綜上,c編譯器和c++編譯器在編譯時的處理時不一樣的,那麼既有c又有c++的程序,在定義函數的時候,就需要指明當前函數該如何處理。

默認情況下,編譯器會根據文件後綴是.c還是.cpp來自動處理

但是如果想在.cpp文件中引用c函數,那麼就需要使用extern "C" void func(void)來申明;

同理在.c文件中,需要用extern "C++" void func(void)來聲明c++庫/源文件中的函數。

 

寫代碼時文件拆分思路:

.h文件中包含屬性域和方法聲明,.c文件中包含方法的實現。這樣在使用此文件提供的方法的時候,只要包含.h頭文件即可。這也是一種“面向對象”的設計思路。
包含頭文件意味着在預編譯的時候聲明瞭屬性域,並且聲明瞭方法,那麼在鏈接的時候就不存在找不到符號表的問題,但是這裏需要避免重複include,方法就是

???.c
#include xxx.h

xxx.h
#ifndef _XXX_H
#define _XXX_H
...
...
...
...
#endif

不要在頭文件中聲明變量,頭文件中僅用來定義變量類型就好,千萬不要出現某個實實在在的變量,比如可以定義結構體,定義枚舉類型,定義聯合體,但是不要在其中聲明某個結構體實例。因爲會導致變量重複定義的情況出現。

頭文件中一般包含如下內容:函數聲明,使用#define和const定義的常量,結構體聲明,類聲明,模板聲明,內聯函數
總之一句話,不要在頭文件中出現任何格式的“實例”,頭文件只做聲明用。

 

內部鏈接屬性 和 外部鏈接屬性:

什麼是鏈接屬性?

以編譯單元爲單位(h文件如果不適用gcc進行編譯則視爲非編譯單元,因爲它是在預編譯階段被#include複製到源代碼文件中),內部的變量和函數在編譯時對外部是否導出符號表,如果其他編譯單元在鏈接時能夠看到,則認爲此變量和函數是外部鏈接屬性,否則是內部鏈接屬性。

一個編譯單元(.cpp)編譯成obj文件後,至少還會有未解決符號表、導出符號表、地址重定向表。而如果這個名稱是內部連接的話,那在導出符號表中不存儲它的入口。也就是別的obj文件無法鏈接到這個名稱。而外部連接剛好相反,在導出的符號表中有它入口。

 

PS:

1)編譯器會提供棧容編譯選項,編譯時可以指定;

2)volatile關鍵字:告訴編譯器“雖然程序沒有修改某個變量,但是這個變量也可能會改變,因此在編譯時不要做優化”,比如某個指向硬件緩衝區的指針,緩衝區裏的內容由硬件驅動來改變,因此隨時都可能改變而且應用軟件也不知道,這種情況下,程序就應該按部就班的執行,而不是讓編譯器自作主張地優化語句。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章