33.1.原始碼到可執行程式過程
(1)原始碼.c檔案->(編譯)->elf可執行程式。
(2)原始碼.c->(編譯)->目標檔案.o->(鏈結)->elf可執行程式。
(3)原始碼.c->(編譯)->彙編檔案.s->(彙編)->目標檔案.o->(鏈結)->elf可執行程式。
(4)原始碼.c->(預處理)->預處理過的.i原始檔->(編譯)->彙編檔案.s->(彙編)->目標檔案.o->(鏈結)->elf可執行程式。
(5)預處理用預處理器,編譯用編譯器,彙編用彙編器,鏈結用鏈結器,這些工具再加上額外的會用到的工具組成編譯工具鏈,gcc就是某個編譯工具鏈。
33.2.預處理的意義及程式設計應用
(1)編譯器本身的主要目的是編譯源**,將c的源**轉化成.s的彙編**;編譯器聚焦核心功能後,就剝離出某些非核心的功能到預處理器;預處理器幫編譯器做一些編譯前的雜事。
(2)預處理階段的主要工作:標頭檔案包含(#include);注釋;條件編譯(#if #elif #endif/#ifdef);巨集定義。
(3)配置gcc實現只預處理不編譯:gcc編譯時可以給某些引數來設定,譬如gcc xx.c -o xx即指定可執行程式的名稱,譬如gcc xx.c -c -o xx.o即指定只編譯不鏈結,譬如gcc -e xx.c -o xx.i即實現只預處理不編譯;通常情況下我們沒必要只預處理不編譯,但有時該技巧可幫助我們研究預處理過程,幫助debug程式。
(4)巨集定義被預處理後的現象:第1巨集定義語句本身不見了,則編譯器根本就不知道#define巨集;第2typedef重新命名語言沒變,則typedef是由編譯器來處理的。
33.3.標頭檔案包含
(1)#include<>和#include」「的區別:<>專門用來包含系統提供的標頭檔案;」「用來包含程式設計師自己寫的標頭檔案。
(2)<>包含標頭檔案:c語言編譯器只會到系統指定目錄(編譯器/作業系統配置的目錄,譬如在ubuntu中是/usr/include目錄,編譯器允許用-i來附加指定其它的包含路徑)去尋找該標頭檔案,若沒找到則提示該標頭檔案不存在,注意編譯器不會在當前目錄下尋找該標頭檔案。
(3)」「包含的標頭檔案,編譯器缺省會先在當前目錄下尋找該標頭檔案,若沒找到則再到系統指定目錄去尋找,若還沒找到則提示該標頭檔案不存在。
(4)總結:若包含系統指定的自帶的標頭檔案用<>;若標頭檔案是自己寫的存放在當前目錄下用」「;若標頭檔案是自己寫的但集中存放在某個目錄下將來在編譯器中用-i引數來尋找,此時標頭檔案包含用<>。
(5)標頭檔案包含的真實含義:在包含標頭檔案的那一行,將標頭檔案的內容原地展開替換包含標頭檔案的語句,替換過程在預處理中進行。
33.4.注釋和條件編譯
(1)注釋是給人看的,不是給編譯器看的;在預處理階段時預處理器會拿掉程式中所有的注釋語句,到了編譯器編譯階段程式中已經沒有注釋了。
(2)條件編譯即有時候我們希望程式有多種配置(源**編輯階段給每個配置開關編寫了相應的原始碼),在源**級別去修改配置開關來讓程式編譯出不同的效果。
(3)條件編譯中條件判斷標準:#ifdef和#if;#ifdef ***(其判斷標準是***該符號在本語句之前是否被定義,只要定義了則表示式成立(#define ***或#define *** 12或#define *** yyy));#if(條件表示式)(判定標準是()中的表示式是否為true還是flase,類似c中的if語句)。
33.5.巨集定義的規則和使用解析
(1)巨集定義的解析規則:在預處理階段由預處理器進行原封不動的替換;巨集定義替換會遞迴進行,直到替換出來的值本身不再是某個巨集為止。
(2)巨集定義格式:第1部分是#dedine,第2部分是巨集名,第3部分為剩下的全部東西。
(3)巨集可以帶引數(帶參巨集),帶參巨集的原理類似帶參函式,在定義帶參巨集時,每個引數在巨集體中引用時都必須加括號,最後整體再加括號,括號缺一不可。
(4)巨集定義示例1:max巨集,求2個數中較大的乙個:#define max(a, b) (((a)>(b)) ? (a) : (b))(使用三目運算子;括號的使用)。
(5)巨集定義示例2:sec_per_year巨集,用巨集定義表示一年中有多少秒:#define sec_per_year (365*24*60*60ul)(當某個數字直接出現在程式中時,其型別預設是int;一年有多少秒該數字超過了int型別儲存的範圍)。
33.6.帶參巨集和帶參函式的區別
(1)巨集定義是在預處理期間處理的,而函式是在編譯期間處理的;巨集定義最終是在呼叫巨集的地方把巨集體原地展開,而函式是在呼叫函式處跳轉到函式中去執行,執行完後再跳轉回來;巨集定義是原地(沒有呼叫開銷),而函式是跳轉執行再返回(有較大的呼叫開銷);則巨集的優勢是沒有呼叫和傳參開銷,當函式體很短可以用巨集定義來替代,以提高效率。
(2)巨集定義不會檢查引數的型別,返回值也不會附帶型別;而函式有明確的引數型別和返回值型別,當我們呼叫函式時編譯器會幫我們做引數的靜態型別檢查。
(3)巨集和函式各優劣:若**比較多用函式適合而且不影響效率(編譯器會進行靜態型別檢查);但若**較短就適合用帶參巨集(巨集不會檢查引數型別)。
33.7.內聯函式和inline關鍵字
(1)內聯函式通過在函式定義前加inline關鍵字實現。
(2)內聯函式本質上是函式,所以有函式的優點(編譯器做引數靜態型別檢查);其也有帶參巨集的優點(原地展開沒有呼叫開銷);則幾乎可認為內聯函式就是帶了引數靜態型別檢查的巨集。
(3)當函式內函式體很短的時候,我們既希望利用編譯器的引數型別檢查來排錯,而且還希望沒有呼叫開銷時,最適合使用內聯函式。
33.8.巨集定義來實現條件編譯
(1)程式有debug版本和release版本,區別就是編譯時有無定義debug巨集。
(2)#define、#undef、#ifdef舉例:
/* * 公司:***x
* 部落格:
* github:
* 專案:c語言預處理詳解
* 功能:通過預處理.i檔案解釋#define和typedef的本質區別。
*/#if 0
32.preprocess.i檔案內容:
# 1 "33.preprocess.c"
# 1 ""
# 1 ""
# 1 "33.preprocess.c"
# 13 "33.preprocess.c"
typedef char * pchar
int main(int argc, char **argv)
#endif
#define pchar char *
typedef char * pchar;
int main(int argc, char **argv)
33.preprocess_if_ifdef
/* * 公司:***x
* 部落格:
* github:
* 專案:c語言預處理詳解
* 功能:條件編譯#ifdef和#if是使用方法。
*/#include
#define num
#define pre 1
int main(int argc, char **argv)
33.macro
/* * 公司:***x
* 部落格:
* github:
* 專案:c語言預處理詳解
* 功能:演示巨集定義的各種用法。
*/// 演示巨集的遞迴替換
#define m 10
#define num m
// 巨集定義示例1:max巨集,求2個數中較大的乙個
#define max(a, b) ((a)>(b) ? (a) : (b))
// 巨集定義示例2:sec_per_year巨集,用巨集定義表示一年中有多少秒
#define sec_per_year (365*24*60*60ul)
// 巨集定義來實現條件編譯
#define debug
//#undef debug // 登出debug巨集,若前面有定義debug巨集則取消該巨集
#ifdef debug
#define debug(x) printf(x)
#else
#define debug(x)
#endif
#include
int main(int argc, char **argv)
C語言 預處理和巨集定義
今天分享一點c語言的預處理還有巨集相關的內容。目錄 預處理和巨集定義 一 乙個c程式的誕生 1 預編譯階段 c i 2 編譯階段 i s 3 彙編階段 s o 4 鏈結階段 該程式的所有.o exe 1 c語言中部分內建巨集 2 巨集的功能 1 使用巨集定義常量 2 使用巨集重新命名運算子 3 使用...
C語言 巨集定義,預處理巨集
巨集是學習任何語言所不可缺少的,優秀的巨集定義可以使得 變得很簡潔且高效,有效地提高程式設計效率。巨集是一種預處理指令,它提供了一種機制,可以用來替換源 中的字串,直譯器或編譯器在遇到巨集時會自動進行這一模式替換 c語言有簡單的巨集系統,由編譯器或彙編器的預處理器實現。c的巨集預處理器的工作只是簡單...
c語言預處理 巨集定義
個人筆記 巨集定義對於用c語言程式設計的人是經常用,這裡只講使用中需注意的問題點和方便的用法。1.巨集擴充套件中空格對擴充套件結果的影響 define a y a expanded y a x 被擴充套件為 a expanded x define a y a expanded y a x 被擴充套件...