巨集是簡單的字串替換

巨集是簡單的字串替換。 (嚴格地說,它們使用預處理令牌,而不是任意字串。)

#include <stdio.h>

#define SQUARE(x) x*x

int main(void) {
    printf("%d\n", SQUARE(1+2));
    return 0;
}

你可能希望此程式碼列印 93*3),但實際上會列印 5,因為巨集將擴充套件為 1+2*1+2

你應該將引數和整個巨集表示式包裝在括號中以避免此問題。

#include <stdio.h>

#define SQUARE(x) ((x)*(x))

int main(void) {
    printf("%d\n", SQUARE(1+2));
    return 0;
}

另一個問題是巨集的引數不能保證只被評估一次; 它們可能根本不被評估,或者可能被多次評估。

#include <stdio.h>

#define MIN(x, y) ((x) <= (y) ? (x) : (y))

int main(void) {
    int a = 0;
    printf("%d\n", MIN(a++, 10));
    printf("a = %d\n", a);
    return 0;
}

在此程式碼中,巨集將擴充套件為 ((a++) <= (10) ? (a++) : (10))。由於 a++0)小於 10a++將被評估兩次,它將使 a 的值和 MIN 返回的值與你的預期不同。

這可以通過使用函式來避免,但請注意,型別將由函式定義修復,而巨集可以(太)靈活地使用型別。

#include <stdio.h>

int min(int x, int y) {
    return x <= y ? x : y;
}

int main(void) {
    int a = 0;
    printf("%d\n", min(a++, 10));
    printf("a = %d\n", a);
    return 0;
}

現在雙重評估的問題是固定的,但是這個 min 函式不能在不截斷的情況下處理 double 資料。

巨集指令可以有兩種型別:

#define OBJECT_LIKE_MACRO     followed by a "replacement list" of preprocessor tokens
#define FUNCTION_LIKE_MACRO(with, arguments) followed by a replacement list

區分這兩種型別的巨集的是在 #define 之後跟隨識別符號的字元:如果它是一個 lparen ,它是一個類似函式的巨集; 否則,它是一個類似物件的巨集。如果打算編寫一個類似函式的巨集,巨集的名稱末尾和 ( 之間一定不能有空格。請檢視詳細說明。

Version >= C99

在 C99 或更高版本中,你可以使用 static inline int min(int x, int y) { … }

Version >= C11

在 C11 中,你可以為 min 寫一個’type-generic’表示式。

#include <stdio.h>

#define min(x, y) _Generic((x), \
                        long double: min_ld, \
                        unsigned long long: min_ull, \
                        default: min_i \
                        )(x, y)

#define gen_min(suffix, type) \
    static inline type min_##suffix(type x, type y) { return (x < y) ? x : y; }

gen_min(ld, long double)
gen_min(ull, unsigned long long)
gen_min(i, int)

int main(void)
{
    unsigned long long ull1 = 50ULL;
    unsigned long long ull2 = 37ULL;
    printf("min(%llu, %llu) = %llu\n", ull1, ull2, min(ull1, ull2));
    long double ld1 = 3.141592653L;
    long double ld2 = 3.141592652L;
    printf("min(%.10Lf, %.10Lf) = %.10Lf\n", ld1, ld2, min(ld1, ld2));
    int i1 = 3141653;
    int i2 = 3141652;
    printf("min(%d, %d) = %d\n", i1, i2, min(i1, i2));
    return 0;
}

通用表示式可以擴充套件為更多型別,例如 doublefloatlong longunsigned longlongunsigned - 以及寫入的適當的 gen_min 巨集呼叫。