Je parcourais du code source Route netlink.

Je voulais savoir quelle était la valeur de RTNLGRP_NEIGH

Source: 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)

Que fait cette énumération avec #define ? Quelle sera la valeur de RTNLGRP_NEIGH ? 6 OR 3

Merci

3
Haswell 4 janv. 2016 à 09:40

2 réponses

Meilleure réponse

La valeur de RTNLGRP_NEIGH sera 3. Vous pouvez facilement tester cela avec le programme suivant.

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

Il produit ceci:

RTNLGRP_NEIGH = 3

Puisque chaque macro est #define d à son propre nom, le RTNLGRP_NEIGH dans main sera remplacé par RTNLGRP_NEIGH. Mais comme l'expansion n'est pas récursive, elle s'arrêtera à ce stade et le programme utilisera la enum constante RTNLGRP_NEIGH qui est la quatrième et a donc la valeur 3.

Si vous n'êtes pas sûr de ce que fait le préprocesseur, vous pouvez toujours compiler avec le commutateur -E et regarder la sortie pré-traitée. Compiler l'exemple ci-dessus avec gcc -E donne (ne montre pas 840 lignes des en-têtes de bibliothèque standard #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);
}

Ce qui, espérons-le, est beaucoup moins déroutant.

Les #define mélangés dans la définition enum n'ont aucun effet sur la définition enum. Peu importe où se trouvent les #define. Ils auraient pu (et auraient probablement dû) être placés avant ou après la définition.

/* 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)

La raison pour laquelle ils ont écrit ce code bizarre est probablement qu'ils voulaient refactoriser l'ancien code en utilisant

#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
/* ... */

Pour utiliser un enum à la place. Mais comme le code existant pouvait reposer sur le fait que les identifiants sont des macros (comme testing #ifdef RTNLGRP_NEIGH), ils voulaient fournir des macros avec la même valeur. Notez que cette approche est imparfaite, cependant, car le préprocesseur ne connaîtra pas la valeur de la constante donc vous ne pouvez pas faire des choses comme #if RTNLGRP_NEIGH >= 3 que vous pourriez, si RTNLGRP_NEIGH avait été #define d à 3 littéralement. Donc, en substance, leur approche combine les inconvénients de l'utilisation de macros (pollution de l'espace de nom) avec ceux de l'utilisation de enum s (non disponibles au moment du prétraitement).

Un modèle peut-être plus utile que j'ai vu auparavant est de #define les constantes en nombres entiers réels.

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,
        /* ... */
};

Qui sera prétraité à ce qui suit.

enum rtnetlink_groups {
        RTNLGRP_NONE

        = 0,
        RTNLGRP_LINK

        = 1,
        RTNLGRP_NOTIFY

        = 2,
        RTNLGRP_NEIGH

        = 3,
        RTNLGRP_TC

        = 4,
        RTNLGRP_IPV4_IFADDR

        = 5,

};

Notez qu'ici, il est essentiel que les #define soient mélangés dans la définition enum, sinon nous aurions du code invalide tel que 3 = 3, au lieu du RTNLGRP_NEIGH = 3 souhaité.

Oh, et n'utilisez pas __RTNLGRP_MAX comme identifiant. Les noms contenant deux traits de soulignement adjacents ou commençant par un trait de soulignement suivi d'une lettre majuscule sont réservés par la norme C. Leur utilisation dans votre propre code conduit à un comportement indéfini.

5
5gon12eder 4 janv. 2016 à 07:15

La valeur de RTNLGRP_NEIGH sera 3 (c'est la quatrième constante d'énumération: RTNLGRP_NONE a la valeur 0, RTNLGRP_LINK a la valeur 1 et RTNLGRP_NOTIFY a la valeur 2) .

Le truc #define est quelque peu bizarre - c'est le genre de chose qui donne envie aux gens de vous empêche d'utiliser le préprocesseur C.

L'idée est qu'elle vous donne une macro pour RTNLGRP_NEIGH qui peut être testée, mais le développement de la macro est la constante d'énumération (orthographiée de la même manière). Il n'y a pas de boucle infinie dans les expansions, car une fois qu'une macro a été développée, elle n'est pas à nouveau développée pendant que le texte de remplacement est à nouveau analysé.

Donc, le résultat est que vous pouvez écrire:

#ifdef RTNLGRP_NEIGH
   …code using RTNLGRP_NEIGH…
#endif
4
Community 23 mai 2017 à 10:28