1樓:匿名使用者
掌握"巨集"概念的關鍵是「換」。一切以換為前提、做任何事情之前先要換,準確理解之前就要「換」。所以只要換就可以了:
把b換成a*a,那麼b*b就是a*a*a*a,然後把a換成3+2,結果就是:3+2*3+2*3+2*3+2=23;
不知道你理解了沒有,如果是這樣巨集定義a的,那麼結果又不一樣了,#define a (3+2)
那麼結果就是5*5*5*5,所以比它本來沒括號,你不能習慣的加了括號,這是最容易犯錯的。
2樓:兜裡有兩塊糖
c語言中的巨集,在編譯時,進行簡單替換。
也就是說
當你算b*b時
編譯器會把它變為a*a*a*a
3+2*3+2*3+2*3+2 = 23
因為你沒有寫括號也就是
#define a (3+2)
所以不是你預想的
(3+2)*(3+2)*(3+2)*(3+2)所以說巨集是簡單替換,不會幫你把運算優先順序也編譯出來。
這個是一個常見的巨集問題。
3樓:匿名使用者
巨集只是簡單的文字替換
b等價於
3 + 2 * 3 + 2
b*b等價於
3 + 2 * 3 + 2 * 3 + 2 * 3 + 2
4樓:匿名使用者
3+2*3+2*3+2*3+2
c語言中,巨集替換的替換規則
5樓:焉秋梵正
簡單來說:巨集定義又稱為巨集代換、巨集替換,簡稱「巨集」。巨集替換是c/c++的預處理中的一部分,在c++標準中有4條規則來定義替換。
規則1:實參替換。
本條規則描述帶引數的巨集的替換過程。
對於巨集定義中的形參,在替換列表中,如果不是作為#或##的運算元,那麼將對應實參完全
(相當於對實參進行求值),然後將替換列表中的形參替換掉.如果是#或##的運算元,
那麼不進行替換。
規則2:多次掃描。
在所有的形參替換為實參後,對結果進行再次掃描,如果發現還有可替換的巨集,則進行替換,
否則中止。
規則3:遞迴替換抑制。
如果在替換列表中發現當前正在的巨集的名字,那麼這裡不進行替換.更進一步,在巢狀
的替換過程中發現已經替換過的巨集的名字,則不進行替換。
規則4:遞迴預處理抑制。
如果替換後的結果形成預處理指令,則不執行這條預處理指令。
看幾個c++標準中的例子:
#define x 3
#define f(a) f(x * (a))
#undef x
#define x 2
#define g f
#define z z[0]
#define h g(~
#define m(a) a(w)
#define w 0,1
#define t(a) a
f(y+1) + f(f(z)) % t(t(g)(0) + t)(1);
g(x+(3,4)-w) | h 5) & m(f)^m(m);
其結果分別是
f(2 * (y+1)) + f(2 * (f(2 * (z[0])))) % f(2 * (0)) + t(1);
f(2 * (2+(3,4)-0,1)) | f(2 * ( ~ 5)) & f(2 * (0,1))^m(0,1);
對於第一個,主要在於t(t(g)(0) + t)(1)的。
容易計算出最外層的t的實參是f(2 * (0)) + t,而作為t的引數傳入時其中的t是
正在被的巨集,所以根據規則3,不對這個t進行處理,保持不變,得到f(2 * (0)) + t(1)。
對於第二個,h 5)被替換為g(~5),應用規則2,被替換為f(2 * ( ~ 5))。
而m(m)首先被替換為m(w),然後應用規則2再次進行替換,但是m已經是替換過的了,所以保持
不變,只對w進行替換。
#define str(s) # s
#define xstr(s) str(s)
#define debug(s, t) printf("x" # s "= %d, x" # t "= %s", \
x ## s, x ## t)
#define incfile(n) vers ## n /* from previous #include example */
#define glue(a, b) a ## b
#define xglue(a, b) glue(a, b)
#define highlow "hello"
#define low low ", world"
debug(1, 2);
fputs(str(strncmp("abc\0d", "abc", 』\4』) /* this goes away */
== 0) str(: @\n), s);
#include xstr(incfile(2).h)
glue(high, low);
xglue(high, low)
其結果分別是
printf("x" "1" "= %d, x" "2" "= %s", x1, x2);
fputs("strncmp(\"abc\\0d\", \"abc\", 』\\4』) = = 0" ": @\n", s);
#include "vers2.h"
"hello";
"hello" ", world"
關鍵是glue和xglue.
對於glue(high, low),首先有一個規則1的抑制,得到highlow;的結果,然後二次掃描,得到
"hello";
對於xglue(high, low)沒有抑制效果,所以對引數求值,分別得到high和low ", world",即
glue(high, low ", world")。
然後進行連線操作得到highlow ", world",最後再掃描一次得到"hello" ", world"
如果考慮字串的自然的連線,就可以得到"hello, world"了。
擴充套件資料
巨集語言是一類程式語言,其全部或多數計算是由擴充套件巨集完成的。巨集語言並未在通用程式設計中廣泛使用,但在文字處理程式中應用普遍。例如, c preprocessor c前處理器internet macros(iopus) m4(如前所述,源於at&t,**於unix)
巨集定義c程式提供的預處理功能之一。包括帶引數的巨集定義和不帶引數的巨集定義。具體是指用一個指定的標誌符來進行簡單的字串替換或者進行闡述替換。形式為:
#define標誌符[(參數列)] 字串
巨集名在上定義中的標誌符被稱為「巨集名」。
巨集在c程式編譯時將巨集名替換成字串的過程稱為「巨集」。
巨集語言是一類程式語言,其全部或多數計算是由擴充套件巨集完成的。巨集語言並未在通用程式設計中廣泛使用, 但在文字處理程式中應用普遍。例如,
c preprocessorc 前處理器
internet macros(iopus)
m4(如前所述,源於at&t,**於unix)
6樓:匿名使用者
簡單來說:巨集定義又稱為巨集代換、巨集替換,簡稱「巨集」。是c提供的三種預處理功能的其中一種。
複雜的請看下面,講的很全。下面的帶參巨集定義,多行巨集定義,在linux核心原始碼中很多。另外sizeof也是一個巨集定義。
巨集定義巨集定義是c提供的三種預處理功能的其中一種,這三種預處理包括:巨集定義、檔案包含、條件編譯
1. 不帶引數的巨集定義:
巨集定義又稱為巨集代換、巨集替換,簡稱「巨集」。
格式: #define 識別符號 字串
其中的識別符號就是所謂的符號常量,也稱為「巨集名」。
預處理(預編譯)工作也叫做巨集:將巨集名替換為字串。
掌握"巨集"概念的關鍵是「換」。一切以換為前提、做任何事情之前先要換,準確理解之前就要「換」。
即在對相關命令或語句的含義和功能作具體分析之前就要換:
例: #define pi 3.1415926 把程式**現的pi全部換成3.1415926
說明:(1)巨集名一般用大寫
(2)使用巨集可提高程式的通用性和易讀性,減少不一致性,減少輸入錯誤和便於修改。例如:陣列大小常用巨集定義
(3)預處理是在編譯之前的處理,而編譯工作的任務之一就是語法檢查,預處理不做語法檢查。
(4)巨集定義末尾不加分號;
(5)巨集定義寫在函式的花括號外邊,作用域為其後的程式,通常在檔案的最開頭。
(6)可以用#undef命令終止巨集定義的作用域
(7)巨集定義可以巢狀
(8)字串" "中永遠不包含巨集
(9)巨集定義不分配記憶體,變數定義分配記憶體。
2. 帶引數的巨集定義:
除了一般的字串替換,還要做引數代換
格式: #define 巨集名(參數列) 字串
例如:#define s(a,b) a*b
area=s(3,2);第一步被換為area=a*b; ,第二步被換為area=3*2;
類似於函式呼叫,有一個啞實結合的過程:
(1)實參如果是表示式容易出問題
#define s(r) r*r
area=s(a+b);第一步換為area=r*r;,第二步被換為area=a+b*a+b;
正確的巨集定義是#define s(r) ((r)*(r))
(2)巨集名和引數的括號間不能有空格
(3)巨集替換隻作替換,不做計算,不做表示式求解
(4)函式呼叫在編譯後程式執行時進行,並且分配記憶體。巨集替換在編譯前進行,不分配記憶體
(5)巨集的啞實結合不存在型別,也沒有型別轉換。
(6)函式只有一個返回值,利用巨集則可以設法得到多個值
(7)巨集使源程式變長,函式呼叫不會
(8)巨集不佔執行時間,只佔編譯時間,函式呼叫佔執行時間(分配記憶體、保留現場、值傳遞、返回值)
3. 巨集定義其他冷門、重點知識
#define用法
1、 用無參巨集定義一個簡單的常量
#define len 12
這個是最常見的用法,但也會出錯。
比如下面幾個知識點你會嗎?可以看下:
(1) #define name "zhangyuncong"
程式中有"name"則,它會不會被替換呢?
(2) #define 0x abcd
可以嗎?也就是說,可不可以用把識別符號的字母替換成別的東西?
(3) #define name "zhang
這個可以嗎?
(4) #define name "zhangyuncong"
程式中有上面的巨集定義,並且,程式裡有句:
namelist這樣,會不會被替換成"zhangyuncong"list
四個題答案都是否定的。
第一個,""內的東西不會被巨集替換。這一點應該大都知道。
第二個,巨集定義前面的那個必須是合法的使用者識別符號
第三個,巨集定義也不是說後面東西隨便寫,不能把字串的兩個""拆開。
第四個:只替換識別符號,不替換別的東西。namelist整體是個識別符號,而沒有name識別符號,所以不替換。
也就是說,這種情況下記住:#define 第一位置第二位置
(1) 不替換程式中字串裡的東西。
(2) 第一位置只能是合法的識別符號(可以是關鍵字)
(3) 第二位置如果有字串,必須把""配對。
(4) 只替換與第一位置完全相同的識別符號
還有就是老生常談的話:記住這是簡單的替換而已,不要在中間計算結果,一定要替換出表示式之後再算。
2、 帶參巨集一般用法
比如#define max(a,b) ((a)>(b)?(a):(b))
則遇到max(1+2,value)則會把它替換成:
((1+2)>(value)?(1+2):(value))
注意事項和無參巨集差不多。
但還是應注意
#define fun(a) "a"
則,輸入fun(345)會被替換成什麼?
其實,如果這麼寫,無論巨集的實參是什麼,都不會影響其被替換成"a"的命運。
也就是說,""內的字元不被當成形參,即使它和一模一樣。
那麼,你會問了,我要是想讓這裡輸入fun(345)它就替換成"345"該怎麼實現呢?
請看下面關於#的用法
3、 有參巨集定義中#的用法
#define str(str) #str
#用於把巨集定義中的引數兩端加上字串的""
比如,這裡str(my#name)會被替換成"my#name"
一般由任意字元都可以做形參,但以下情況會出錯:
str())這樣,編譯器不會把「)」當成str()的引數。
str(,)同上,編譯器不會把「,」當成str的引數。
str(a,b)如果實參過多,則編譯器會把多餘的引數捨去。(vc++2008為例)
str((a,b))會被解讀為實參為:(a,b),而不是被解讀為兩個實參,第一個是(a第二個是b)。 4、 有參巨集定義中##的用法
#define wide(str) l##str
則會將形參str的前面加上l
比如:wide("abc")就會被替換成l"abc"
如果有#define fun(a,b) vo##a##b()
那麼fun(id ma,in)會被替換成void main()
5、 多行巨集定義:
#define doit(m,n) for(int i=0;i<(n);++i)\
設有如下定義 int pp 100則printfd ,p的輸出結果是100,對麼?為什麼
隔壁小鍋 int p p 100 則 printf d p 的輸出結果是100是不對的,p宣告為一個int型指標,但是沒有指向具體的記憶體空間。int型別在記憶體中佔用了4個位元組,也就是32位。int型別是有符號的,因此,32位並不會全部用來儲存資料,使用最高位來儲存符號,最高位是0,提示資料是正...
設有如下定義 intptr則以下敘述中正確的是
墨汁諾 c。ptr函式返回一個int 型資料 這個才是正確的,理由是int fun 顯然是一個函式說明,fun換為 ptr 則ptr就是指向函式的指標了。int ptr m ptr是一個指向m長度的線性記憶體空間,即m大小的一維陣列 int ptr ptr是一個返回值為int 引數列表為空的函式指標...
有誰知道,設有如下定義 int a 2,b 3,c 4則值為0的表示式為
選a這裡考察的是表示式中的邏輯運算子 所以要理解 和 的含義。當其左右兩邊的表示式都為真時,整個表示式的值才為真,否則為假 0 當其左右兩邊的表示式都為假時,整個表示式的值才為假,否則為真.而在c語言判斷一個表示式是否為真的標準就是0是假,非0是真。所以a a 1 b 0 就是 2 1 3 0 2取...