C C 巨集 巨集的作用 副作用

2021-10-07 16:07:16 字數 3295 閱讀 4625

基礎:

巨集是預處理指令;

巨集的本質是簡單的字串替換,預處理時進行巨集替換;

可在定義巨集時要求它接收引數,巨集替換時會代入引數;

巨集的名字不允許過載;

巨集預處理**沒有能力處理遞迴呼叫。

作用:

看一段math.h中的巨集定義:

#if defined _use_math_defines && !defined _math_defines_defined

#define _math_defines_defined

// definitions of useful mathematical constants

//// define _use_math_defines before including to expose these macro

// definitions for common math constants. these are placed under an #ifdef

// since these commonly-defined names are not part of the c or c++ standards

#define m_e 2.71828182845904523536

// e

#define m_log2e 1.44269504088896340736

// log2(e)

#define m_log10e 0.434294481903251827651

// log10(e)

#define m_ln2 0.693147180559945309417

// ln(2)

#define m_ln10 2.30258509299404568402

// ln(10)

#define m_pi 3.14159265358979323846

// pi

#define m_pi_2 1.57079632679489661923

// pi/2

#define m_pi_4 0.785398163397448309616

// pi/4

#define m_1_pi 0.318309886183790671538

// 1/pi

#define m_2_pi 0.636619772367581343076

// 2/pi

#define m_2_sqrtpi 1.12837916709551257390

// 2/sqrt(pi)

#define m_sqrt2 1.41421356237309504880

// sqrt(2)

#define m_sqrt1_2 0.707106781186547524401

// 1/sqrt(2)

#endif

要使用m_pi:

#define _use_math_defines

//#undef _use_math_defines

#include

問題:這樣的巨集定義是否真的有存在的必要?答案是否。

巨集定義「函式」:

乙個簡單的加法:

#define add(a,b) a+b
***:

int z =

add(1,

10)*add(1

,10)//期望11*11=121;實際結果為21;

這很簡單,即1+10*1+10;為了避免這種情況,巨集定義時多加括號:

#define add(a,b) (a+b)
然而,有些***是無法避免的:

#include

#define min(a,b) ((a)<(b))?(a):(b)

intmain

(int argc,

char

** ar**)

會輸出3,11,2;即x++執行了兩次,為什麼?展開:

int z =

((x++

)<

(y++))

?(x++):

(y++

);

判斷後一次,賦值後一次。

巨集只有在展開後才能被編譯器看到,因此,如果在巨集裡面存在錯誤,只有當巨集展開後才可能發現。編譯器無法在定義巨集的時候發現錯誤,這就導致關於巨集的報錯資訊常常晦澀不清,讓人不明所以。而不幸的是幾乎每個巨集都帶有一些美中不足甚至是缺陷。

建議:

預處理程式本身的能力非常有限,你根本沒必要也不可能處理太複雜的巨集;巨集在c語言中非常重要,但在c++中的作用就小多了;實際上,在現代c++語言中,auto、constexpr、const、decltype、enum、inline、lambda表示式、namespace和template機制可以完成原來的預處理機制的大多數功能,如:

const

double pi =

3.14159265358979323846

;template

<

class

t>

inline

const t&

min(

const t& a,

const t& b)

因此,建議只有在進行條件編譯尤其是執行包含檔案防護的任務時再使用巨集。

note:

##巨集運算子可以把兩個字串拼接成乙個:

#define name(a,b) a##b 

intname

(a, func)()

;//會展開成int afunc();

在置換字串中,如果引數的名字前面有乙個單獨的#,表示此處是包含巨集引數的字串:

#define printx(x) std::cout << #x "=" << x << std::endl

//int a = 1;

//printx(a); //std::cout << "a" << "=" << a << std::endl;

巨集的引數列表可以為空,甚至可以是可變引數的,不贅述。

巨集的副作用

巨集定義在實際使用中如果不稍加注意,如果表示式有 就可能導致返回的結果跟預期不符。下面是幾種常見的問題 define multi a,b a b multi 1 2,3 1 2 3 define add a,b a b int c add 1,2 3 1 2 3因此巨集的引數和返回值最好是用 擴起來...

巨集的一些副作用

1 優先順序問題 1 傳入變數優先順序 define multi a,b a b multi 1 2,3 1 2 3 其實是想要 1 2 3 2 作為值返回時,類似1 define add a,b a b int c add a,b 3 a b 3 其實是想要 a b 3 所以,一般的規則是 巨集裡...

D REENTRANT 巨集作用

2.reentrant巨集 在乙個多執行緒程式裡,預設情況下,只有乙個errno變數供所有的執行緒共享。在乙個執行緒準備獲取剛才的錯誤 時,該變數很容易被另乙個執行緒中的函式呼叫所改變。類似的問題還存在於fputs之類的函式中,這些函式通常用乙個單獨的全域性性區域來快取輸出資料。為解決這個問題,需要...