2014-09-19 18 views
1

從草案C++標準(N3337):如果值truefalse被存儲到任何尺寸的bool類型的位字段應該使用int類型的位字段嗎?

9.6位字段

4(包括一一位比特字段),原始值和比特字段的值應相等。如果枚舉器的值存儲在相同枚舉類型的位字段中,並且位字段中的位數足夠大以容納該枚舉類型(7.2)的所有值,則原始枚舉器值和比特值的值應該相等。

對於其他類型的位字段,這個標準是非承諾的。爲了理解克++(4.7.3)如何與其它類型的位字段的交易,我使用了以下測試程序:

#include <iostream> 

enum TestEnum 
{ 
    V1 = 0, 
    V2 
}; 

struct Foo 
{ 
    bool   d1:1; 
    TestEnum  d2:1; 
    int   d3:1; 
    unsigned int d4:1; 
}; 

int main() 
{ 
    Foo foo; 
    foo.d1 = true; 
    foo.d2 = V2; 
    foo.d3 = 1; 
    foo.d4 = 1; 

    std::cout << std::boolalpha; 

    std::cout << "d1: " << foo.d1 << std::endl; 
    std::cout << "d2: " << foo.d2 << std::endl; 
    std::cout << "d3: " << foo.d3 << std::endl; 
    std::cout << "d4: " << foo.d4 << std::endl; 
    std::cout << std::endl; 

    std::cout << (foo.d1 == true) << std::endl; 
    std::cout << (foo.d2 == V2) << std::endl; 
    std::cout << (foo.d3 == 1) << std::endl; 
    std::cout << (foo.d4 == 1) << std::endl; 

    return 0; 
} 

輸出:

 
d1: true 
d2: 1 
d3: -1 
d4: 1 

true 
true 
false 
true 

我被的線驚訝輸出對應於Foo::d3。輸出結果與ideone.com相同。

由於該標準對於int類型的比特字段的比較是非承諾的,所以g ++似乎沒有違反標準。這讓我想起了我的問題。

是使用int類型的位字段的一個壞主意嗎?它應該泄氣嗎?

+3

那麼,我至少會對位字段使用'unsigned'類型,任何'signed'類型在語義上都沒有真正意義。 – 2014-09-19 18:58:16

回答

3

是的,int類型的位域是一個壞主意,因爲它們的簽名是實現定義的。改爲使用signed intunsigned int

對於非位域聲明,類型名int是完全等同於signed int(或int signed,或signed)。 short,longlong long的模式相同:未修飾的類型名稱爲,簽名的版本,您必須添加unsigned關鍵字來命名相應的無符號類型。

由於歷史原因,位域是一種特殊情況。用int類型定義的位域相當於或者到與signed int相同的聲明,或者與unsigned int相同的聲明。選擇是實現定義的(即,它取決於編譯器,而不是程序員)。位字段是intsigned int不是(必然)同義的唯一上下文。這同樣適用於charshort,longlong long

引述C++ 11標準,部分9.6 [class.bit]:

它實現定義是否一個普通的(既沒有明確 簽署也無符號)charshortintlong,或long long位字段是 帶符號。

(我不能完全肯定這樣做的理由的。的C很老的版本沒有的unsigned關鍵字,和無符號位字段通常比籤位字段更有用,這可能是一些早期的C編譯器在引入unsigned關鍵字之前實現了位字段,即使聲明爲int,默認情況下使位字段無符號可能只是一個方便的問題,沒有真正的理由保持規則,而不是避免破壞舊代碼。)

大多數位字段的目的是無符號的,這當然意味着它們應該這樣定義。

如果你想有一個簽署位字段(比方說,一個4位字段,它可以代表的數值從-8至+7,或者從-7到+7非二進制補碼系統),然後,你應該明確地將其定義爲signed int。如果你將其定義爲int,那麼一些編譯器會將其視爲unsigned int

如果你不關心你的位字段是否帶有符號,那麼你可以將它定義爲int - 但如果你定義一個位字段,那麼你幾乎可以肯定護理無論是簽署或無符號。

0

intsigned,並且在C++ Two's complement可以被使用,所以在第一int的字節標誌可以被存儲。當signed int有2位時,它可以等於1,0 see it working

+2

@Praetorian雖然他們要求籤名。所以1位有符號字段沒有多大意義。 – rici 2014-09-19 19:09:08

+0

@rici在我評論的時候,答案意味着總是使用補碼。我只是指那部分。 – Praetorian 2014-09-19 19:11:50

0

這完全合乎邏輯。 int是一個有符號整數類型,如果底層體系結構使用二進制補碼錶示有符號整數(如同所有現代體系結構一樣),那麼高位是符號位。因此,1位有符號整數位域可以取值0-1。例如,一個3位有符號整數位域可以取值在-43之間(包括兩端)。

只要你理解二進制補碼錶示法,沒有理由對全部有符號整數位域進行全面禁止。

+1

看rici和Keith Thompson的答案,問題不在於平臺是使用二進制補碼錶示還是補碼錶示。問題是'int'在位域中使用時可以是'signed int'或'unsigned int'。明確這種類型更有意義。 – 2014-09-19 20:37:02

+0

@RSahu:謝謝你!我並不知道'int'位域可以是有符號或無符號的。 – TonyK 2014-09-21 14:52:42

+0

我不知道,直到他們發佈他們的答案。 – 2014-09-21 15:31:29

2

您可以絕對使用unsigned任何大小的位字段,其大小不得大於unsigned int的大小。雖然signed位字段是合法的(至少如果寬度大於一),我個人更喜歡不使用它們。但是,如果您確實想要使用帶符號的位字段,則應明確將其標記爲signed,因爲它與實施有關,以確定不合格的int位字段是帶符號還是無符號。 (這類似於char,但沒有明確的不合格char*文字的複雜化的特徵。)

從這個角度來說,我同意int位字段應該氣餒。[注1]儘管我不知道任何實現中的位字段是隱式無符號的,但它肯定是被標準允許的,因此如果您不明確指示符號,那麼對於特定於實現的非預期行爲有很多機會。

該標準規定了一個有符號整數表示由可選的填充位,正好一個符號位和值位組成。雖然該標準並不保證至少有一個值位,並且如OP所示,gcc並不堅持存在 - 我認爲這是標準的可信解釋,因爲它明確允許不存在填充比特,並且不具有對應於值比特的任何這種措詞。

在任何情況下,只有三個允許可能簽署表示:

  • 2的補數,其中單位字段由1的應該被解釋爲-1

  • 1的補和符號量級。在這兩種情況下,允許由1組成的單比特字段是陷阱表示,因此唯一能夠在1比特有符號比特字段中表示的數字是0

由於移植的代碼不能假定1位籤位字段可以表示任何非零值,它似乎是合理的堅持,一個符號位,場至少有2位,無論您解釋標準實際需要或不需要。


注:

  1. 事實上,如果不是因爲一個事實,即字符串文字明確不合格,我寧願始終指定unsigned char。但是沒有辦法在這一點上回滾歷史。)
相關問題