本次將分爲四個章節來講,分別來講述一下gcc編譯背後的四個步驟。
第一部分:預處理。
相信大家對這一塊其實挺了解了,下面我就獻醜,以自己的理解再講一遍。
預處理是c語言從源代碼變成可執行程序的第一步,它包括頭文件的包含,宏定義擴展,條件編譯的選擇等。
首先介紹一下預處理的命令:一共有12條預處理的命令,都是以#開頭且每個預處理命令必須獨佔一行,它的結尾沒有分號,因爲它不是語句。
#if
#ifdef
#ifndef
#else
#elif
#endif
#define
#undef
#line
#error
#pragma
#include
1.#if 經常用於調試用
比如一段代碼我們暫時不用,那麼把這段代碼用#if括起來是一個很好的選擇,如
#if 0
...
#endif
2.#ifdef
判斷那個宏是否已經定義過了,如果定義了則進行相應的處理。
如
#ifdef TEST_SUPPORT
int test;
#endif
3.#ifndef 則相反,很多時候用於包含頭文件,避免重複包含與重複定義。
如test.h
#ifndef TESTDEC_POSTPROCESSCODEBLOCKMESSAGE_H
#define TESTDEC_POSTPROCESSCODEBLOCKMESSAGE_H
void testDec_PostProcessCodeBlockMessage(void **state);
#endif
這樣的話,不管這個test.h被包含多少次,這個函數都只會被申明一次,避免了重複申明。
4.#else則於c語言中的else 類似,#elif 則與c語言的else if 類似。
5.#define 最常見了,我就不詳細說明了,主要包含頭文件用的。
6.#undef 取消宏定義
7.#line 改變__LINE__和__FILE__的內容
用法:#line number "filename"
大家可以自己測試一下。
8.#pragma
它的作用是設定編譯器的狀態或者是指示編譯器完成一些特定的動作,如
#pragma message(“消息文本”)
當編譯器遇到這條指令時就在編譯輸出窗口中將消息文本打印出來。
這條命令比較複雜,如果大家有興趣,可以自己再去查看一下。
有兩個預處理操作符:#和##,它們可以在#define中使用。
操作符#通常稱爲字符串化的操作符,它把其後的串變成用雙引號包圍的串。例如:
#include <stdio.h>
#define mkstr(s) #s
int main(void)
{
Printf(mkstr(I like C));
Return 0;
}
預處理程序把以下的語句:
Printf(mkstr(I like C));
變成
Printf(“I like C”);
操作符##把兩個標記拼在一起,形成一個新標記。例如:
#include <stdio.h>
#define concat(a,a) a##b
int main(void)
{
Int xy = 10;
Printf(“%d”,concat(x,y));
Return 0;
}
預處理程序把以下語句:
Printf(“%d”,concat(x,y));
變成
Printf(“%d”,xy);
操作符#和##主要作用是允許預處理程序對付某些特殊情況,多數程序中並不需要。
預定義宏
C規範了5個固有的預定義宏,它們是:
__LINE__
__FILE__
__DATE__
__TIME__
__STDC__
__LINE__和__FILE__包含正在編譯的程序的行號和文件名。
__DATE__和內容形如month/day/year(月/日/年)的串,代表源文件翻譯成目標碼的日期。
__TIME__中的串代表源代碼編譯成目標碼的時間,形如hour:minute:second(時:分:秒)
如果__STDC__的內容是十進制常數1,則表示編譯程序的實現符合標準C。
打印出預處理之後的結果:gcc -E hello.c
在命令行定義宏:gcc -Dmacro hello.c