赞
踩
c语言的 define 语句虽然看起来很简单,大部分的时候都只是给某个变量或者函数加上一个别名或者进行简单的运算,但 define 的功能还有其他一些十分方便的操作,用好了能够极大的方便我们编程。
#define PI 3.1415926 // 定义一些常量
#define LOG(...) printf(__VA_ARGS__) // 函数取别名
#define max(a, b) (a) > (b) ? (a) : (b) // 进行简单运算
LOG("你好,%s\n", "谢老板不用蟹");
printf("max(10, 8) = %d\n", max(10, 8));
output:
你好,谢老板不用蟹
max(10, 8) = 10
在一行的结尾处加上反斜杠符号 \ 即可
#define PRINT \
{ \
printf("define 1\n"); \
printf("define 2\n"); \
printf("define 3\n"); \
}
PRINT;
output:
define 1
define 2
define 3
该功能有时候会非常实用,比如说 float -> char*[] 如果自己写函数的话要进行各种运算存储,非常麻烦,但是使用宏的话就会变得非常简单。
注:# 号以及后面出现的 ## 号后面紧挨着的东西只能是宏定义传进来的参数。
#define String(a) #a
printf("String(92.84) = %s\n", String(92.84));
output:
String(92.84) = 92.84
宏定义(定义的一定要是字符串常量)之间的拼接用空格隔开,不能紧挨着,不然会被判定成另一个宏。
宏定义(定义的一定要是字符串常量)与字符串常量之间的拼接可以用空格隔开,也可以紧挨着。
#define S1 "hello"
#define S2 "world"
#define STR1 S1 S2
#define STR2 "/" S1 " the " S2 "/" // 或者"/"S1" the "S2"/"
printf("STR1 = %s\n", STR1);
printf("STR2 = %s\n", STR2);
output:
STR1 = helloworld
STR2 = /hello the world/
宏定义传入的参数如果是字符串类型的,与其他定义是字符串类型的宏定义或者其他字符串常量连接时使用 ## 号连接。
宏定义传入的参数如果是其他基本类型的,在相连时要加上 # 号,将其变成字符串类型。
注:前面的 # 号以及 ## 号后面紧挨着的东西只能是宏定义传进来的参数。
#define S1 " Tom "
#define S2 " Jerry "
#define STR1(a, b) S1##a##S2##b // 拼接字符串时 a 和 b 传入的参数只能是字符串类型
#define STR2(dev, i) dev ## #i // 这种情况 i 就可以传入其他基本类型的变量了
#define VAR(fn, id) int __count_##fn##_##id // 这种情况传入的参数不能为字符串
#define STR3(a) S1 S2 #a
VAR(temp, 0);
printf("STR1(\"and\", \"is friend\") = %s\n", STR1("and", "is friend"));
printf("STR2(\"dev_\", 2)", STR2("dev_", 2));
printf("STR3(\"aaa\") = %s\n", STR3("aaa"));
printf("STR3(9.9) = %s\n", STR3(9.9));
output:
int __count_temp_0;
STR1("and", "is friend") = Tom and Jerry is friend
STR2("dev_", 2) = dev_2
STR3("aaa") = Tom Jerry "aaa"
STR3(9.9) = Tom Jerry 9.9
①#define STR1(a, b) S1##a##S2##b
中传入的参数 a 和 b 只能是字符串类型。比如STR1("and", "is friend")
,但是这种定义方法不能传入同是定义了字符串类型的宏,比如:
#define S1 "/"
#define S2 "/"
#define A "menu"
#define B "osd"
#define STR1(a, b) S1##a##S2##b
// printf("%s", STR1(A, B)); // 错误,因为这样编译器不会将A和B定义的内容进行展开
如果想要传入的参数可以支持宏的话,要加多一层中间转换宏,加这层宏的用意是把所有宏的参数在这层里全部展开,比如:
#define S1 "/"
#define S2 "/"
#define A "menu"
#define B "osd"
#define _STR1(a, b) S1##a##S2##b
#define STR1(a, b) _STR1(a, b)
printf("%s\n", STR1(A, B)); // 正确
printf("%s\n", STR1("hello", "world"));
output:
/menu/osd
/hello/world
②如果像是#define STR1(a, b) S1##a##S2##b
和#define STR2(dev, i) dev ## #i
这两个语句使用 gcc 进行编译的话,很可能会出现这样的错误:
test.c:36:37: error: pasting ""hello"" and ""world"" does not give a valid preprocessing token
printf("C(\"hello\",\"world\")", C("hello", "world"));
^
而在 VS 中却不会出现这种情况,原因是原文链接,就是说根据 C 标准,用 ## 操作后的结果必须是一个已经预定义过的符号。否则是未定义的。所以 gcc 和 vs 对于这个未定义行为表示了不同的看法,前者是给出错误,后者一笑而过。那什么是已经预定过的符号呢? 它包含了这些:头文件名、等式、预处理数字、字符常数、字符串值、标点符号、单个非空字符。
那如何解决这个错误呢?如果看到这个错误的话,只需将 ## 去掉后很大概率这个错误就会被解决到了。比如说这个例子中:
没去除前(错误写法):
#include "stdio.h"
#define S1 "/"
#define S2 "/"
#define STR1(a, b) S1##a##S2##b
#define STR2(dev, i) dev ## #i
#define STRUCTMEMBER(Member, Value) a.##Member = Value
struct {
char ch;
int i;
}a;
void main()
{
STRUCTMEMBER(ch, 'x');
printf("a.ch = %c\n", a.ch);
printf("%s\n", STR1("hello", "world"));
printf("%s\n", STR2("hello", 0));
}
报错:
去除后(正确写法):
#include "stdio.h"
#define S1 "/"
#define S2 "/"
#define STR1(a, b) S1 a S2 b
#define STR2(dev, i) dev #i
#define STRUCTMEMBER(Member, Value) a.Member = Value
struct {
char ch;
int i;
}a;
void main()
{
STRUCTMEMBER(ch, 'x');
printf("a.ch = %c\n", a.ch);
printf("%s\n", STR1("hello", "world"));
printf("%s\n", STR2("hello", 0));
}
output:
a.ch = x
/hello/world
hello0
参考文章:关于宏##的使用注意一点
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。