【C 语言】编译过程 分析 ( 预处理 | 编译 | 汇编 | 链接 | 宏定义 | 条件编译 | 编译器指示字 )
相關(guān)文章鏈接 :
1.【嵌入式開(kāi)發(fā)】C語(yǔ)言 指針數(shù)組 多維數(shù)組
2.【嵌入式開(kāi)發(fā)】C語(yǔ)言 命令行參數(shù) 函數(shù)指針 gdb調(diào)試
3.【嵌入式開(kāi)發(fā)】C語(yǔ)言 結(jié)構(gòu)體相關(guān) 的 函數(shù) 指針 數(shù)組
4.【嵌入式開(kāi)發(fā)】gcc 學(xué)習(xí)筆記(一) - 編譯C程序 及 編譯過(guò)程
5.【C語(yǔ)言】 C 語(yǔ)言 關(guān)鍵字分析 ( 屬性關(guān)鍵字 | 常量關(guān)鍵字 | 結(jié)構(gòu)體關(guān)鍵字 | 聯(lián)合體關(guān)鍵字 | 枚舉關(guān)鍵字 | 命名關(guān)鍵字 | 雜項(xiàng)關(guān)鍵字)
- 一 編譯過(guò)程
- 編譯過(guò)程圖解
- 步驟1 編譯預(yù)處理
- 1 預(yù)編譯處理內(nèi)容
- 2 預(yù)編譯處理代碼示例 驗(yàn)證 include define 注釋 處理過(guò)程
- 步驟2 編譯
- 1 編譯 中的操作
- 2 編譯 示例
- 步驟3 匯編
- 1 匯編 中的操作
- 2 匯編 示例
- 單步編譯 示例 預(yù)處理 編譯 匯編 鏈接
- 鏈接器
- 1 鏈接器簡(jiǎn)介
- 2 靜態(tài)鏈接
- 3 動(dòng)態(tài)鏈接
- 二 宏定義 使用詳解
- 宏定義 常量
- 宏表達(dá)式
- 1 宏表達(dá)式簡(jiǎn)介
- 2 宏表達(dá)式 代碼示例
- 3 宏表達(dá)式 與 函數(shù)對(duì)比
- 宏表達(dá)式 或 宏常量 作用域限制
- 1 宏定義 沒(méi)有作用域限制
- 2 undef 限制宏定義 作用域
- 內(nèi)置宏
- 1 內(nèi)置宏 簡(jiǎn)介
- 2 日志宏 代碼示例
- 三 條件編譯
- 基本概念
- 1 條件編譯簡(jiǎn)介
- 2 條件編譯 示例 簡(jiǎn)單的條件編譯 修改代碼實(shí)現(xiàn)
- 3 條件編譯 示例 使用命令行生成宏定義控制條件編譯 不修改代碼實(shí)現(xiàn)
- include 間接包含
- 1 間接包含 介紹
- 2 include 間接包含 示例 錯(cuò)誤示例 解決方案
- 2 include 間接包含 示例 正確的處理方法
- 條件編譯控制示例 編譯不同產(chǎn)品 控制開(kāi)發(fā)版本和發(fā)布版本編譯
- 基本概念
- 四 編譯指示字 error line
- error 編譯指示字
- 1 error 簡(jiǎn)介
- 2 error warning 代碼示例
- line 編譯指示字
- 1 line 簡(jiǎn)介
- 2 line 代碼示例
- pragma 編譯器指示字
- 1 pragma 簡(jiǎn)介
- 2 pragma message 參數(shù)
- 3 pragma pack 參數(shù)
- error 編譯指示字
- 五 運(yùn)算符
- 運(yùn)算符
- 運(yùn)算符
一. 編譯過(guò)程
1. 編譯過(guò)程圖解
編譯過(guò)程 :
編譯過(guò)程 : 預(yù)處理 -> 編譯 -> 匯編 -> 鏈接;
1. 編譯預(yù)處理 : 產(chǎn)生 .i 后綴的預(yù)處理文件;
2. 編譯操作 : 產(chǎn)生 .s 后綴的匯編文件;
3. 匯編操作 : 產(chǎn)生 .o 后綴的機(jī)器碼二進(jìn)制文件;
4. 鏈接操作 : 產(chǎn)生可執(zhí)行文件 ;
2. 步驟1 : 編譯預(yù)處理
(1) 預(yù)編譯處理內(nèi)容
預(yù)編譯操作 :
- 1.處理注釋 : 刪除所有的注釋, 使用空格取代注釋內(nèi)容;
- 2.處理宏定義 : 刪除所有的 #define 宏定義, 替換 代碼中 宏定義 對(duì)應(yīng) 的 內(nèi)容;
- 3.處理?xiàng)l件編譯指令 : 處理 #if, #else, #ifdef, #elif, #endif 等條件編譯指令 ;
- 4.處理#include : 處理 #include, 將被包含的文件拷貝到代碼中.
- 5.處理#pragma : 編譯器使用的 # program 指令 保留下來(lái), 這個(gè)指令是 C 代碼 到 匯編 代碼 進(jìn)行 處理的指示字.
預(yù)處理指令 : gcc -E test_1.c -o test_1.i
(2) 預(yù)編譯處理代碼示例 (驗(yàn)證 #include | #define | 注釋 處理過(guò)程)
編譯預(yù)處理示例 :
- 1.代碼示例 :
- 2.預(yù)處理 : 使用 gcc -E test_1.c -o test_1.i 命令進(jìn)行預(yù)處理, 預(yù)處理完之后生成 test_1.i 文件.
- 3.查看預(yù)處理文件 : 查看 test_1.i 文件 ;
test_1.i 出現(xiàn)了800多行的預(yù)處理文件, 原因是 #include < stdio.h >, 將 stdio.h 的文件拷貝了進(jìn)來(lái), 如果去掉了 #include 聲明, 那么預(yù)處理文件就很小.
刪除了 # include 代碼 :
- 1.代碼示例 :
- 2.預(yù)處理 : 使用 gcc -E test_1.c -o test_1.i 命令進(jìn)行預(yù)處理;
- 3.查看預(yù)處理文件 :
如果沒(méi)有了 #include 聲明, 那么預(yù)編譯后的文件會(huì)大大減少.
3. 步驟2 : 編譯
(1) 編譯 中的操作
編譯 步驟中的操作 :
- 1.詞法分析 : 分析 關(guān)鍵字, 標(biāo)識(shí)符, 立即數(shù) 的合法性;
- 2.語(yǔ)法分析 : 檢查 代碼 是否遵循 C 語(yǔ)言語(yǔ)法規(guī)則;
- 3.語(yǔ)義分析 : 分析表達(dá)式是否合法;
編譯 需要的指令 : gcc -S test_1.c -o test_1.s ;
(2) 編譯 示例
編譯 示例 :
- 1.代碼內(nèi)容 :
- 2.執(zhí)行編譯內(nèi)容 : 執(zhí)行 gcc -S test_1.c -o test_1.s 命令, 得到 test_1.o 文件.
- 3.查看編譯結(jié)果 : 查看生成的 test_1.s 文件, 是一個(gè)匯編文件 ;
4. 步驟3 : 匯編
(1) 匯編 中的操作
匯編 操作 :
- 1.執(zhí)行者 : 匯編器;
- 2.操作 : 使用 匯編器 將 匯編代碼, 轉(zhuǎn)化為 機(jī)器可執(zhí)行的 機(jī)器碼.
匯編 命令 : gcc -c test_1.s -o test_1.o ;
每條匯編指令都對(duì)應(yīng)著指定的機(jī)器碼 .
(2) 匯編 示例
匯編 過(guò)程示例 :
- 1.代碼內(nèi)容 :
- 2.執(zhí)行編譯內(nèi)容 : 執(zhí)行 gcc -S test_1.c -o test_1.s 命令, 得到 test_1.o 文件.
- 3.查看編譯結(jié)果 : 查看生成的 test_1.s 文件, 是一個(gè)匯編文件 ;
- 4.匯編 : 執(zhí)行 gcc -c test_1.s -o test_1.o 命令, 得到 test_1.o 文件 ;
5. 單步編譯 示例 ( 預(yù)處理 | 編譯 | 匯編 | 鏈接)
單步編譯示例 :
- 1.代碼結(jié)構(gòu): 頭文件 test_1.h, 代碼文件 test_1.c ;
- 2.頭文件代碼 :
- 3.主要邏輯代碼 :
- 4.進(jìn)行預(yù)編譯 : 執(zhí)行 gcc -E test_1.c -o test_1.i 指令, 會(huì)生成 test_1.i 文件;
5.預(yù)編譯目標(biāo)結(jié)果文件 : test_1.i 文件;
分析 test_1.i 文件:
- 拷貝包含文件 : #include “test_1.h” 直接將 test_1.h 中的內(nèi)容拷貝到 test_1.i 文件中 , 8 ~ 13 行是 test_1.h 文件拷貝到 test_1.i 中的內(nèi)容.
- 編譯器注釋說(shuō)明 : #部分不管, 是編譯器生成的說(shuō)明 ;
- 處理注釋 : 將注釋使用空格替換, test_1.i 中 8 ~ 12 行 5 行是空格, 第 8, 9, 12 行對(duì)應(yīng)著 test_1.h 中的注釋, 第十行對(duì)應(yīng)著 test_1.h 中的宏定義, 第11行對(duì)應(yīng)著空白行.
- 替換宏定義 : 將宏定義的位置替換到代碼中, 宏定義行使用空格替代 , 其中 8 ~ 12 行空行, 第10行就是宏定義刪除后的空行 ; 代碼中 MIN(a,b) 的位置 被 (((a)>(b)) ? (b) : (a)) 替換, SMALL 被 666 替換, BIG 被 888 替換.
6.編譯 產(chǎn)生 匯編文件 : 執(zhí)行 gcc -S test_1.i -o test_1.s 命令 , 生成了 test_1.s 文件,
- 7.將匯編文件轉(zhuǎn)為機(jī)器碼 : 執(zhí)行指令 gcc -C test_1.s -o test_1.o , 生成 test_1.o 文件 , 生成的機(jī)器碼是二進(jìn)制的文件, 使用 文本編輯器打不開(kāi), 在 Windows 中使用 010Editer 打開(kāi)查看二進(jìn)制內(nèi)容 ;
6. 鏈接器
(1) 鏈接器簡(jiǎn)介
鏈接器簡(jiǎn)介 :
- 1.銜接模塊引用 : 軟件各個(gè)模塊之前會(huì)相互調(diào)用, 鏈接器就是處理這些相互引用的位置之間的銜接 .
鏈接器 模塊拼裝 :
- 1.普通鏈接 : 運(yùn)行時(shí), 將所有的代碼庫(kù) .o 文件, 一次性拷貝到內(nèi)存中, 如果運(yùn)行多個(gè)副本, 那么相同的代碼庫(kù)會(huì)各自占用一部分內(nèi)存, 這些內(nèi)存中存儲(chǔ)的東西是一樣的.
- 2.靜態(tài)鏈接 : 出于節(jié)省內(nèi)存的考慮, 我們可以將相同的代碼封裝到靜態(tài)庫(kù)中, 那么多個(gè)副本同時(shí)運(yùn)行時(shí), 只加載一份靜態(tài)庫(kù)即可, 這樣相對(duì)于普通鏈接來(lái)說(shuō)節(jié)省內(nèi)存, 內(nèi)存消耗比動(dòng)態(tài)鏈接要多.
- 3.動(dòng)態(tài)鏈接 : 運(yùn)行開(kāi)始的時(shí)候只加載必要的模塊, 當(dāng)開(kāi)始調(diào)用某一動(dòng)態(tài)鏈接庫(kù)時(shí), 才去尋找并加載動(dòng)態(tài)鏈接庫(kù)到內(nèi)存中, 節(jié)省內(nèi)存, 但是運(yùn)行效率慢.
(2) 靜態(tài)鏈接
靜態(tài)鏈接 :
- 1.加載時(shí)機(jī) : 靜態(tài)庫(kù)中的代碼, 在運(yùn)行開(kāi)始前就全部加載到內(nèi)存中, 這與動(dòng)態(tài)鏈接中
- 2.加載份數(shù) : 在內(nèi)存中, 靜態(tài)庫(kù)只加載一次, 第一次執(zhí)行程序用到靜態(tài)庫(kù)時(shí), 加載靜態(tài)庫(kù), 當(dāng)再次運(yùn)行時(shí), 繼續(xù)復(fù)用第一次加載靜態(tài)庫(kù), 這樣比較節(jié)省內(nèi)存.
靜態(tài)鏈接圖示 :
當(dāng)運(yùn)行2個(gè)a.out 時(shí), 對(duì)于靜態(tài)庫(kù) test_3.a 只需要加載 1 次, 但是對(duì)于 test_1.o 和 test_2.o 需要各自加載一次.
靜態(tài)庫(kù)鏈接內(nèi)存圖 :
(3) 動(dòng)態(tài)鏈接
動(dòng)態(tài)鏈接 :
- 1.加載時(shí)機(jī) : 程序運(yùn)行時(shí)不加載動(dòng)態(tài)鏈接庫(kù), 程序執(zhí)行調(diào)用動(dòng)態(tài)鏈接庫(kù)函數(shù)的時(shí)候, 才動(dòng)態(tài)的加載動(dòng)態(tài)鏈接庫(kù) .
- 2.執(zhí)行效率 : 動(dòng)態(tài)鏈接效率 比 靜態(tài)鏈接要低, 因?yàn)槠鋱?zhí)行的時(shí)候, 需要搜索并加載動(dòng)態(tài)鏈接, 這樣會(huì)消耗一定的性能 ;
動(dòng)態(tài)鏈接圖解 :
二. 宏定義 使用詳解
1. 宏定義 常量
宏定義 常量 :
- 1.#define 定義 常量, 只是進(jìn)行簡(jiǎn)單的代碼替換.
- 2.#define 定義的不是真正意義的常量, 只是進(jìn)行簡(jiǎn)單的代碼替換, 下面代碼中的內(nèi)容都是合法的.
2. 宏表達(dá)式
(1) 宏表達(dá)式簡(jiǎn)介
宏表達(dá)式 #define :
- 1.本質(zhì)不是函數(shù) : 使用 # define 表達(dá)式, 有函數(shù)的假象, 但是其并不是函數(shù);
- 2.功能強(qiáng)大, 但容易出錯(cuò) : 某些用法 生硬的替換代碼 可能導(dǎo)致出現(xiàn) 出錯(cuò)的情況.
- 3.宏使用示例 :
- 4.執(zhí)行結(jié)果 :
(2) 宏表達(dá)式 代碼示例
宏替換代碼示例 :
- 1.原始 C 代碼 (含有宏定義) :
- 2.預(yù)處理宏替換結(jié)果 : test_1.c 進(jìn)行預(yù)處理后的 test_1.i, 使用 gcc -E test_1.c -o test_1.i 命令進(jìn)行預(yù)處理;
- 3.執(zhí)行結(jié)果 :
(3) 宏表達(dá)式 與 函數(shù)對(duì)比
宏表達(dá)式 與 函數(shù)對(duì)比 :
- 1.對(duì)編譯器透明 : 宏表達(dá)式在預(yù)編譯階段進(jìn)行替換處理, 編譯器不知道宏的存在;
- 2.運(yùn)算方面 : 宏替換不進(jìn)行任何運(yùn)算, 沒(méi)有實(shí)參形參的概念, 全部都是機(jī)械的替換, 宏表達(dá)式參數(shù)可以使變量,也可以是類型;
- 3.調(diào)用開(kāi)銷方面 : 宏表達(dá)式不消耗任何調(diào)用開(kāi)銷, 沒(méi)有函數(shù)調(diào)用開(kāi)銷, 其在預(yù)處理階段就被替換了;
- 4.關(guān)于遞歸 : 宏表達(dá)式不能使用遞歸定義宏;
遞歸代碼示例 (錯(cuò)誤示例) :
- 1.宏遞歸代碼示例 :
- 2.預(yù)編譯結(jié)果 : 宏替換后的結(jié)果 ;
- 3.編譯結(jié)果 : 編譯報(bào)錯(cuò), 提示沒(méi)有定義 FAC() 方法 ;
3. 宏表達(dá)式 或 宏常量 作用域限制
(1) 宏定義 沒(méi)有作用域限制
宏定義作用域限制 :
- 1.宏定義位置 : 宏定義可以再程序的任意位置定義, 甚至是函數(shù)內(nèi)部;
- 2.宏定義使用位置 : 宏定義可以再任何位置使用;
- 3.代碼示例 :
- 4.預(yù)編譯結(jié)果 :
- 5.執(zhí)行結(jié)果 :
(2) #undef 限制宏定義 作用域
限制宏定義作用域 #undef 用法 :
- 1.使用方法 : 定義宏 #define MIN 100 之后, 可以使用 #undef MIN 限制其作用范圍, 只能在 #define 和 #undef 之間使用該宏, 在 #undef 之后就不可使用該宏了;
- 2.使用示例 (錯(cuò)誤示例) :
- 3.預(yù)編譯結(jié)果 :
- 4.編譯報(bào)錯(cuò)內(nèi)容 :
4. 內(nèi)置宏
(1) 內(nèi)置宏 簡(jiǎn)介
內(nèi)置宏舉例 :
- 1.__FILE__ : 代表被編譯的文件名稱 ;
- 2.__LINE__ : 代表當(dāng)前的行號(hào) ;
- 3.__DATE__ : 代表當(dāng)前的日期 ;
- 4.__TIME__ : 代表編譯時(shí)的時(shí)間 ;
- 5.__STDC__ : 編譯器是否遵循 標(biāo)準(zhǔn) C 規(guī)范 ;
(2) 日志宏 代碼示例
使用宏定義日志打印 :
- 1.代碼示例 :
- 2.運(yùn)行結(jié)果 :
日志宏 : 打印日志的同時(shí), 打印當(dāng)前的文件名稱, 代碼行號(hào), 當(dāng)前運(yùn)行時(shí)間 ;
三. 條件編譯
1. 基本概念
(1) 條件編譯簡(jiǎn)介
條件編譯指令 :
- 1.指令 : #if , #ifdef, #ifndef, #else, #endif 等 ;
- 2.用法 : 與 if else 等用法類似, 具體查看下面的示例, 但是 #if, #else, #endif 是預(yù)編譯階段被預(yù)編譯處理的, if else 是在編譯階段, 被編譯器處理, 是要被編譯到目標(biāo)代碼中的 ;
- 3.作用 : 條件編譯指令是預(yù)編譯指令, 控制某段代碼是否被編譯, 可以按照不同的條件選擇性編譯指定的代碼段, 選擇性的忽略某段代碼, 用以編譯出不同功能的可執(zhí)行目標(biāo)文件 ;
條件編譯的應(yīng)用環(huán)境 :
- 1.軟件分支維護(hù) : 維護(hù)一個(gè)軟件的不同分支, 控制軟件分支編譯;
- 2.區(qū)分版本 : 區(qū)分軟件調(diào)試版本 和 正式上線的版本, 開(kāi)發(fā)版本肯定有很多調(diào)試信息, 正式版沒(méi)有冗余的信息;
條件編譯 注意點(diǎn) :
- 1.命令行定義宏 : 可以使用 gcc -D 選項(xiàng)來(lái)定義宏, 如 gcc -DDEBUG test_1.c 等價(jià)于 #define DEBUG, gcc -DMIN=1 test_1.c 等價(jià)于 #define MIN 1 語(yǔ)句 ;
- 2.條件編譯處理頭文件包含問(wèn)題 : #include 會(huì)出現(xiàn)多重嵌套問(wèn)題, 使用 #ifndef _HEAD_H_ | #define _HEAD_H_ | #endif 可以解決頭文件多次引用的問(wèn)題 ;
- 3.使用一套代碼維護(hù)不同產(chǎn)品 : 開(kāi)發(fā)中, 可以條件編譯來(lái)維護(hù)一套代碼, 編譯出不同的產(chǎn)品 ;
- 4.開(kāi)發(fā)板和正式版區(qū)分 : 使用條件編譯可以區(qū)分產(chǎn)品的開(kāi)發(fā)調(diào)試版本 和 正式發(fā)布版本 ;
(2) 條件編譯 示例 (簡(jiǎn)單的條件編譯 | 修改代碼實(shí)現(xiàn))
通過(guò)修改代碼 控制 條件編譯 代碼示例 :
- 1.代碼1 :
- 2.條件編譯 預(yù)編譯結(jié)果 : 使用 gcc -E test_1.c -o test_1.i 命令進(jìn)行預(yù)編譯 ;
- 3.執(zhí)行結(jié)果 :
修改代碼后 刪除宏定義 :
- 1.代碼2 :
- 2.條件編譯 預(yù)編譯結(jié)果 :
- 3.執(zhí)行結(jié)果 :
上述兩個(gè)例子, 主要是通過(guò)在代碼中定義 宏常量, 來(lái)控制條件編譯中, 哪些語(yǔ)句需要編譯, 哪些語(yǔ)句在預(yù)編譯階段就要?jiǎng)h除 ;
(3) 條件編譯 示例 ( 使用命令行生成宏定義控制條件編譯 | 不修改代碼實(shí)現(xiàn))
使用命令行定義宏 從而控制條件編譯, 代碼不變 :
- 1.代碼 :
- 2.命令行1 : 使用命令行命令 gcc -DC=1 -E test_1.c -o test_1.i, 該命令 等價(jià)于 定義 宏 #define C 1, 下面是預(yù)編譯結(jié)果 和 執(zhí)行結(jié)果 ;
- 3.命令行2 : 使用命令行命令*gcc -DC=2 -E test_1.c -o test_1.i, 該命令等價(jià)于 定義宏 #define C 2, 下面是預(yù)編譯結(jié)果 和 執(zhí)行結(jié)果 ;
2. #include 間接包含
(1) 間接包含 介紹
#include 間接包含 :
- 1.#include作用 : #include 作用是 單純的 將 文件內(nèi)容 嵌入 到 當(dāng)前的 文件 ;
- 2.間接包含 : #include 會(huì)有間接包含的情況, 如 包含的 文件中, 有重復(fù)包含的情況 ;
(2) #include 間接包含 示例 ( 錯(cuò)誤示例 | 解決方案 )
間接包含 結(jié)構(gòu)圖示 : test_1.c 文件包含 三個(gè)頭文件, test_1.h 包含的 test_2.h 頭文件 與 test_1.c 包含的該頭文件相同, 同一個(gè)頭文件被導(dǎo)入了2次, 因此編譯時(shí)會(huì)報(bào)錯(cuò);
間接包含 代碼示例 :
- 1.test_1.c 代碼 :
- 2.test_1.h 頭文件代碼 :
- 3.test_2.h 頭文件代碼 :
- 4.預(yù)編譯結(jié)果 : 同時(shí)拷貝了兩份 int test_2_variable = 666; 語(yǔ)句, 如果進(jìn)入編譯階段, 肯定是重復(fù)定義變量 ;
- 5.編譯報(bào)錯(cuò)內(nèi)容 :
間接包含 簡(jiǎn)單解決方案 : 下面的代碼與上面的唯一區(qū)別是, test_1.c 中注釋掉了 #include “test_2.h” 語(yǔ)句.
- 1.test_1.c 代碼 :
- 2.test_1.h 頭文件代碼 :
- 3.test_2.h 頭文件代碼 :
- 4.執(zhí)行結(jié)果 :
(2) #include 間接包含 示例 ( 正確的處理方法 )
使用 #ifndef , #define 和 #endif 語(yǔ)句處理頭文件包含情況 :
- 1.主代碼 test_1.c :
- 2.頭文件1 test_1.h :
- 3.頭文件2 test_2.h :
- 4.預(yù)編譯結(jié)果 :
- 5.代碼執(zhí)行結(jié)果 :
3. 條件編譯控制示例 ( 編譯不同產(chǎn)品 | 控制開(kāi)發(fā)版本和發(fā)布版本編譯)
條件編譯控制代碼示例 :
- 1.代碼 :
- 2.編譯產(chǎn)品1代碼開(kāi)發(fā)版本(debug)并執(zhí)行 : 產(chǎn)品1 的 debug 版本需要定義 DEBUG宏 和 PRODUCT_1 宏, 使用命令 gcc -DDEBUG -DPRODUCT_1 test_1.c 進(jìn)行編譯即可 ;
- 3.編譯產(chǎn)品2代碼開(kāi)發(fā)版本(bebug)并執(zhí)行 : 產(chǎn)品2 debug 版本, 不需要定義 PRODUCT_1 宏, 但是需要定義 DEBUG 宏, 使用命令 gcc -DDEBUG test_1.c 進(jìn)行編譯即可;
- 4.編譯產(chǎn)品1代碼發(fā)布版本(release)并執(zhí)行 : 產(chǎn)品1的release 版本, 不定義 DEBUG 宏, 但是需要定義 PRODUCT_1 宏, 使用命令 gcc -DPRODUCT_1 test_1.c 即可 ;
- 5.編譯產(chǎn)品2代碼發(fā)布版本(release)并執(zhí)行 : 產(chǎn)品2的release版本, 只需要不定義 DEBUG宏 和 PRODUCT_1宏即可, 使用 gcc test_1.c 命令 ;
四. 編譯指示字 ( #error | #line )
1. #error 編譯指示字
(1) #error 簡(jiǎn)介
#error簡(jiǎn)介 :
- 1.#error 作用 : #error 編譯指示字 用于生成 編譯錯(cuò)誤信息, 立即終止編譯 ; 這個(gè)編譯錯(cuò)誤是程序員自定義的編譯錯(cuò)誤信息;
- 2.#error 用法 : #error error_message, 這個(gè) error_message 是字符串, 不需要使用 “” 包起來(lái);
#warning 也是編譯指示字, 用于在編譯時(shí)生成警告信息, 但是編譯的過(guò)程不會(huì)終止, 會(huì)繼續(xù)編譯下去 ;
(2) #error #warning 代碼示例
#error #warning 代碼示例 :
- 1.代碼 :
- 2.編譯結(jié)果( 命令行中定義指定的宏 ) : 使用 gcc -DMAX test_1.c 命令編譯, 此處定義了 MAX 宏, 編譯執(zhí)行成功.
- 3.編譯結(jié)果( 命令行中不定義指定的宏 ) : 使用 gcc test_1.c 命令編譯, 此處沒(méi)有命定義 MAX 宏, 編譯時(shí)報(bào)錯(cuò).
- 4.單步操作預(yù)編譯結(jié)果 (定義宏) : 使用 gcc -DMAX -E test_1.c -o test_1.i 命令, 進(jìn)行預(yù)編譯, 結(jié)果預(yù)編譯成功, 查看預(yù)編譯生成的 test_1.i 文件 ;
- 5.單步操作預(yù)編譯結(jié)果 (不定義宏) : 使用 gcc -E test_1.c -o test_1.i 命令, 進(jìn)行預(yù)編譯, 結(jié)果預(yù)編譯也停止了, 沒(méi)有生成 test_1.i 文件, 因此#error 和 #warning 是在預(yù)編譯階段進(jìn)行處理的 ;
2. #line 編譯指示字
(1) #line 簡(jiǎn)介
#line 簡(jiǎn)介 :
- 1.#line 作用 : 用于修改當(dāng)前的 __LINE__ 和 __FILE__ 的內(nèi)置宏 ;
- 2.#line 用法 : #line 行號(hào) 文件名 , 即將當(dāng)前的 內(nèi)置宏 __LINE__ 設(shè)置為 行號(hào), __FILE__ 設(shè)置為 文件名 ;
- 3.使用環(huán)境 : 調(diào)試代碼時(shí), 編譯 查錯(cuò) 的時(shí)候, 設(shè)置自己關(guān)心的代碼, 這是很古老的調(diào)試方法, 該技術(shù)已經(jīng)被淘汰 ;
(2) #line 代碼示例
#line 使用代碼示例 :
- 1.代碼示例 :
- 2.執(zhí)行結(jié)果 :
3. #pragma 編譯器指示字
(1) #pragma 簡(jiǎn)介
#pragma 編譯器指示字 簡(jiǎn)介 :
- 1.#pragma 作用 : 該 編譯器指示字 指示編譯器完成一些特定的操作 ;
- 2.編譯器特有, 不可移植 : #pragma 的很多指示字 參數(shù), 這些參數(shù) 都是編譯器 特有的, 編譯器指示字 在 編譯器之間不通用, 不可移植 ;
- 3.忽略不識(shí)別的指令 : 如果編譯器不支持某個(gè) #pragma 指令 參數(shù), 預(yù)處理器會(huì)忽略這條指令, 并將其刪除;
- 4.相同指令 操作不同 : 每個(gè)編譯器對(duì) #pragma 指令定義都不同, 可能存在兩個(gè)相同的 #pragma 指令在不同的編譯器中執(zhí)行不同的操作 ;
#pragma 用法 : #pragma 參數(shù)
(2) #pragma message 參數(shù)
#pragma message 參數(shù) :
- 1.作用 : 編譯器編譯時(shí)將編譯信息輸出到窗口中 ;
- 2.與 #error 編譯器指示字對(duì)比 : #error只是在出現(xiàn)錯(cuò)誤的時(shí)候, 將錯(cuò)誤信息輸出出來(lái), #pragma message 是單純的額將所有信息輸出出來(lái), 不管對(duì)錯(cuò) ;
- 3.代碼示例 :
- 4.不定義宏進(jìn)行編譯 : 如果既不定義 MAX 宏, 也不定義 MIN 宏, 那么直接執(zhí)行 #error 報(bào)錯(cuò);
- 5.定義 MAX 宏并執(zhí)行 :
(3) #pragma pack 參數(shù)
內(nèi)存對(duì)齊 簡(jiǎn)介 :
- 1.內(nèi)存對(duì)齊概念 : 不同類型的數(shù)據(jù)存放在內(nèi)存中, 但是其存放順序不是順序存放, 而是按照內(nèi)存對(duì)齊規(guī)則進(jìn)行排列 ;
- 2.內(nèi)存對(duì)齊原因 : ① 出于性能考慮 , CPU 讀取內(nèi)存不是想讀取多少就讀取多少, 其內(nèi)存讀取是分塊讀取的, 塊大小只能是 2 的n次方字節(jié), 如 1, 2, 4, 8, 16, 字節(jié), 如果被讀取的數(shù)據(jù)沒(méi)有對(duì)齊, 那么需要多次讀取, 這樣性能就降低了 ; ② 硬件平臺(tái)限制 : 可能存在某些硬件只能讀取偶數(shù)地址, 一旦讀取奇數(shù)地址, 直接出現(xiàn)硬件異常導(dǎo)致程序掛掉;
- 3.內(nèi)存對(duì)齊設(shè)置不能高于編譯器默認(rèn)對(duì)齊字節(jié)數(shù) : GCC 編譯器默認(rèn)支持 4 字節(jié)對(duì)齊, 那么使用 #pragma pack() 只能設(shè)置 1字節(jié) 或 2 字節(jié), 4 字節(jié)支持, 不能設(shè)置其它支持方式; 如果編譯器默認(rèn)是 8 字節(jié), 那么只能設(shè)置 1, 2, 4, 8 四種字節(jié)對(duì)齊方式, 只能往低設(shè)置, 不能高于編譯器默認(rèn)的對(duì)齊字節(jié)數(shù);
結(jié)構(gòu)體 struct 占用內(nèi)存計(jì)算方式 :
- 1.第一個(gè)起始位置 : 第一個(gè)元素 在 第一個(gè)位置, 從 偏移量 0 開(kāi)始;
- 2.對(duì)齊參數(shù) : 對(duì)齊數(shù) 和 類型大小 之間較小的一個(gè)是 對(duì)齊參數(shù) ; 這里要注意 如果有結(jié)構(gòu)體元素, 那么該結(jié)構(gòu)體元素的對(duì)齊參數(shù)就是 結(jié)構(gòu)體中的最大對(duì)齊參數(shù);
- 3.從第二個(gè)開(kāi)始的起始位置 : 除第一個(gè)之外的起始位置, 都必須整除對(duì)應(yīng)的 對(duì)齊參數(shù) ;
- 4.最終計(jì)算大小要整除所有的對(duì)齊參數(shù) ;
- 5.注意結(jié)構(gòu)體中有結(jié)構(gòu)體元素 : 結(jié)構(gòu)體中的結(jié)構(gòu)體元素對(duì)齊數(shù)是結(jié)構(gòu)體元素中的最大對(duì)齊數(shù) ;
- 5.代碼示例 :
- 6.執(zhí)行結(jié)果 :
五. #運(yùn)算符
1. #運(yùn)算符
#運(yùn)算符作用 :
- 1.將宏參數(shù)轉(zhuǎn)為字符串 : # 運(yùn)算符 可以在 編譯 的 預(yù)編譯 階段, 將宏定義中的參數(shù), 轉(zhuǎn)化為 字符串 ;
- 2.預(yù)處理器開(kāi)始符號(hào) : 預(yù)處理器 開(kāi)始處理 的符號(hào) ;
- 3.#運(yùn)算符代碼示例 :
- 4.預(yù)編譯結(jié)果 : 使用 “gcc -E test_1.c -o test_1.i” 指令進(jìn)行預(yù)編譯, 可以看到 # 運(yùn)算符將 宏定義參數(shù)轉(zhuǎn)為字符串 ;
# 運(yùn)算符 將 Hello 666 main 轉(zhuǎn)為 “Hello” “666” “main” 字符串, 將 square 轉(zhuǎn)為了 “square” 字符串 ;
- 5.編譯執(zhí)行最終結(jié)果 :
2. ##運(yùn)算符
## 運(yùn)算符作用 :
- 1.作用 : 在預(yù)編譯階段粘連兩個(gè)符號(hào) ;
- 2.代碼示例 :
- 3.預(yù)編譯結(jié)果 : 使用 “gcc -E test_1.c -o test_1.i” 命令, 執(zhí)行預(yù)編譯 ;
- 4.最終編譯執(zhí)行結(jié)果 :
總結(jié)
以上是生活随笔為你收集整理的【C 语言】编译过程 分析 ( 预处理 | 编译 | 汇编 | 链接 | 宏定义 | 条件编译 | 编译器指示字 )的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 【Java 语言】Java 多线程 一
- 下一篇: 【C 语言】指针 与 数组 ( 指针 |