令人困惑的MACRO和枚举定义

我正在浏览一些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做的这个枚举是什么。 RTNLGRP_NEIGH的价值是多少6 OR 3

谢谢

RTNLGRP_NEIGH的值为3.您可以使用以下程序轻松测试。

 #include  /* 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为自己的名称,因此RTNLGRP_NEIGH中的RTNLGRP_NEIGH将被RTNLGRP_NEIGH替换。 但由于扩展不是递归的,它将在此时停止并且程序使用enum常量RTNLGRP_NEIGH ,这是第四个因此具有值3。

如果您不确定预处理器的作用,您始终可以使用-E开关进行编译并查看预处理的输出。 用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定义中的#defineenum定义没有影响。 #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) 

他们编写这个weired代码的原因可能是他们想要使用重构旧代码

 #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字面RTNLGRP_NEIGH #define d。 因此,从本质上讲,他们的方法结合了使用宏(名称空间污染)和使用enum (在预处理时不可用)的缺点。

我之前看到的一个可能更有用的模式是#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, /* ... */ }; 

将预处理到以下。

 enum rtnetlink_groups { RTNLGRP_NONE = 0, RTNLGRP_LINK = 1, RTNLGRP_NOTIFY = 2, RTNLGRP_NEIGH = 3, RTNLGRP_TC = 4, RTNLGRP_IPV4_IFADDR = 5, }; 

请注意,这里, RTNLGRP_NEIGH = 3定义混合到enum定义中是至关重要的,否则我们会得到无效的代码,例如3 = 3,而不是期望的RTNLGRP_NEIGH = 3

哦,请不要使用__RTNLGRP_MAX作为标识符。 包含两个相邻下划线或以下划线开头,后跟大写字母的名称由C标准保留。 在您自己的代码中使用它们会导致未定义的行为。

RTNLGRP_NEIGH的值将为3(它是第四个枚举常量: RTNLGRP_NONE的值为0, RTNLGRP_LINK的值为1, RTNLGRP_NOTIFY的值为2)。

#define东西有些奇怪 – 这种东西很容易让人们想要阻止你使用C预处理器 。

这个想法是它给你一个可以测试的RTNLGRP_NEIGH的宏,但是宏的扩展是枚举常量(拼写相同)。 扩展中没有无限循环,因为一旦宏被扩展,在重新扫描替换文本时它不会再次展开。

所以,结果是你可以写:

 #ifdef RTNLGRP_NEIGH …code using RTNLGRP_NEIGH… #endif