源自《C語言程序設計現代方法》《高質量程序設計指南C++/C語言》的讀書筆記
- 由#開頭的一些命令(其前面只能出現空白字符)
- #include、#define這些指令都是由預處理器處理。預處理器就是一個小軟件,在編譯前處理C程序。
- 最主要功能:宏定義、條件編譯
一、工作原理
1、#define
定義了一個宏——用來代表其他東西的一個名字,例如常量、表達式
重命名和替換:將宏替換爲其定義值
2、#include
告訴預處理器,打開一個特定的文件,將其內容作爲正在編譯的文件的一部分“包含”進來
用於包含一個頭文件(頭文件中存放的一般是模塊接口),預編譯處理器在掃描到該僞指令之後,就用對應的文本內容替換它
#include <頭文件名稱>
#include "頭文件名稱" //自己寫的
頭文件前面可以加相對路徑或者絕對路徑
#include "C:\myproject\test\source\include\abc.inl"
a、內部包含衛哨——避免同一編譯單元包含同一個頭文件的內容超過一次
b、外部包含衛哨——可提高編譯速度???
3、預處理器在編譯過程中的作用
-
C程序—>預處理器—>修改後的C程序—>編譯器—>目標代碼
- 預處理器輸入——C語言程序(可能包含指令)
- 預處理器——執行指令,並在處理過程中刪除這些指令
- 預處理器輸出——另外一個,不再包含指令,原程序編輯後的版本(用了#define的話,完成程序替換)
- 交給編譯器,編譯器檢查是否有錯,並將程序翻譯爲目標代碼(機器指令)
二、預處理指令
- 都以【#】開始,不需要在行首,前面有空白符就行
- 在指令的符號之間可以插入任意的*空格*和*製表符*:# define N 1000
- 可以出現在程序任意地方
1、宏定義
#define 指令定義一個宏、#undef 指令刪除一個宏定義
宏定義不是C++\C語句,所以不需要加 “;” 這個符號。當不再使用某個宏,用 【#undef 宏名稱】取消定義
a、簡單的宏
格式:
#define 標識符 替換列表
兩種典型的錯誤:不要等號、不要分號、不要其他一切不需要的符號
#define N = 100
int a[N]; //等價於 int a[=100];
//////////////////////////////
#define N 100;
int a[N]; //等價於 int a[100;];
對數值、字符值、字符串命名
#define STE_LEN 80
#define TRUE 1
#define FALSE 0
#define PI 3.14158
#define CR '\r'
#define EOS '\0'
#define MEM_ERR "Error: not enough memory"
對C語言做修改
#define BEGIN {
#define END }
還有對類型重命名、控制條件編譯
b、帶參數的宏
格式: #define 標識符(x1,x2,...,xn) 替換列表
#define MAX(x,y) ((x)>(y)?(x):(y)) //一定要多加括號
i = MAX(j+k,m-n);
/////////////////////等價於///////////////////////
i = ((j+k)>(m-n)?(j+k):(m-n));
/////////////////////////////////////////////////
檢測字符c是否在'a'到'z'之間,是的話,轉換爲相應的大寫字母
#define TOUPPER(c) ('a'<=(c)&&'z'>=(c)?(c)-'a'+'A':(c))
是不是可以作爲面試題?
tips:對於複雜的,一定要在最外層加一個大括號,對於每一個參數還要再加小括號
【補充——ASCII碼與字符】A-65 Z-90 a-97 z-122 0-48 1-49
宏可能不止一次的計算參數
n = MAX(i++,j);
n = ((i++)>(j)?(i++):(j));
i有可能會自增兩次
如果厭煩了 printf("%d\n",i);
#define PRINT_INT(n) printf("%d\n",n)
c、# 和 ## 運算符號
#——將宏的一個參數轉換爲字符串字面量(字符串化)
#define PRINT_INT(n) printf(#n "= %d\n",n)
調用:
int i = 5;
int j = 4;
PRINT_INT(i / j);
輸出:
i / j = 1
##——可以將兩個記號(如標識符)粘合在一起,成爲一個記號
#define MK_ID(n) i##n
調用:
int MK_ID(1),MK_ID(2);
結果:
int i1,i2; //粘合作用
d、預定義宏
圖片截圖自:http://c.biancheng.net/view/350.html
printf("Compiled on %s at %s\n",__DATE__,__TIME__);
結果:
Compiled on Mar 13 2019 at 13:32:23
注意:是兩根短的下劃線
使用__LINE__和__FILE__來找錯誤——被0除的定位問題
#define CHECK_ZERO(divisor)\
if(divisor==0)\
printf("***Attempt to divide by zero on line %d "\
"of file %s ***",__LINE__,__FILE__)
運行:
int i = 5;
int j = 0;
CHECK_ZERO(j);
結果:
***Attempt to divide by zero on line 16 of file 。。。。***
2、文件包含
#include【指定文件的內容包含到程序中】
3、條件編譯
#if、#ifdef、#ifndef、#elif、#else、#endif
可以根據預處理器可以測試的條件,來確定是將【一段文本塊】包含到程序中,還是排除在程序之外
條件編譯——根據預處理器所執行的測試結果來包含或排除程序的片段
a、#if 和 #endif
#define DEBUG 1
#if DEBUG
printf("Value of i : %d\n");
#endif
由於DEBUG的值不爲0,所以printf會被保留在程序中;如果DEBUG的值爲0,這些代碼就可以理解爲註釋(目前我這麼理解)
測試用:
#if DEBUG
會失敗(但不會產生出錯消息),而測試
#if !DEBUG
會成功
b、defined運算符
當defined應用於標識符時,如果標識符是一個定義過的宏,則返回1,否則返回0
#if defined(DEBUG) //#if defined DEBUG 這兩種都可以
...
#endif
僅當DEBUG被定義成宏的時候,#if 和 #endif 之間的代碼會被保留。有沒有括號都可以
c、#ifdef 和 #ifndef
#ifdef——用來測試一個標識符是否已經定義爲宏;#ifndef 爲 #ifdef的對立面
#ifdef 標識符
當標識符被定義爲宏時需要包含的代碼
#endif
等價於
#if defined(標識符)
d、#elif 和 #else
#if 表達式1
當表達式1非0時需要包含的代碼
#elif 表達式2
當表達式1爲0,表達式2非0時需要包含的代碼
#elif
其他情況下需要包含的代碼
#endif
有點像MATLAB,和普通的if else 那些也沒什麼差別
e、條件屏蔽
#if 0
包含註釋的代碼行
#endif
f、#error、#line、#pragma
很少用到,先跳過