C语言宏定义相关

C语言宏定义在代码编写中很常见,它常会带来一些很高的性能和很方便的写法,在看Linux源码中sockaddr_in的时候遇到宏定义中##。特地在此记录.

宏定义中##用法

问题背景

Linux中sockaddr_in的定义在文件/netinet/in.h文件中。具体如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
/* Structure describing an Internet socket address. */
struct sockaddr_in
{
__SOCKADDR_COMMON (sin_);
in_port_t sin_port; // Port number.
struct in_addr sin_addr; // Internet address.
// Pad to size of struct sockaddr.
unsigned char sin_zero[sizeof (struct sockaddr) -
__SOCKADDR_COMMON_SIZE -
sizeof (in_port_t) -
sizeof (struct in_addr)];
};
#define __SOCKADDR_COMMON(sa_prefix)\
sa_family_t sa_prefix##family

从上面可以看出,__SOCKADDR_COMMON的宏定义中出现了##的使用方法。那它在宏定义中的意思是什么呢?

##详解

##是一种分隔连接方式。它的作用是先分隔,然后进行强制连接。

在普通的宏定义中,预处理器一般吧空格解释为分段标志,然后进行相应的替换工作。但是这样做的结果是被替换的段之间会出现空格。如果我们不希望这些空格出现,可以使用##来代替空格。

如:

1
2
#define type1(type,name) type name_##type##_type
#define type2(type,name) type name##_##type##_type

上述type1(int,c)将被替换为:int name_int_type
上述type2(int,c)将被替换为:int c_int_type

故我们再回去看SOCKADDR_COMMON的宏定义. SOCKADDRCOMMON (sin);将被解释为sa_family_t sin_family;

注意在函数参数中使用##可以将空字符串过滤掉,否则会出现问题。如:

1
2
3
4
5
6
7
8
9
10
11
12
13
#define comac_get_args_cnt( ... ) comac_arg_n( __VA_ARGS__ )
#define comac_arg_n( _0,_1,_2,_3,_4,_5,_6,_7,N,...) N
#define comac_args_seqs() 7,6,5,4,3,2,1,0
#define comac_join_1( x,y ) x##y
//计算可变参数...中参数的个数.注意由于seq的范围为7-0,该宏可识别的参数个数的范围为0-7个。超过7个参数识别有问题
#define comac_argc( ... ) comac_get_args_cnt( 0,##__VA_ARGS__,comac_args_seqs() )
int main() {
//若上述comac_get_cnt中的__VA_ARGS__去掉##,结果为1,加上为0。
comac_argc();
}

#详解

在宏定义中,#符号的作用是将宏定义参数用””括起来,

1
2
#define example(test) printf("%s", #test);
example(123 456); //printf("%s", "123 456");

宏定义中占位符理解

在宏定义中我们可以使用字母或者单词作为占位符,初次之外,我们也可以使用_1,_2作为占位符,不要被它蒙骗了,其作用与单词作为占位符相同。如:

1
2
#define sum(a,b) (a+b)
#define sum(_1,_2) (_1+_2)//作用同上