【问题标题】:Confusing MACRO and enum definition令人困惑的 MACRO 和枚举定义
【发布时间】:2016-01-04 06:40:26
【问题描述】:

我正在浏览一些 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

谢谢

【问题讨论】:

  • 原作者应该知道比把#define和enum这样混用,看起来一团糟

标签: c enums c-preprocessor


【解决方案1】:

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

由于每个宏的名称都是#defined,因此main 中的RTNLGRP_NEIGH 将被RTNLGRP_NEIGH 替换。但由于扩展不是递归的,它会在这一点停止,程序使用enum 常量RTNLGRP_NEIGH,它是第四个,因此值为 3。

如果您不确定预处理器的作用,您可以随时使用-E 开关进行编译并查看预处理后的输出。用gcc -E 编译上面的例子给出(没有显示#included 标准库头文件的840 行)

# 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);
}

希望这不会令人困惑。

#defines 混入enum 定义对enum 定义没有影响。 #defines 的位置无关紧要。它们可以(也可能应该)放在定义之前或之后。

/* 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 &gt;= 3 这样的事情,而 RTNLGRP_NEIGH 从字面上看是 #defined 到 3。因此,从本质上讲,他们的方法结合了使用宏(名称空间污染)和使用enums(在预处理时不可用)的缺点。

我以前见过的一个可能更有用的模式是将#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,

};

请注意,此处将#defines 混入enum 定义中至关重要,否则我们会得到无效代码,例如3 = 3,,而不是所需的RTNLGRP_NEIGH = 3

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

【讨论】:

  • 很好的解释。感谢您的宝贵时间。
  • 提问者自己使用__RTNLGRP_MAX作为标识符。问题中的代码来自系统头文件。
【解决方案2】:

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

#define 的东西有点奇怪——这种东西很容易让人想要stop you using the C pre-processor

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

所以,结果是你可以写:

#ifdef RTNLGRP_NEIGH
   …code using RTNLGRP_NEIGH…
#endif

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-01-26
    • 1970-01-01
    • 1970-01-01
    • 2022-01-21
    • 2022-01-17
    相关资源
    最近更新 更多