C語言深度解剖讀書筆記(3.預編譯處理)

本節知識點:

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的例子:

  1. #include <stdio.h>  
  2. #include <stdlib.h>  
  3. #include <string.h>  
  4.   
  5.   
  6. #define X 2  
  7. #define Y X*2  
  8. #undef X  
  9. #define X 3  
  10.   
  11.   
  12. int main()  
  13. {  
  14.     printf("%d\n",Y);  
  15.     return 0;  
  16. }  

這個輸出的是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   這個#號必須使用在帶參宏中

有個小例子:

  1. #include <stdio.h>  
  2. #include <stdlib.h>  
  3. #include <string.h>  
  4. /*在字符串中  加入宏參用的*/  
  5. #define SCAN(N,String) scanf("%"#N"s",String);  //N是截取的個數  String是存儲的字符串   
  6. int main()  
  7. {  
  8.     char dd[256];  
  9.     SCAN(3,dd) //記得沒有分號哈  自定義 任意格式輸入的scanf  截取輸入的前三個   
  10.     printf("%s\n",dd);  
  11.     return 1;  
  12. }  

8.##運算符(粘合劑)

    一般用於粘貼兩個東西,一般是用作在給變量或函數命名的時候使用。如#define XNAME(n) x##n

    XNAME(8)爲8n   這個##號可以使用在帶參宏或無參宏中

下面是一個##運算符的小例子,代碼如下:

  1. #include <stdio.h>  
  2. #include <stdlib.h>  
  3. #include <string.h>  
  4.   
  5. #define BL1 bb##ll##1  
  6.   
  7. #define BL(N) bbll##N  
  8. int main()  
  9. {  
  10.     int BL1=10;  
  11.   
  12.     int BL(4)=15;  
  13.     printf("%d\n",bbll1);  
  14.       
  15.     printf("%d\n",bbll4);  
  16.     return 1;  
  17. }  

注意:#號和##號都必須只能在宏定義中使用,不能使用在其他地方
9.其實預編譯這塊還有一些,不常用到的預編譯指令,也是盲點,但是不難理解,用到的時候查查就好。比如說#line、#error、#warning等。

發佈了27 篇原創文章 · 獲贊 19 · 訪問量 4萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章