c 宏定义用法#define
轉自:https://blog.csdn.net/boring_wednesday/article/details/78756696
?
宏定義
??? 語法
??? #define name Stuff
#define PI 3.14
//定義一個M,值為3.14
#define DO_FOREVER for(;;)
//定義一個死循環
#define REG register
//定義REG來作為register的別名
#define CASE break;case
//在switch中用CASE來補上break;
#define DEBUG_PRINT printf("file:%s\tline:%d\tdate:%s\ttime:%s\n",\
??????????????????????? __FILE__, __LINE__, __DATE__, __TIME__);
//測試預定義符號
??? 1
??? 2
??? 3
??? 4
??? 5
??? 6
??? 7
??? 8
??? 9
??? 10
??? 11
1、宏的作用范圍
先看下面代碼:
歡迎訪問jo-qzy的博客
讓我們查看上圖中代碼經過預處理后的樣子
歡迎訪問jo-qzy的博客
可以發現宏只對宏定義后的行數起作用,且與定義在哪里無關,即使函數不被調用,也可以使用宏
2、宏替換的原則
在程序中擴展#define定義符號和宏時,需要涉及幾個步驟。
??? 1. 在調用宏時,首先對參數進行檢查,看看是否包含任何由#define定義的符號。如果是,它們首先被替換。
??? 2. 替換文本隨后被插入到程序中原來文本的位置。對于宏,參數名被他們的值替換。
??? 3. 最后,再次對結果文件進行掃描,看看它是否包含任何由#define定義的符號。如果是,就重復上述處理過程。
注意:
??? 1、宏函數不能出現遞歸
??? 2、宏定義的符號,即name部分不會在預處理替換的時候被搜索
3、宏定義#define后不需要加;
??? 例如:
??? #define M 100;
??? 這里在100后面加上了;
??? 在句子中有時候就會出現問題
觀察下面代碼片段:
#define M 100;
if (condition)
??? m = M;
else
??? max = 0;
??? 1
??? 2
??? 3
??? 4
??? 5
在該選擇語句中會出現語法錯誤,沒有與else與之匹配的if語句
4、宏函數申明
??? 宏函數申明格式:
??? #define name ( parament-list ) stuff
??? parament-list為參數表,可以包含多個參數,他們會在stuff出現
例如:
#define SQARE( X ) X*X
//定義一個計算乘方的宏函數
??? 1
??? 2
但是這么定義是會出現預料之外的錯誤的,觀察下面代碼片段
#define SQUARE(X) X*X
int main()
{
??? int a = 5;
??? printf("%d\n", SQUARE(a+1));
??? return 0;
}
??? 1
??? 2
??? 3
??? 4
??? 5
??? 6
??? 7
??? 8
本來結果應該為6^2 = 36
歡迎訪問jo-qzy的博客
但是實際計算機輸出的結果為11
我們用gcc觀察預處理后的代碼片段是怎么樣的
使用命令$ gcc -E test.c -o test.i來查看預處理后的代碼
歡迎訪問jo-qzy的博客
可以看到這里原式被替換成 a + 1 * a + 1 = 11,而這個結果顯然不是我們期望的
這里我們提出解決方案,將 X 用括號括起來(X),這樣就避免上述代碼因符號優先級帶來的錯誤
至此,上面代碼解決了,來看下面的宏函數定義:
#define DOUBLE(X) (X) + (X)
int main()
{
??? int a = 5;
??? printf("%d\n", 10 * DOUBLE(a));
??? return 0;
}
??? 1
??? 2
??? 3
??? 4
??? 5
??? 6
??? 7
??? 8
期望結果為100,而看程序執行結果:
歡迎訪問jo-qzy的博客
程序再次出現預料之外的結果,原因是原式被替換成了
10 * 5 + 5 = 55
解決方法:
在宏函數定義時,對Stuff中的參數以及結果均用括號來避免因符號運算優先級帶來的問題
5、宏中的#和##
#的用法
首先要理解一個原則,即鄰近字符串連接原則
??? 在C語言中
??? printf(“hello”” world!”“\n”);
??? 這句話是合法的
??? 打印結果為:
??? hello world!
按照上述原則,我們可以寫出下列代碼:
#define PRINT(FORMAT, VALUE) printf("the value of " #VALUE " is "FORMAT"\n", VALUE)
int main()
{
??? int i = 0;
??? PRINT("%d", i + 3);
??? return 0;
}
??? 1
??? 2
??? 3
??? 4
??? 5
??? 6
??? 7
??? 8
看下gcc編譯后的代碼,程序正常運行:
歡迎訪問jo-qzy的博客
我們再通過gcc生成test.i文件看下預處理是怎么樣的:
歡迎訪問jo-qzy的博客
所以我們可以看出來#的作用:
將一個宏參數變成一個對應的字符串
在上述例子中:
??? #VALUE被替換成了“i + 3”
??? 別忘了被替換的時候i + 3兩邊加上了雙引號
##的用法
看下面的代碼片段
#define ADD_TO_AN(num, value) a##num += value
int main()
{
??? int a1 = 0;
??? int a2 = 0;
??? ADD_TO_AN(1, 5);
??? ADD_TO_AN(2, 6);
??? return 0;
}
??? 1
??? 2
??? 3
??? 4
??? 5
??? 6
??? 7
??? 8
??? 9
??? 10
解釋一下上面這段代碼
假設有一個變量叫a1
此時代碼片段為ADD_TO_AN(1, 5)
即替換為a1 += 5,給a1變量增加5
同理ADD_TO_AN(2, 6)則替換成a2 += 6
??? ##的作用
??? 將##兩邊的字符連在一起作為一個標識符
??? 前提連接后的標識符必須合法,否則編譯出現標識符未定義
6、宏和函數
宏通常被應用于執行簡單的運算
和函數相比,宏有他的優點
??? 宏的優點:
??? 1. 用于調用函數和從函數返回的代碼可能比實際執行這個小型計算工作所需要的時間更多。所以宏比函數在程序的規模和速度方面更勝一籌
??? 2. 更為重要的是函數的參數必須聲明為特定的類型。所以函數只能在類型合適的表達式上使用。反之這個宏怎可以適用于整形、長整型、浮點型等可以用于>來比較的類型。宏的參數與類型無關的
??? 3. 宏參數可以使用變量類型,而函數不可以,例如:
#define MALLOC(num, type) (type *)malloc(num * sizeof(type))
MALLOC(10, int);//類型作為參數
//預處理器替換之后:
(int *)malloc(10 * sizeof(int));
??? 1
??? 2
??? 3
??? 4
??? 5
但是,宏參數與類型無關是一把雙刃劍,和函數比較也有他的缺點
??? 宏的缺點:
??? 1. 每次使用宏的時候,一份宏定義的代碼將插入到程序中。除非宏比較短,否則可能大幅度增加程序的長度
??? 2. 宏是沒法調試的
??? 3. 宏由于類型無關,也就不夠嚴謹
??? 4. 宏可能會帶來運算符優先級的問題,導致程序容易出現問題
我的建議:
??? 當有一部分功能既可以用函數實現也可以用宏實現,且在調用函數的過程與宏使用過程所消耗的資源相當時,優先考慮代碼的嚴謹性,使用函數
7、宏參數的副作用
??? 當宏參數在宏的定義中出現超過一次的時候,如果參數帶有副作用,那么你在使用這個宏的時候就可能出現危險,導致不可預測的后果。副作用就是表達式求值的時候出現的永久性效果。
#define MAX(a, b) ( (a) > (b) ? (a) : (b) )
x = 5;
y = 8;
z = MAX(x++, y++);
printf("x=%d y=%d z=%d\n", x, y, z);
//結果為x=6 y=10 z=9
??? 1
??? 2
??? 3
??? 4
??? 5
??? 6
??? 7
可以看到上面代碼中y++帶來了副作用,所以在使用宏的時候,避免使用++運算和--運算
8、宏的命名
宏和函數的使用很類似,我們平時的使用習慣是:
??? 1. 宏名全部大寫
??? 2. 函數名不全大寫
---------------------
作者:jo-qzy
來源:CSDN
原文:https://blog.csdn.net/boring_wednesday/article/details/78756696
版權聲明:本文為博主原創文章,轉載請附上博文鏈接!
總結
以上是生活随笔為你收集整理的c 宏定义用法#define的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: gdb相关(栈和寄存器)
- 下一篇: linux proc/xx/maps文件