本節知識點:
1.編譯過程的簡介:
預編譯:
a.處理所有的註釋,以空格代替。
b.將所以#define刪除,並展開所有的宏定義,字符串替換。
c.處理條件編譯指令#if,#ifdef,#elif,#else,#endif
d.處理#include,並展開被包含的文件,把頭文件中的聲明,全部拷貝到文件中。
e.保留編譯器需要使用的#pragma指令、
怎麼樣觀察這些變化呢?最好的方法就是在GCC中,輸入預處理指令,可以看看不同文件經過預處理後變成什麼樣了,預處理指令:gcc -E file.c -o file.i 注意:-C -E一起使用是預編譯的時候保留註釋。
編譯:
a.對預處理文件進行一系列詞法分析,語法分析和語義分析
詞法分析:主要分析關鍵字,標示符,立即數等是否合法
語法分析:主要分析表達式是否遵循語法規則
語義分析:在語法分析的基礎上進一步分析表達式是否合法
b.分析結束後進行代碼優化生成相應的彙編代碼文件 編譯指令:gcc -S file.c -o file.s
彙編:
彙編器將彙編代碼轉變爲機器可以執行的指令,每個彙編語句幾乎都對應一條機器指令,其實機器指令就是機器碼,就是2進制碼。彙編指令:gcc -c file.c -o file.o 注意:-c是編譯彙編不連接。
鏈接:
再把產生的.o文件,進行鏈接就可以生成可執行文件。連接指令:gcc file.o file1.o -o file 這句指令是鏈接file.o和file1.o兩個編譯並彙編的文件,並生成可執行文件file。
鏈接分兩種:靜態鏈接和動態鏈接,靜態鏈接是在編譯器完成的,動態鏈接是在運行期完成的。靜態鏈接的指令是:gcc -static file.c -o file對於一些沒有動態庫的嵌入式系統,這是常用的。
一般要想通過一條指令生成可執行文件的指令是: gcc file.c -o file
資料:這裏面說到了很多關於gcc的使用的問題,我提供一個gcc的學習資料,個人覺得還不錯,也不長,就是一個txt文檔,很全面。資源下載地址http://download.csdn.net/detail/qq418674358/6041183 Ps:嘿嘿,設了一個下載積分,因爲真的是沒分用了!希望大家見諒哈!
2.c語言中的預處理指令:#define、#undef(撤銷已定義過的宏名)、#include、#if、#else、#elif、#endif、#ifdef、#ifndef、#line、#error、#pragma。還有一些ANSI標準C定義的宏:__LINE__、__FILE__、__DATA__、__TIME__、__STDC__。這樣使用printf("%s\n",__TIME__); printf(__DATE__);
一個#undef的例子:
- #include <stdio.h>
- #include <stdlib.h>
- #include <string.h>
- #define X 2
- #define Y X*2
- #undef X
- #define X 3
- int main()
- {
- printf("%d\n",Y);
- return 0;
- }
這個輸出的是6,說明了#undef的作用
3.宏定義字符串的時候:應該是 #define HELLO "hello world" 記住是雙引號。還有就是一切宏都是不能有分號的,這個一定要切忌!!!
4.宏與函數的比較:
a.宏表達式在預編譯期被處理,編譯器不知道有宏表達式存在
b.宏表達式沒有任何的"調用"開銷
c.宏表達式中不能出現遞歸定義
5.爲什麼不在頭文件中定義全局變量:
如果一個全局變量,想要在兩個文件中,同時使用,那這兩個文件中都應該#include這個頭文件,這樣的話就會出現重複定義的問題。其實是重名的問題,因爲#include是分別在兩個文件中展開的,試想一下,如果在兩個文件中的開始部分,都寫上int a = 10; 是不是也會報錯。可能你會說那個#ifndef不是防止重複定義嗎?是的 ,那是防止在同一個文件中,同時出現兩次這個頭文件。現在是兩個文件中,所以都要展開的。全局變量就重名了!!!所以 對於全局變量,最好是定義在.c文件中,不要定義在頭文件中。
6.#pargma pack 設置字符對齊,看後面一節專門寫字符對齊問題的!!!
7.#運算符(轉換成字符串):
假如你希望在字符串中包含宏參數,那我們就用#號,它把語言符號轉換成字符串。
#define SQR(x) printf("the "#x"lait %d\n",((x)*(x)));
SQR(8)
輸出結果是:the 8 lait 64 這個#號必須使用在帶參宏中
有個小例子:
- #include <stdio.h>
- #include <stdlib.h>
- #include <string.h>
- /*在字符串中 加入宏參用的*/
- #define SCAN(N,String) scanf("%"#N"s",String); //N是截取的個數 String是存儲的字符串
- int main()
- {
- char dd[256];
- SCAN(3,dd) //記得沒有分號哈 自定義 任意格式輸入的scanf 截取輸入的前三個
- printf("%s\n",dd);
- return 1;
- }
8.##運算符(粘合劑)
一般用於粘貼兩個東西,一般是用作在給變量或函數命名的時候使用。如#define XNAME(n) x##n
XNAME(8)爲8n 這個##號可以使用在帶參宏或無參宏中
下面是一個##運算符的小例子,代碼如下:
- #include <stdio.h>
- #include <stdlib.h>
- #include <string.h>
- #define BL1 bb##ll##1
- #define BL(N) bbll##N
- int main()
- {
- int BL1=10;
- int BL(4)=15;
- printf("%d\n",bbll1);
- printf("%d\n",bbll4);
- return 1;
- }
注意:#號和##號都必須只能在宏定義中使用,不能使用在其他地方
9.其實預編譯這塊還有一些,不常用到的預編譯指令,也是盲點,但是不難理解,用到的時候查查就好。比如說#line、#error、#warning等。