摘要:當預處理器搜索定義的符號的時候,字符串常量的內容并不被搜索。這種替換的方式很簡單預處理器先刪除這條指令,并用包含文件的內容替換。
目錄
__FILE__ //進行編譯的源文件__LINE__ //文件當前的行號__DATE__ //文件被編譯的日期__TIME__ //文件被編譯的時間__STDC__ //如果編譯器遵循ANSI C,其值為1,否則未定義
int main(){ printf("%s/n", __FILE__); printf("%d/n", __LINE__); printf("%s/n", __DATE__); printf("%s/n", __TIME__); //printf("%d/n", __STDC__);//因為VS不支持ANSI C,其實__STDC__未定義 //gcc 是支持的 //gcc對C語言語法的支持非常好 return 0;}
?這些預定義符號都是語言內置的,例題如下
int main() { printf("file:%s line:%d/n", __FILE__, __LINE__); return 0; }
#define name stuff
#define MAX 100#define reg register //為 register這個關鍵字,創建一個簡短的名字#define STR "HEHE"int main(){ int a = 3; int b = 20; if (a == 4) b = MAX; else b = -a; register int num = 100; reg int num2 = 200; int m = MAX; printf("%d/n", MAX); printf("%d/n", m); printf("%s/n", STR); return 0;}
?在define定義標識符的時候,要不要在最后加上 ; ?
#define MAX 1000;int main(){ int x = 1; int max; if (x==1) { max = MAX;//這里替換為 MAX 1000;;語法錯誤 } else { max = 0; } printf("%d", max); return 0;}
#define 機制包括了一個規定,允許把參數替換到文本中,這種實現通常稱為宏(macro)或定義宏(define macro)。
下面是宏的申明方式:
#define name( parament-list ) stuff 其中的 parament-list 是一個由逗號隔開的符號表,它們可能出現在 stuff中。
注意: 參數列表的左括號必須與name緊鄰。 如果兩者之間有任何空白存在,參數列表就會被解釋為stuff的一部 分。
#define SQUARE( x ) x * x
這個宏接收一個參數 x=5.
SQUARE( 5 );
程序會如何表達,如下
5 * 5
int a = 5;printf("%d/n" ,SQUARE( a + 1) );
觀察上面的代碼,你會認為結果是多少?
替換文本時,參數x被替換成a + 1,所以這條語句實際上變成了: printf ("%d/n",a + 1 * a + 1 );
在宏定義上加上兩個括號,這個問題便輕松的解決了:
#define SQUARE(x) (x) * (x)
這樣預處理之后就產生了預期的效果:
printf ("%d/n",(a + 1) * (a + 1) );
#define SQUARE(X) ((X)*(X))int main(){ int a = 5; int ret = SQUARE(a+5); //int ret = a + 5 * a + 5; //int ret = ((a) * (a)); printf("%d/n", ret); return 0;}
?這里還有一個宏定義:
#define DOUBLE(x) (x) + (x)
定義中我們使用了括號,想避免之前的問題,但是這個宏可能會出現新的錯誤。
int a = 5; printf("%d/n" ,10 * DOUBLE(a));
好像打印100,但事實上打印的是55. 我們發現替換之后:
printf ("%d/n",10 * (5) + (5));
這個問題,的解決辦法是在宏定義表達式兩邊加上一對括號就可以了。
#define DOUBLE(x) ( ( x ) + ( x ) )
#define DOUBLE(X) ((X)+(X))int main(){ int ret = 10 * DOUBLE(2); //int ret = 10 * 2 + 2; printf("%d/n", ret); return 0;}
?提示:所以用于對數值表達式進行求值的宏定義都應該用這種方式加上括號,避免在使用宏時由于參數中的操作符或 鄰近操作符之間不可預料的相互作用。
在調用宏時,首先對參數進行檢查,看看是否包含任何由#define定義的符號。如果是,它們首先被替換。
替換文本隨后被插入到程序中原來文本的位置。對于宏,參數名被他們的值替換。
最后,再次對結果文件進行掃描,看看它是否包含任何由#define定義的符號。如果是,就重復上述處理過程。
注意:
宏參數和#define 定義中可以出現其他#define定義的變量。但是對于宏,不能出現遞歸。
當預處理器搜索#define定義的符號的時候,字符串常量的內容并不被搜索。
#define PRINT(n) printf("the value of "#n" is %d/n", n)int main(){ int a = 10; PRINT(a); int b = 20; PRINT(b); return 0;}
?用#define連接字符串
#define PRINT(FORMAT, VALUE)printf("the value is "FORMAT"/n", VALUE);PRINT("%d", 10);
#define PRINT(n) printf("the value of "#n" is %d/n", n)int main(){ int a = 10; printf("the value of a is %d/n", a); int b = 20; printf("the value of b is %d/n", b); printf("hello world/n"); printf("hello ""world/n"); return 0;}
##可以把位于它兩邊的符號合成一個符號。 它允許宏定義從分離的文本片段創建標識符。
#define ADD_TO_SUM(num, value) sum##num += value; ADD_TO_SUM(5, 10);//作用是:給sum5增加10.
#define CAT(X,Y) X##Yint main(){ int class103 = 100; printf("%d/n", CAT(class, 103)); printf("%d/n", CAT(1, 2)); return 0;}
當宏參數在宏的定義中出現超過一次的時候,如果參數帶有副作用,那么你在使用這個宏的時候就可能出現危險,導 致不可預測的后果。副作用就是表達式求值的時候出現的永久性效果。 例如:
x+1;//不帶副作用x++;//帶有副作用
MAX宏可以證明具有副作用的參數所引起的問題。
int main(){ int a = 10; int b = a + 1;//b得到的是11,a不變 int b = ++a;//b得到的是11,但是a變了,這個表達式是有副作用的 return 0;}
int Max(int x, int y){ return x > y ? x : y;}int main(){ int a = 5; int b = 8; //int m = MAX(a++, b++); //int m = ((a++) > (b++) ? (a++) : (b++)); //函數的參數是計算后再傳進去的 int m = Max(a++, b++); printf("m=%d/n", m);//8 printf("a=%d/n", a);//6 printf("b=%d/n", b);//9 return 0;}
//宏的實現 - 1#define MAX(X,Y) ((X)>(Y)?(X):(Y))int Max(int x, int y){ return x > y ? x : y;}int main(){ int a = 5; int b = 8; //宏的參數是不計算直接替換進去的 //替換進去進去后參與運算 int m = Max(a++, b++); printf("m=%d/n", m);//8 printf("a=%d/n", a);//6 printf("b=%d/n", b);//9 return 0;
宏通常被應用于執行簡單的運算。比如在兩個數中找出較大的一個。
#define MAX(a, b) ((a)>(b)?(a):(b))
用于調用函數和從函數返回的代碼可能比實際執行這個小型計算工作所需要的時間更多。所以宏比函數在程序的規模和速度方面更勝一籌。
更為重要的是函數的參數必須聲明為特定的類型。所以函數只能在類型合適的表達式上使用。反之這個宏怎可 以適用于整形、長整型、浮點型等可以用于>來比較的類型。宏是類型無關的。
宏的劣勢
每次使用宏的時候,一份宏定義的代碼將插入到程序中。除非宏比較短,否則可能大幅度增加程序的長度。
宏是沒法調試的。
宏由于類型無關,也就不夠嚴謹。
宏可能會帶來運算符優先級的問題,導致程容易出現錯。
屬 性 | #define定義宏 | 函數 |
代 碼 長 度 | 每次使用時,宏代碼都會被插入到程序中。除了非常小的宏 之外,程序的長度會大幅度增長 | 函數代碼只出現于一個地方;每次使 用這個函數時,都調用那個地方的同 一份代碼 |
執 行 速 度 | 更快 | 存在函數的調用和返回的額外開銷, 所以相對慢一些 |
操 作 符 優 先 級 | 宏參數的求值是在所有周圍表達式的上下文環境里,除非加 上括號,否則鄰近操作符的優先級可能會產生不可預料的后 果,所以建議宏在書寫的時候多些括號 | 函數參數只在函數調用的時候求值一 次,它的結果值傳遞給函數。表達式 的求值結果更容易預測。 |
帶 有 副 作 用 的 參 數 | 參數可能被替換到宏體中的多個位置,所以帶有副作用的參 數求值可能會產生不可預料的結果。 | 函數參數只在傳參的時候求值一次, 結果更容易控制。 |
參 數 類 型 | 宏的參數與類型無關,只要對參數的操作是合法的,它就可 以使用于任何參數類型。 | 函數的參數是與類型有關的,如果參 數的類型不同,就需要不同的函數, 即使他們執行的任務是不同的 |
調 試 | 宏是不方便調試的 | 函數是可以逐語句調試的 |
遞 歸 | 宏是不能遞歸的 | 函數是可以遞歸的 |
一般來講函數的宏的使用語法很相似。所以語言本身沒法幫我們區分二者。 那我們平時的一個習慣是:
把宏名全部大寫 函數名不要全部大寫
這條指令用于移除一個宏定義。
#undef NAME //如果現存的一個名字需要被重新定義,那么它的舊名字首先要被移除。
命令行定義
許多C 的編譯器提供了一種能力,允許在命令行中定義符號。用于啟動編譯過程。 例如:當我們根據同一個源文件要 編譯出不同的一個程序的不同版本的時候,這個特性有點用處。(假定某個程序中聲明了一個某個長度的數組,如果 機器內存有限,我們需要一個很小的數組,但是另外一個機器內存大寫,我們需要一個數組能夠大寫。)
#define MAX 100int main(){ int m = MAX; #undef MAX int n = MAX;//err return 0;}
#define M 500int main(){#if M==100 printf("haha/n");#elif M==200 printf("hehe/n");#else printf("heihei/n");#endif return 0;}
我們已經知道, #include 指令可以使另外一個文件被編譯。就像它實際出現于 #include 指令的地方一樣。 這種替換的方式很簡單: 預處理器先刪除這條指令,并用包含文件的內容替換。 這樣一個源文件被包含10次,那就 實際被編譯10次。
頭文件被包含的方式:
#include "filename"
?VS環境的標準頭文件的路徑:
C:/Program Files (x86)/Microsoft Visual Studio 9.0/VC/include
庫文件包含
#include
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/122184.html
前言:博主之前有已經寫過了C語言常用字符函數詳解+模擬實現,感興趣的同學可以去圍觀一下哦! 目錄 前言: 1.內存函數 memcpy() ?memmove() memcmp() memset() 2.錯誤信息報告函數 strerror() ?perror() 1.內存函數 memcpy() 作用:內存拷貝 函數原型: 注意:count:要拷貝的字節數 函數memcpy從src位置開始向后賦值c...
摘要:本文介紹了類的常用接口的使用,并對其進行了模擬實現,對模擬實現中涉及到的深淺拷貝問題進行了解析。在此之前,必須提到一個經典問題。為了解決淺拷貝問題,所以中引入了深拷貝。但是實際使用中需要是第一個形參對象,才能正常使用。 本文介紹了string類的常用接口的使用,并對其進行了模擬實現,對模擬實...
摘要:語言在設計中考慮了函數的高效性和易用性兩個原則。在語言中,最常見的當屬函數了。以上就是一個函數,它被稱為語言的入口函數,或者主函數。例如和都是函數名。形式參數當函數調用完成之后就自動銷毀了。 ...
摘要:位運算符是對其操作數按其二進制形式逐位進行運算。接下來我們逐一講解位運算符的計算原理按位與用于清零取某些指定位保位的計算原理,,結果上面使用按位與的一段程序運行結果為我們用二進制來分析一下它的計算規則。 C語言中位運算符共有六種 目錄 1.&(按位與) 2. |(按位或) 3.^(按位抑或)...
閱讀 916·2021-10-27 14:14
閱讀 1741·2021-10-11 10:59
閱讀 1315·2019-08-30 13:13
閱讀 3152·2019-08-29 15:17
閱讀 2749·2019-08-29 13:48
閱讀 488·2019-08-26 13:36
閱讀 2081·2019-08-26 13:25
閱讀 857·2019-08-26 12:24