目錄
一、#和##
1.1#運(yùn)算符
1.2## 運(yùn)算符?
二、命名約定?
三、#undef?
四、命令行定義?
五、條件編譯?
六、頭文件的包含?
4.1 頭文件被包含的方式:?
4.1.1?本地文件包含?
Linux環(huán)境的標(biāo)準(zhǔn)頭文件的路徑:?
VS環(huán)境的標(biāo)準(zhǔn)頭文件的路徑:
4.1.2庫文件包含?
4.2嵌套文件包含?
一、#和##
1.1#運(yùn)算符
#運(yùn)算符?
#運(yùn)算符將宏的一個參數(shù)轉(zhuǎn)換為字符串字面量。它僅允許出現(xiàn)在帶參數(shù)的宏的替換列表中。?
#運(yùn)算符所執(zhí)行的操作可以理解為”字符串化“。?
當(dāng)我們有一個變量 int a = 10; 的時候,我們想打印出: the value of a is 10 .?
就可以寫:
#define PRINT(n)
printf("the value of "#n " is %d", n);
當(dāng)我們按照下面的方式調(diào)用的時候:
?PRINT(a);//當(dāng)我們把a(bǔ)替換到宏的體內(nèi)時,就出現(xiàn)了#a,而#a就是轉(zhuǎn)換為"a",時一個字符串?
代碼就會被預(yù)處理為:
printf("the value of ""a" " is %d", a);
運(yùn)行代碼就能在屏幕上打印:
?the value of a is 10
1.2## 運(yùn)算符?
## 可以把位于它兩邊的符號合成一個符號,它允許宏定義從分離的文本片段創(chuàng)建標(biāo)識符。 ## 被稱
為記號粘合
這樣的連接必須產(chǎn)生一個合法的標(biāo)識符。否則其結(jié)果就是未定義的。
這里我們想想,寫一個函數(shù)求2個數(shù)的較大值的時候,不同的數(shù)據(jù)類型就得寫不同的函數(shù)。?
比如:
int int_max(int x, int y)
{
return x>y?x:y;
}
float float_max(float x, float y)
{
return x>yx:y;
}
但是這樣寫起來太繁瑣了,現(xiàn)在我們這樣寫代碼試試:
//宏定義?
#define GENERIC_MAX(type) \
type type##_max(type x, type y)\
{ \
?return (x>y?x:y);\
}\
使用宏,定義不同函數(shù)
GENERIC_MAX(int) //替換到宏體內(nèi)后int##_max 生成了新的符號 int_max做函數(shù)名?
GENERIC_MAX(float) //替換到宏體內(nèi)后float##_max 生成了新的符號 float_max做函數(shù)名
int main()
{
//調(diào)用函數(shù)?
int m = int_max(2, 3);
printf("%d\n", m);
float fm = float_max(3.5f, 4.5f);
printf("%f\n", fm);
return 0;
}
在實際開發(fā)過程中##使用的很少,很難取出非常貼切的例子。?
//宏定義?
#define GENERIC_MAX(type) \
type type##_max(type x, type y)\
{\
return (x>y?x:y);\
}\
GENERIC_MAX(int) //替換到宏體內(nèi)后int##_max 生成了新的符號 int_max做函數(shù)名?
GENERIC_MAX(float)
int main()
{
//調(diào)用函數(shù)?
int m = int_max(2, 3);
printf("%d\n", m);
float fm = float_max(3.5f, 4.5f);
printf("%f\n", fm);
return 0;
}
二、命名約定?
一般來講函數(shù)的宏的使用語法很相似。所以語言本身沒法幫我們區(qū)分二者。
那我們平時的一個習(xí)慣是:
把宏名全部大寫
函數(shù)名不要全部大寫
但是也有例外: offsetof
三、#undef?
這條指令用于移除一個宏定義。
#undef NAME
//如果現(xiàn)存的一個名字需要被重新定義,那么它的舊名字首先要被移除。?
#define MAX 100
int main()
{
printf("%d\n", MAX);
#undef MAX //移除宏定義
//printf("%d\n", MAX);
#define MAX 1000
printf("%d\n", MAX);
return 0;
}
?
四、命令行定義?
許多C 的編譯器提供了一種能力,允許在命令行中定義符號。用于啟動編譯過程。?
例如:當(dāng)我們根據(jù)同一個源文件要編譯出一個程序的不同版本的時候,這個特性有點用處。(假定某個程序中聲明了一個某個長度的數(shù)組,如果機(jī)器內(nèi)存有限,我們需要一個很小的數(shù)組,但是另外一個機(jī)器內(nèi)存大些,我們需要一個數(shù)組能夠大些。)
#include <stdio.h>
int main()
{
int array [ARRAY_SIZE];
int i = 0;
for(i = 0; i< ARRAY_SIZE; i ++)
{
array[i] = i;
}
for(i = 0; i< ARRAY_SIZE; i ++)
{
printf("%d " ,array[i]);
}
printf("\n");
return 0;
}
編譯指令:?
//linux 環(huán)境演示?
gcc -D ARRAY_SIZE=10 programe.c
在不改變代碼的情況下,編譯出不同的版本
五、條件編譯?
在編譯一個程序的時候我們?nèi)绻獙⒁粭l語句(一組語句)編譯或者放棄是很方便的。因為我們有條件編譯指令。在預(yù)處理階段,如果滿足條件執(zhí)行后續(xù)語句,如果不滿足,就不執(zhí)行
比如說:
調(diào)試性的代碼,刪除可惜,保留又礙事,所以我們可以選擇性的編譯。
?
#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__ //如果__DEBUG__為真就參與編譯
printf("%d\n", arr[i]);//為了觀察數(shù)組是否賦值成功。 ?
#endif //__DEBUG__
}
return 0;
}
int main()
{
#if 1==2
printf("hehe\n");//不參與編譯
#endif
return 0;
}
#define M 3
int main()
{
int a = 3;
#if M==a//err a是局部變量,預(yù)編譯時不參與
printf("hehe\n");
#endif
return 0;
}
#define M 2
int main()
{
#if M==1
printf("hehe\n");
#elif M==2
printf("haha\n");
#elif M == 3
printf("heihei\n");
#else
printf("~~~~\n");
#endif
return 0;
}
常見的條件編譯指令:?
1.#if 常量表達(dá)式
//...
#endif
//常量表達(dá)式由預(yù)處理器求值。?
如:
#define __DEBUG__ 1
#if __DEBUG__
//..
#endif
//結(jié)束條件編譯
2.多個分支的條件編譯
#if 常量表達(dá)式
//...
#elif 常量表達(dá)式
//...
#else
//...
#endif
3.判斷是否被定義
#if defined(symbol)
#ifdef symbol
#if !defined(symbol)
#ifndef symbol
4.嵌套指令
#if defined(OS_UNIX)
#ifdef OPTION1
unix_version_option1();
六、頭文件的包含?
4.1 頭文件被包含的方式:?
4.1.1?本地文件包含?
#include "filename"
查找策略:先在源文件所在目錄下查找,如果該頭文件未找到,編譯器就像查找?guī)旌瘮?shù)頭文件一樣在標(biāo)準(zhǔn)位置查找頭文件。
如果找不到就提示編譯錯誤。
Linux環(huán)境的標(biāo)準(zhǔn)頭文件的路徑:?
/usr/include
VS環(huán)境的標(biāo)準(zhǔn)頭文件的路徑:
C:\Program Files (x86)\Microsoft Visual Studio 12.0\VC\include
//這是VS2013的默認(rèn)路徑?
注意按照自己的安裝路徑去找。
4.1.2庫文件包含?
#include <filename.h>
查找頭文件直接去標(biāo)準(zhǔn)路徑下去查找,如果找不到就提示編譯錯誤。
這樣是不是可以說,對于庫文件也可以使用 “” 的形式包含?
答案是肯定的,可以,但是這樣做查找的效率就低些,當(dāng)然這樣也不容易區(qū)分是庫文件還是本地文件了。
4.2嵌套文件包含?
我們已經(jīng)知道, #include 指令可以使另外一個文件被編譯。就像它實際出現(xiàn)于 #include 指令的
地方一樣。
這種替換的方式很簡單:預(yù)處理器先刪除這條指令,并用包含文件的內(nèi)容替換。
一個頭文件被包含10次,那就實際被編譯10次,如果重復(fù)包含,對編譯的壓力就比較大。
如果直接這樣寫,test.c文件中將test.h包含5次,那么test.h文件的內(nèi)容將會被拷貝5份在test.c中。?
如果test.h 文件比較大,這樣預(yù)處理后代碼量會劇增。如果工程比較大,有公共使用的頭文件,被大家都能使用,又不做任何的處理,那么后果真的不堪設(shè)想。
如何解決頭文件被重復(fù)引入的問題?答案:條件編譯。
每個頭文件的開頭寫:
#ifndef __TEST_H__
#define __TEST_H__
//頭文件的內(nèi)容?
#endif //__TEST_H__
或者
#pragma once
就可以避免頭文件的重復(fù)引入。
筆試題:
1. 頭文件中的 ifndef/define/endif是干什么用的??
答:用于防止頭文件的內(nèi)容在同一個編譯單元中被多次包含。
2. #include <filename.h> 和 #include "filename.h"有什么區(qū)別? ?
答:#include <filename.h>:這是用于包含系統(tǒng)提供的頭文件的常用格式。編譯器通常會在其預(yù)定義的系統(tǒng)頭文件目錄中查找這樣的文件。
#include "filename.h":這是用于包含用戶定義的頭文件或項目特定的頭文件的常用格式。編譯器首先在當(dāng)前文件或指定的用戶目錄中查找這樣的文件,如果找不到,它可能會回退到系統(tǒng)目錄。
? ??
其他預(yù)處理指令?
#error
#pragma
#line
...
不做介紹,自己去了解。
#pragma pack()在結(jié)構(gòu)體部分介紹。
祝大家新年快樂?。。?/p>
?
看到這里了還不給博主扣個:
?? 點贊??收藏 ?? 關(guān)注!文章來源:http://www.zghlxwxcb.cn/news/detail-801551.html
你們的點贊就是博主更新最大的動力!
有問題可以評論或者私信呢秒回哦。文章來源地址http://www.zghlxwxcb.cn/news/detail-801551.html
到了這里,關(guān)于預(yù)處理詳解(#和##運(yùn)算符、命名約定、#undef??、命令行定義?、條件編譯、頭文件的包含?)的文章就介紹完了。如果您還想了解更多內(nèi)容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!