2015-11-07 66 views
4

我想更好地理解C99標準,但現在我很困惑在枚舉中使用枚舉作爲位域,如果它們被視爲int或作爲實現定義的類型。在C99的最終草案中查找時,我發現6.7.2.1段。 4枚舉爲位域實現定義類型嗎?

位字段應具有一種類型,是_Bool的合格或不合格的版本,符號int無符號整型,或其他一些實現定義類型。

和6.7.2.2段。 4

每個枚舉類型應與,有符號整數類型,或一個無符號整數類型兼容。類型的選擇是實現定義的,但應能夠表示枚舉的所有成員的值。 ...

所以我用這個簡單的源代碼試圖

enum e { 
    E0, E1 
}; 

struct s { 
    enum e bitfield : 4; 
}; 

我可以用gcc-5.0編譯這個沒有警告和使用-std=c99 -Wall -Wextra -pedantic鐺-3.5,但我得到的gcc-4.8以下警告

warning: type of bit-field 'bitfield' is a GCC extension 

在這裏開始混亂。枚舉作爲位域被視爲int還是實現定義的類型?這是GCC-4.8中的一個錯誤還是他們改變了對標準的解釋?和其他C99編譯器一起使用它是否安全?

+0

可能重複[使用位域與無符號字符時警告](http://stackoverflow.com/questions/10906238/warning-when-using-bitfield-with-unsigned-char) – 2015-11-07 21:07:23

+1

@Rhymoid我不確定如果這真的是重複的。 AFAIK是無符號字符unsigned int的子類型,而枚舉可以等同於int。它也可能是我誤解了6.7.2.2段中的「與整數類型兼容」。 4. – fsasm

+0

第一個引用意味着它是實現定義哪些其他類型允許用作位域 –

回答

4

枚舉爲位域實現定義的類型嗎?

是的。

你看到的是gcc實現定義的行爲的變化。

正如你所引用的標準的部分說,有些字段必須是_Bool類型,intunsigned int一些實現定義的類型。

A enum類型與某些整數類型兼容。通過實驗和gcc手冊看看,對於gcc,您的enum eunsigned int兼容。但該標準不允許位域爲兼容unsigned int。它實際上必須是unsigned int(兼容類型不一定是相同類型)。 外,它也可以是其他實現定義的類型。

按照manual for gcc 4.8.4

  • 允許的位字段比_Boolsigned int,和unsigned int其他類型的(C99 6.7.2.1)。

在嚴格符合模式下不允許其他類型。

根據手冊爲GCC-5.2.0:

  • 允許位字段類型以外_Boolsigned int,和unsigned int(C99和C11 6.7.2.1)。

其他整數類型,例如long int和枚舉類型即使在嚴格符合模式下也是允許的。

因此,您所看到的是gcc行爲的變化,即使在「嚴格符合模式」中也允許更多類型的位字段。這不是gcc對標準解釋的改變;兩種行爲都是允許的。使用enum作爲位域是不可移植的。符合的C編譯器可能會支持它們,也可能不支持它們。 (如果gcc保留了爲此發出警告的能力,我個人會首選它。)

0

它可能是安全的,但不要這樣做。你違反了暗示的合同設計。有點。你提到了構造,但沒有提到你真的想用它。可能有更乾淨的方法。

如果您有:

typedef enum { 
    E0, E1, E2 
} myenum_t; 

myenum_t val; 

現在,如果你有不同的開關語句,如:

switch (val) { 
case E0: 
    ... 
    break; 
case E1: 
    ... 
    break; 
case E2: 
    ... 
    break; 
} 

他們將在編譯時進行檢查,以確保您的switch覆蓋所有的案件。如果您隨後將E3添加到您的枚舉定義中,編譯器將會將switch語句標記爲缺少E3。這很有用。

如果你開始有點diddling你的枚舉值,您可能需要改變你的switch到:

switch (val) { 
case E0: 
    ... 
    break; 
case E1: 
    ... 
    break; 
case E2: 
    ... 
    break; 
default: 
    ... 
    break; 
} 

現在,如果你添加E3到您的枚舉,編譯器不會標誌您switch爲缺少case,因爲它假定default將處理它。也許不是你想要的。

添加default通常用於調試錯誤的枚舉值。

但是,如果您有:

typedef struct { 
    unsigned int mask:8; 
    unsigned int enval:8; 
    unsigned int ident:8; 
    unsigned int other:8; 
} mybit_t; 

mybit_t bval; 

使用下列內容:

switch ((myenum_t) bval.enval) { 
    ... 
} 

可能是有點清潔,也許更接近你真正希望達到的目標。

「隱含的合約」是[在此範圍內]枚舉旨在爲一組整數。需要注意的是,你還可以有:

typedef enum { 
    M0 = 1 << E0, M1 = 1 << E1, M2 = 1 << E2 
} mymask_t; 

而且,這是完全沒有做這樣的事情:bval.mask |= M2;。將位域切片和切塊留在int,您可以在其中控制大小[排序]。當使用標準的東西時,嘗試應用半不可移植的擴展並沒有什麼好處。