2016-01-04 71 views
3

我正在瀏覽一些Route net的源代碼。混淆MACRO和枚舉定義

我想弄清楚什麼是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_NEIGH6 OR 3

由於

+0

原作者應該比混合#define和enum更像混亂,看起來像一團糟 –

回答

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 d到自己的名稱,在該mainRTNLGRP_NEIGH將由RTNLGRP_NEIGH代替。但由於擴張是不是遞歸的,它會停在這一點上,程序使用enum不斷RTNLGRP_NEIGH這是第四個,因此具有值3

如果你不能確定預處理器做什麼,你總是可以編譯與-E切換並查看預處理輸出。編譯具有gcc -E上面的例子中給出了(未示出840行#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); 
} 

這是希望混亂少得多。

#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的東西。

這個想法是它爲您提供了一個可以測試的宏,但是宏的擴展是枚舉常量(拼寫相同)。在擴展中沒有無限循環,因爲一旦宏被擴展,在重新掃描替換文本時不會再次擴展。

所以,結果是,你可以寫:

#ifdef RTNLGRP_NEIGH 
    …code using RTNLGRP_NEIGH… 
#endif