令人困惑的MACRO和枚举定义

时间:2016-01-04 06:40:27

标签: c enums c-preprocessor

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

#strong> enum与#define 在做什么。 RTNLGRP_NEIGH 的价值是多少? 6 OR 3

由于

2 个答案:

答案 0 :(得分:5)

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

如果您不确定预处理器的作用,您始终可以使用-E开关进行编译并查看预处理的输出。使用gcc -E编译上面的示例(不显示#include d标准库头的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);
}

希望更加容易混淆。

混合到#define定义中的enumenum定义没有影响。 #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 #define3 d enum字面意思。因此,从本质上讲,他们的方法结合了使用宏(名称空间污染)和使用#define s(在预处理时不可用)的缺点。

我之前看到的一个可能更有用的模式是将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

请注意,此处enum3 = 3,定义混合在一起至关重要,否则我们会收到RTNLGRP_NEIGH = 3之类的无效代码,而不是__RTNLGRP_MAX }}

哦,请不要使用java -version java version "1.8.0_66" Java(TM) SE Runtime Environment (build 1.8.0_66-b17) Java HotSpot(TM) 64-Bit Server VM (build 25.66-b17, mixed mode) 作为标识符。包含两个相邻下划线或以下划线开头后跟大写字母的名称由C标准保留。在您自己的代码中使用它们会导致未定义的行为。

答案 1 :(得分:4)

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