2016-01-04 3 views
3

Я просматривал исходный код маршрутной сети Route.Запутывание определения MACRO и enum

Я хотел, чтобы выяснить, какова была стоимость 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

Благодаря

+0

оригинальный автор должен знать лучше, чем смешивать #define и перечислять как это, выглядит как беспорядок –

ответ

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 дает (не показано 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 s, смешанный с определением enum, не влияют на определение enum. Не имеет значения, где расположены #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 были #define d до 3 буквально. Таким образом, по сути, их подход сочетает в себе недостатки использования макросов (загрязнение именных пространств) с использованием 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, 

}; 

Обратите внимание, что здесь, это очень важно, что #define s смешивает в enum определения, в противном случае мы получим неверный код, такие как 3 = 3, вместо желаемого RTNLGRP_NEIGH = 3.

О, и, пожалуйста, не используйте __RTNLGRP_MAX как идентификатор. Имена, содержащие два смежных подчеркивания или начинающиеся с символа подчеркивания, за которым следует буква верхнего регистра, зарезервированы по стандарту C. Использование их в вашем собственном коде приводит к неопределенному поведению.

+0

Очень приятное объяснение. Спасибо за ваше время. – Haswell

+0

Ответчик ** не ** использует '__RTNLGRP_MAX' как сам идентификатор. Код в вопросе - из файла заголовка системы. – immibis

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 
Смежные вопросы