宏是简单的字符串替换。(严格来说,它们使用预处理令牌,而不是任意字符串。)
#include <stdio.h> #define SQUARE(x) x*x int main(void) { printf("%d\n", SQUARE(1+2)); return 0; }
您可能希望此代码可以打印9(3*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)小于10,a++将被评估两次,这将使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,则它是一个类似于函数的宏;否则,它是一个类似对象的宏。如果要编写类似函数的宏,则宏名称的末尾与之间不得有任何空格(。检查此内容以获取详细说明。
在C99或更高版本中,您可以使用。static inline int min(int x, int y) { … }
在C11中,您可以为编写“类型通用”表达式min。
#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; }
通用表达可以与多种类型的,例如被扩展double,float,long long,unsigned long,long,unsigned-和适当的gen_min宏调用写入。