C/C++編譯預處理


源自《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

很少用到,先跳過


 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章