混淆 MACRO 和枚举定义
Confusing MACRO and enum definition
我正在浏览一些 Route netlink 源代码。
我想弄清楚 RTNLGRP_NEIGH
的值是多少
来源:http://lxr.free-electrons.com/source/include/linux/rtnetlink.h?v=2.6.35#L550
541 /* RTnetlink multicast groups */
542 enum rtnetlink_groups {
543 RTNLGRP_NONE,
544 #define RTNLGRP_NONE RTNLGRP_NONE
545 RTNLGRP_LINK,
546 #define RTNLGRP_LINK RTNLGRP_LINK
547 RTNLGRP_NOTIFY,
548 #define RTNLGRP_NOTIFY RTNLGRP_NOTIFY
549 RTNLGRP_NEIGH,
550 #define RTNLGRP_NEIGH RTNLGRP_NEIGH
551 RTNLGRP_TC,
552 #define RTNLGRP_TC RTNLGRP_TC
553 RTNLGRP_IPV4_IFADDR,
554 #define RTNLGRP_IPV4_IFADDR RTNLGRP_IPV4_IFADDR
... ...
... ...
#define RTNLGRP_PHONET_IFADDR RTNLGRP_PHONET_IFADDR
585 RTNLGRP_PHONET_ROUTE,
586 #define RTNLGRP_PHONET_ROUTE RTNLGRP_PHONET_ROUTE
587 __RTNLGRP_MAX
588 };
589 #define RTNLGRP_MAX (__RTNLGRP_MAX - 1)
#define 这个 enum 是做什么的。
RTNLGRP_NEIGH 的值是多少? 6 OR 3
谢谢
RTNLGRP_NEIGH
的值为3(它是第四个枚举常量:RTNLGRP_NONE
的值为0,RTNLGRP_LINK
的值为1,RTNLGRP_NOTIFY
值为 2).
#define
的东西有点奇怪 — 它是那种容易让人想要 的东西。
的思路是给你一个可以测试的RTNLGRP_NEIGH
的宏,但是这个宏的扩展就是枚举常量(拼写一样)。扩展中没有无限循环,因为一旦宏被扩展,它就不会在重新扫描替换文本时再次扩展。
所以,结果是你可以这样写:
#ifdef RTNLGRP_NEIGH
…code using RTNLGRP_NEIGH…
#endif
RTNLGRP_NEIGH
的值为 3。您可以使用以下程序轻松测试它。
#include <stdio.h>
/* RTnetlink multicast groups */
enum rtnetlink_groups {
RTNLGRP_NONE,
#define RTNLGRP_NONE RTNLGRP_NONE
RTNLGRP_LINK,
#define RTNLGRP_LINK RTNLGRP_LINK
RTNLGRP_NOTIFY,
#define RTNLGRP_NOTIFY RTNLGRP_NOTIFY
RTNLGRP_NEIGH,
#define RTNLGRP_NEIGH RTNLGRP_NEIGH
RTNLGRP_TC,
#define RTNLGRP_TC RTNLGRP_TC
RTNLGRP_IPV4_IFADDR,
#define RTNLGRP_IPV4_IFADDR RTNLGRP_IPV4_IFADDR
/* ... */
#define RTNLGRP_PHONET_IFADDR RTNLGRP_PHONET_IFADDR
RTNLGRP_PHONET_ROUTE,
#define RTNLGRP_PHONET_ROUTE RTNLGRP_PHONET_ROUTE
__RTNLGRP_MAX
};
#define RTNLGRP_MAX (__RTNLGRP_MAX - 1)
int
main()
{
printf("RTNLGRP_NEIGH = %d\n", RTNLGRP_NEIGH);
}
它输出这个:
RTNLGRP_NEIGH = 3
由于每个宏都是 #define
d 到它自己的名称,main
中的 RTNLGRP_NEIGH
将被 RTNLGRP_NEIGH
替换。但是由于展开不是递归的,它会在此时停止并且程序使用 enum
常量 RTNLGRP_NEIGH
这是第四个,因此值为 3.
如果您不确定预处理器的作用,您可以随时使用 -E
开关进行编译并查看 pre-processed 输出。用 gcc -E
编译上面的例子给出(不显示 840 行的 #include
d 标准库头文件)
# 4 "main.c"
enum rtnetlink_groups {
RTNLGRP_NONE,
RTNLGRP_LINK,
RTNLGRP_NOTIFY,
RTNLGRP_NEIGH,
RTNLGRP_TC,
RTNLGRP_IPV4_IFADDR,
RTNLGRP_PHONET_ROUTE,
__RTNLGRP_MAX
};
int
main()
{
printf("RTNLGRP_NEIGH = %d\n", RTNLGRP_NEIGH);
}
希望这不会造成混淆。
混入enum
定义的#define
对enum
定义没有影响。 #define
位于何处并不重要。它们可以(而且可能应该)放在定义之前或之后。
/* RTnetlink multicast groups */
enum rtnetlink_groups {
RTNLGRP_NONE,
RTNLGRP_LINK,
RTNLGRP_NOTIFY,
RTNLGRP_NEIGH,
RTNLGRP_TC,
RTNLGRP_IPV4_IFADDR,
/* ... */
RTNLGRP_PHONET_ROUTE,
__RTNLGRP_MAX
};
#define RTNLGRP_NONE RTNLGRP_NONE
#define RTNLGRP_LINK RTNLGRP_LINK
#define RTNLGRP_NOTIFY RTNLGRP_NOTIFY
#define RTNLGRP_NEIGH RTNLGRP_NEIGH
#define RTNLGRP_TC RTNLGRP_TC
#define RTNLGRP_IPV4_IFADDR RTNLGRP_IPV4_IFADDR
#define RTNLGRP_PHONET_IFADDR RTNLGRP_PHONET_IFADDR
/* ... */
#define RTNLGRP_PHONET_ROUTE RTNLGRP_PHONET_ROUTE
#define RTNLGRP_MAX (__RTNLGRP_MAX - 1)
他们写这段奇怪代码的原因可能是他们想使用
重构旧代码
#define RTNLGRP_NONE 0
#define RTNLGRP_LINK 1
#define RTNLGRP_NOTIFY 2
#define RTNLGRP_NEIGH 3
#define RTNLGRP_TC 4
#define RTNLGRP_IPV4_IFADDR 5
/* ... */
改为使用 enum
。但是因为现有代码可能依赖于标识符是宏这一事实(例如测试 #ifdef RTNLGRP_NEIGH
),所以他们希望提供具有相同值的宏。但是请注意,这种方法是有缺陷的,因为预处理器不知道常量的值,所以你不能做像 #if RTNLGRP_NEIGH >= 3
这样的事情,如果 RTNLGRP_NEIGH
已经 #define
d 到3
字面意思。因此,从本质上讲,他们的方法结合了使用宏(name-space 污染)和使用 enum
s(在 pre-processing 时间不可用)的缺点。
我以前见过的一个可能更有用的模式是 #define
将常量转换为实际整数。
enum rtnetlink_groups {
RTNLGRP_NONE
#define RTNLGRP_NONE 0
= RTNLGRP_NONE,
RTNLGRP_LINK
#define RTNLGRP_LINK 1
= RTNLGRP_LINK,
RTNLGRP_NOTIFY
#define RTNLGRP_NOTIFY 2
= RTNLGRP_NOTIFY,
RTNLGRP_NEIGH
#define RTNLGRP_NEIGH 3
= RTNLGRP_NEIGH,
RTNLGRP_TC
#define RTNLGRP_TC 4
= RTNLGRP_TC,
RTNLGRP_IPV4_IFADDR
#define RTNLGRP_IPV4_IFADDR 5
= RTNLGRP_IPV4_IFADDR,
/* ... */
};
这将是 pre-processed 以下。
enum rtnetlink_groups {
RTNLGRP_NONE
= 0,
RTNLGRP_LINK
= 1,
RTNLGRP_NOTIFY
= 2,
RTNLGRP_NEIGH
= 3,
RTNLGRP_TC
= 4,
RTNLGRP_IPV4_IFADDR
= 5,
};
请注意,将 #define
混合到 enum
定义中至关重要,否则我们会得到无效代码,例如 3 = 3,
而不是所需的 RTNLGRP_NEIGH = 3
.
哦,请不要使用 __RTNLGRP_MAX
作为标识符。包含两个相邻下划线或以下划线开头后跟 upper-case 字母的名称由 C 标准保留。在您自己的代码中使用它们会导致未定义的行为。
我正在浏览一些 Route netlink 源代码。
我想弄清楚 RTNLGRP_NEIGH
的值是多少来源:http://lxr.free-electrons.com/source/include/linux/rtnetlink.h?v=2.6.35#L550
541 /* RTnetlink multicast groups */
542 enum rtnetlink_groups {
543 RTNLGRP_NONE,
544 #define RTNLGRP_NONE RTNLGRP_NONE
545 RTNLGRP_LINK,
546 #define RTNLGRP_LINK RTNLGRP_LINK
547 RTNLGRP_NOTIFY,
548 #define RTNLGRP_NOTIFY RTNLGRP_NOTIFY
549 RTNLGRP_NEIGH,
550 #define RTNLGRP_NEIGH RTNLGRP_NEIGH
551 RTNLGRP_TC,
552 #define RTNLGRP_TC RTNLGRP_TC
553 RTNLGRP_IPV4_IFADDR,
554 #define RTNLGRP_IPV4_IFADDR RTNLGRP_IPV4_IFADDR
... ...
... ...
#define RTNLGRP_PHONET_IFADDR RTNLGRP_PHONET_IFADDR
585 RTNLGRP_PHONET_ROUTE,
586 #define RTNLGRP_PHONET_ROUTE RTNLGRP_PHONET_ROUTE
587 __RTNLGRP_MAX
588 };
589 #define RTNLGRP_MAX (__RTNLGRP_MAX - 1)
#define 这个 enum 是做什么的。
RTNLGRP_NEIGH 的值是多少? 6 OR 3
谢谢
RTNLGRP_NEIGH
的值为3(它是第四个枚举常量:RTNLGRP_NONE
的值为0,RTNLGRP_LINK
的值为1,RTNLGRP_NOTIFY
值为 2).
#define
的东西有点奇怪 — 它是那种容易让人想要
的思路是给你一个可以测试的RTNLGRP_NEIGH
的宏,但是这个宏的扩展就是枚举常量(拼写一样)。扩展中没有无限循环,因为一旦宏被扩展,它就不会在重新扫描替换文本时再次扩展。
所以,结果是你可以这样写:
#ifdef RTNLGRP_NEIGH
…code using RTNLGRP_NEIGH…
#endif
RTNLGRP_NEIGH
的值为 3。您可以使用以下程序轻松测试它。
#include <stdio.h>
/* RTnetlink multicast groups */
enum rtnetlink_groups {
RTNLGRP_NONE,
#define RTNLGRP_NONE RTNLGRP_NONE
RTNLGRP_LINK,
#define RTNLGRP_LINK RTNLGRP_LINK
RTNLGRP_NOTIFY,
#define RTNLGRP_NOTIFY RTNLGRP_NOTIFY
RTNLGRP_NEIGH,
#define RTNLGRP_NEIGH RTNLGRP_NEIGH
RTNLGRP_TC,
#define RTNLGRP_TC RTNLGRP_TC
RTNLGRP_IPV4_IFADDR,
#define RTNLGRP_IPV4_IFADDR RTNLGRP_IPV4_IFADDR
/* ... */
#define RTNLGRP_PHONET_IFADDR RTNLGRP_PHONET_IFADDR
RTNLGRP_PHONET_ROUTE,
#define RTNLGRP_PHONET_ROUTE RTNLGRP_PHONET_ROUTE
__RTNLGRP_MAX
};
#define RTNLGRP_MAX (__RTNLGRP_MAX - 1)
int
main()
{
printf("RTNLGRP_NEIGH = %d\n", RTNLGRP_NEIGH);
}
它输出这个:
RTNLGRP_NEIGH = 3
由于每个宏都是 #define
d 到它自己的名称,main
中的 RTNLGRP_NEIGH
将被 RTNLGRP_NEIGH
替换。但是由于展开不是递归的,它会在此时停止并且程序使用 enum
常量 RTNLGRP_NEIGH
这是第四个,因此值为 3.
如果您不确定预处理器的作用,您可以随时使用 -E
开关进行编译并查看 pre-processed 输出。用 gcc -E
编译上面的例子给出(不显示 840 行的 #include
d 标准库头文件)
# 4 "main.c"
enum rtnetlink_groups {
RTNLGRP_NONE,
RTNLGRP_LINK,
RTNLGRP_NOTIFY,
RTNLGRP_NEIGH,
RTNLGRP_TC,
RTNLGRP_IPV4_IFADDR,
RTNLGRP_PHONET_ROUTE,
__RTNLGRP_MAX
};
int
main()
{
printf("RTNLGRP_NEIGH = %d\n", RTNLGRP_NEIGH);
}
希望这不会造成混淆。
混入enum
定义的#define
对enum
定义没有影响。 #define
位于何处并不重要。它们可以(而且可能应该)放在定义之前或之后。
/* RTnetlink multicast groups */
enum rtnetlink_groups {
RTNLGRP_NONE,
RTNLGRP_LINK,
RTNLGRP_NOTIFY,
RTNLGRP_NEIGH,
RTNLGRP_TC,
RTNLGRP_IPV4_IFADDR,
/* ... */
RTNLGRP_PHONET_ROUTE,
__RTNLGRP_MAX
};
#define RTNLGRP_NONE RTNLGRP_NONE
#define RTNLGRP_LINK RTNLGRP_LINK
#define RTNLGRP_NOTIFY RTNLGRP_NOTIFY
#define RTNLGRP_NEIGH RTNLGRP_NEIGH
#define RTNLGRP_TC RTNLGRP_TC
#define RTNLGRP_IPV4_IFADDR RTNLGRP_IPV4_IFADDR
#define RTNLGRP_PHONET_IFADDR RTNLGRP_PHONET_IFADDR
/* ... */
#define RTNLGRP_PHONET_ROUTE RTNLGRP_PHONET_ROUTE
#define RTNLGRP_MAX (__RTNLGRP_MAX - 1)
他们写这段奇怪代码的原因可能是他们想使用
重构旧代码#define RTNLGRP_NONE 0
#define RTNLGRP_LINK 1
#define RTNLGRP_NOTIFY 2
#define RTNLGRP_NEIGH 3
#define RTNLGRP_TC 4
#define RTNLGRP_IPV4_IFADDR 5
/* ... */
改为使用 enum
。但是因为现有代码可能依赖于标识符是宏这一事实(例如测试 #ifdef RTNLGRP_NEIGH
),所以他们希望提供具有相同值的宏。但是请注意,这种方法是有缺陷的,因为预处理器不知道常量的值,所以你不能做像 #if RTNLGRP_NEIGH >= 3
这样的事情,如果 RTNLGRP_NEIGH
已经 #define
d 到3
字面意思。因此,从本质上讲,他们的方法结合了使用宏(name-space 污染)和使用 enum
s(在 pre-processing 时间不可用)的缺点。
我以前见过的一个可能更有用的模式是 #define
将常量转换为实际整数。
enum rtnetlink_groups {
RTNLGRP_NONE
#define RTNLGRP_NONE 0
= RTNLGRP_NONE,
RTNLGRP_LINK
#define RTNLGRP_LINK 1
= RTNLGRP_LINK,
RTNLGRP_NOTIFY
#define RTNLGRP_NOTIFY 2
= RTNLGRP_NOTIFY,
RTNLGRP_NEIGH
#define RTNLGRP_NEIGH 3
= RTNLGRP_NEIGH,
RTNLGRP_TC
#define RTNLGRP_TC 4
= RTNLGRP_TC,
RTNLGRP_IPV4_IFADDR
#define RTNLGRP_IPV4_IFADDR 5
= RTNLGRP_IPV4_IFADDR,
/* ... */
};
这将是 pre-processed 以下。
enum rtnetlink_groups {
RTNLGRP_NONE
= 0,
RTNLGRP_LINK
= 1,
RTNLGRP_NOTIFY
= 2,
RTNLGRP_NEIGH
= 3,
RTNLGRP_TC
= 4,
RTNLGRP_IPV4_IFADDR
= 5,
};
请注意,将 #define
混合到 enum
定义中至关重要,否则我们会得到无效代码,例如 3 = 3,
而不是所需的 RTNLGRP_NEIGH = 3
.
哦,请不要使用 __RTNLGRP_MAX
作为标识符。包含两个相邻下划线或以下划线开头后跟 upper-case 字母的名称由 C 标准保留。在您自己的代码中使用它们会导致未定义的行为。