混淆的宏定义和枚举定义

4

我正在浏览一些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 还是 3?
谢谢。

原作者应该知道不要像那样混合使用 #define 和 enum,看起来很混乱。 - AndersK
2个回答

6
< p > 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

由于每个宏都被定义为其自己的名称,因此在main中的RTNLGRP_NEIGH将被替换为RTNLGRP_NEIGH。但由于该扩展不是递归的,它将在此处停止,并且程序使用第四个枚举常量RTNLGRP_NEIGH,其值为3。
如果您不确定预处理器的作用,可以始终使用-E开关编译并查看预处理输出。使用gcc -E编译上面的示例会产生(未显示840行#include的标准库头文件)。
# 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);
}

希望这篇翻译能让您更易理解。
在枚举定义中混合使用的 #define 对枚举定义没有影响。无论 #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 被直接定义为 3 的话是可以的。因此,实质上,他们的方法结合了使用宏定义(命名空间污染)和使用 enum(无法在预处理时使用)的缺点。
我曾经看到过一种更有用的模式,即将常量定义为实际的整数值。
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,

};

请注意,在这里,将#define混合到enum定义中非常关键,否则我们会得到无效的代码,例如3 = 3,而不是所需的RTNLGRP_NEIGH = 3
另外,请不要使用__RTNLGRP_MAX作为标识符。包含两个相邻下划线或以下划线后跟大写字母的名称已被C标准保留。在您自己的代码中使用它们会导致未定义的行为。

非常好的解释。谢谢你的时间。 - Haswell
提问者并没有__RTNLGRP_MAX作为自己的标识符使用。问题中的代码来自系统头文件。 - user253751

4
RTNLGRP_NEIGH的值为3(它是第四个枚举常量:RTNLGRP_NONE的值为0,RTNLGRP_LINK的值为1,RTNLGRP_NOTIFY的值为2)。 #define的东西有点奇怪 - 这种东西往往会让人想停止使用C预处理器
这样做的想法是为RTNLGRP_NEIGH提供一个可以进行测试的宏,但是宏的扩展是枚举常量(拼写相同)。扩展中没有无限循环,因为一旦宏被扩展,它在重新扫描替换文本时不会再次扩展。
因此,你可以编写以下内容:
#ifdef RTNLGRP_NEIGH
   …code using RTNLGRP_NEIGH…
#endif

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接