【C语言进阶】预定义详解
預定義
- 一、預定義符號
- 二、#define
- 1.#define定義標識符
- 2.#define定義宏
- 3.#define替換規則
- 4.#和##
- 4.1 #號
- 4.2 ##號
- 三、函數與宏
- 1.代碼長度
- 2.執行速度
- 3.操作符的優先級
- 4.參數類型
- 5.調試
- 6.遞歸
- 7.帶有副作用的參數
- 四、條件編譯
一、預定義符號
預定義符號是C標準定義的宏定義符號
例如 FILE LINE DATE TIME STDC(如果編譯器遵循ASCI C就返回1,否則未定義。)
二、#define
1.#define定義標識符
語法:
define name stuff
(而這個stuff可以是不同的類型)
1.#define MAX 100
2.#define reg register(給register起一個短一點的名字)
3.#define arr “hello world.”
我們也可以利用這個來寫一個死循環
當然在寫代碼的時候會有這樣一個疑問,如果要寫的stuff過長怎么辦?這個時候我們可以分行寫,但是除了最后一行外,每一行都要加一個反斜杠(續航符)。
#define fad printf("name:%s\tage:%s\t \tele:%s\taddre:%s\t",\name,age\tele,addre)2.#define定義宏
#define機制包括了一個規定,允許把參數替換到文本中,這種實現通常稱為宏 (macro)或者定義宏(define macro)。
宏的申明方式
#define name(parament-list) stuff
其中parament-list是一個由逗號隔開的符號表,有可能會出現在后面的stuff中。
注意:參數列表的左括號必須與name緊鄰,不能有空格或者空白,否則會被認為是stuff的一部分。
在定義宏時要合理使用括號,防止運算符的優先級問題
#define SQUARE(x) x*xint main() {int a = 4;printf("%d", SQUARE(a));return 0; }
但如果換成SQUARE(4+1)呢
我們可以看到結果是9,那是因為替換方式
#define SQUARE(x) 4+1*4+1
這樣就提醒我們在寫的時候最好帶上括號
#define SQUARE(x) (x)*(x)int main() {//int a = 4;printf("%d", SQUARE(4+1));return 0; }
但是這樣寫任然不夠嚴密,在有些時候還是會出現錯誤
將上述代碼中printf一部分換成
printf(“%f”, 1.0/SQUARE(4+1))
答案顯然不對,按上述的替換法方式就變成了
#define SQUARE(x) 1.0/(4+1)*(4+1)
那么這樣得出錯誤的結果也就不奇怪了
為了達到最初的目的,我們應該這樣
當然還有一個特殊的地方就是不要在定義標識符或者定義宏時加上分號(;),這樣很容易在使用時出錯。
#define SQUARE(x) ((x)*(x));int main() {if(1)int a=SQUARE(4);printf("%d", a);return 0; }編譯預處理完,替換之后就變成了
if(1) >int a=((4)*(4));;if后面沒有大括號跟兩條語句是不對的
所以我們在定義標識符或者定義宏時不要加上分號,避免上述錯誤。
3.#define替換規則
在程序中擴展#define定義符號和宏時,需要涉及幾個步驟。
1.在調用宏時,首先多參數進行檢查,看看是否包括任何由#define定義的符號,如果是他們首先被替換。
2.替換文本隨后被插入到程序中原來文本的位置。對于宏,參數名被他們的只所替換。
3.最后,再次對結果文件進行掃描,看看它是否包含任何由#define定義的符號。如果是,就重復上述處理過程。
在這里有幾個需要注意的地方
1.宏參數和#define定義中可以出現其他#define定義的符號,但是對于宏,不能出現遞歸。
2.當預處理器搜索#define定義的符號的時候,字符串常量的內容并不被搜索。
4.#和##
4.1 #號
如何把參數插入到字符串中?
首先我們要知道字符串是有自動連接的功能的的特點的。
比如:
而#號加在宏參數前時,可以將這個參數轉化為字符串。
可以看出只有a,b,c和%d,%f不同,這個時候可以用宏來寫
#define PRINT(x,format) printf("the value of "#x" is "format"\n",x) int main() {int a = 10;PRINT(a, "%d");int b = 20;PRINT(b, "%d");float c = 30.0f;PRINT(c, "%f");return 0; }4.2 ##號
##的作用是將兩個字符合成一個字符。
#define A(x,y) printf("%d\n",x##y) int main() {int ab = 20;A(a, b);return 0; }三、函數與宏
1.代碼長度
宏:每次使用時在編譯的預處理階段宏的代碼就會插入到程序中,如果宏的代碼量很大的話,那么整體的代碼量也會大大增加。
函數:函數的代碼只有一份,每次調用都從這里調用。
2.執行速度
宏比函數在速度和規模上都要更勝一籌。
3.操作符的優先級
宏可能會帶來操作符的優先級的問題。
比如上面已經提過的:
執行答案是9,但是我們的目的是算(4+1)*(4+1)
4.參數類型
宏:參數與類型無關,只要參數合法就可以使用
函數:只能在類型合適的表達式上使用,即參數必須聲明為特定的類型。
當然,因為函數與參數無關也就不夠嚴謹。
比如,對于比較大小,宏可以比較不同類型的大小(整形,浮點型等等)
5.調試
宏:由于宏是在編譯預處理直接替換的,所以無法調試
函數:可以調試在vs2019下,按F10是逐過程,F11是逐語句(可以進入函數內部調試)。
6.遞歸
宏是不能遞歸的
函數可以,比如用遞歸寫斐波那契數列
7.帶有副作用的參數
宏:宏參數可能被替換到多個位置,更容易出現問題
函數:只在傳參時計算一次,結果不容易出錯
比如:
我們這里想要比較a++和b++ 的大小,也就是2和3的大小
我們想要的答案應該是3才對
但事實上
這是因為經過替換后
#define MAX(x,y) printf(“%d”,((++a)>(++b)?(++a):(++b)))
所以答案為4
這也是提醒我們在使用宏時不要使用帶有副作用的參數
四、條件編譯
在編譯一個程序的時候,可以用條件編譯來選擇將一條語句編譯或者放棄。
#include <stdio.h> #define __DEBUG__ int main() {int i = 0;int arr[10] = { 0 };for (i = 0; i < 10; i++){arr[i] = i; #ifdef __DEBUG__//判斷是否已經定義,如果定義了就執行下面的語句printf("%d\n", arr[i]); #endif }return 0; } 1. #if 常量表達式 //... #endif //常量表達式由預處理器求值。 如: #define __DEBUG__ 1 #if __DEBUG__ //.. #endif 2.多個分支的條件編譯 #if 常量表達式 //... #elif 常量表達式 //... #else //... #endif 3.判斷是否被定義 #if defined(symbol) //等于 #ifdef symbol #if !defined(symbol)// #ifndef symbol4.嵌套指令(與if嵌套相似) #if defined(OS_UNIX) #ifdef OPTION1 unix_version_option1(); #endif #ifdef OPTION2 unix_version_option2(); #endif #elif defined(OS_MSDOS) #ifdef OPTION2 msdos_version_option2(); #endif #endif總結
以上是生活随笔為你收集整理的【C语言进阶】预定义详解的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: python模板注入_BUUCTF/护网
- 下一篇: 全球十大智能物流装备龙头企业