密接に関連する質問については、Cでの署名の拡張int
を参照してください (重複ではありません)。
ビットフィールドに関するほとんどすべてが「実装定義」であることに注意する必要があります。特に、「プレーンint
」ビットフィールドに負の数を割り当てることができるかどうかは明確ではありません。int
実装で「plain is signed
」または「plain int
is 」のどちらを使用しているかを知る必要がありますunsigned
。どちらが 9番目のビットかということもややこしくなります。0 または 1 から数えますか。また、ビット フィールドのセットのどちらがビット 0 で、どちらがビット 31 であるか (最下位ビット (LSB) をビット 0、最上位ビット (MSB) をビット 31 として数えます) 32 ビット量)。実際、構造体のサイズは 32 ビットである必要はありません。コンパイラには、レイアウトに関する異なる規則がある場合があります。
これらすべての警告を無視して、 から形成された 9 ビット値(blah.a << 4) | blah.b
があり、9 ビットの 2 の補数が (32 ビット) に昇格されたかのように符号拡張する必要がありますint
。
相互参照された回答の機能は、次のように機能します。
#include <assert.h>
#include <limits.h>
extern int getFieldSignExtended(int value, int hi, int lo);
enum { INT_BITS = CHAR_BIT * sizeof(int) };
int getFieldSignExtended(int value, int hi, int lo)
{
assert(lo >= 0);
assert(hi > lo);
assert(hi < INT_BITS - 1);
int bits = (value >> lo) & ((1 << (hi - lo + 1)) - 1);
if (bits & (1 << (hi - lo)))
return(bits | (~0 << (hi - lo)));
else
return(bits);
}
次のように呼び出します。
int result = getFieldSignExtended((blah.a << 4) | blah.b), 8, 0);
数値を固定したい場合は、次のように記述できます。
int x = (blah.a << 4) | blah.b;
int result = (x & (1 << 8)) ? (x | (~0 << 8)) : x;
注: 9番目のビットは、ビット 0..8 を含む値のビット 8 であると想定しています。他の解釈を念頭に置いている場合は調整してください。
作業コード
g++ (GCC) 4.1.2 20080704 (Red Hat 4.1.2-44)
RHEL 5 x86/64 マシンからコンパイルされました。
#include <iostream>
using namespace std;
typedef struct
{
int a: 5;
int b: 4;
int c: 1;
int d: 22;
} example;
int main()
{
example blah;
blah.a = -5; // 11011
blah.b = -3; // 1101
int result = blah.a << 4 | blah.b;
cout << "Result = " << result << endl;
int x = (blah.a << 4) | blah.b;
cout << "x = " << x << endl;
int result2 = (x & (1 << 8)) ? (x | (~0 << 8)) : x;
cout << "Result2 = " << result2 << endl;
return 0;
}
出力例:
Result = 445
x = 445
Result2 = -67
ISO/IEC 14882:2011 — C++ 標準
§7.1.6.2 単純型指定子
¶3 ... [ 注:char
タイプのオブジェクトと特定のビットフィールド (9.6) が符号付きまたは符号なしの数量として表されるかどうかは実装定義です。指定子は、signed
char オブジェクトとビットフィールドに署名を強制します。他のコンテキストでは冗長です。—終わりのメモ]
§9.6 ビットフィールド [class.bit]
¶1 フォームのメンバー宣言子
identifier<sub>opt</sub> attribute-specifier-seq<sub>opt</sub>: constant-expression
ビットフィールドを指定します。その長さは、ビットフィールド名からコロンで区切られています。オプションの attribute-specifier-seq は、宣言されているエンティティに関係します。ビット フィールド属性は、クラス メンバーの型の一部ではありません。定数式は、ゼロ以上の値を持つ整数定数式でなければなりません。整数定数式の値は、ビットフィールドの型のオブジェクト表現 (3.9) のビット数よりも大きい場合があります。そのような場合、余分なビットはパディング ビットとして使用され、ビット フィールドの値表現 (3.9) には関与しません。クラス オブジェクト内のビット フィールドの割り当ては実装定義です。ビットフィールドのアライメントは実装定義です。ビットフィールドは、アドレス指定可能なアロケーション ユニットにパックされます。[ ノート:ビット フィールドは、一部のマシンではアロケーション ユニットにまたがり、他のマシンではまたがりません。ビットフィールドは、一部のマシンでは右から左に割り当てられ、他のマシンでは左から右に割り当てられます。—終わりのメモ]
¶2 識別子を省略したビットフィールドの宣言は、名前のないビットフィールドを宣言します。名前のないビット フィールドはメンバーではないため、初期化できません。[ 注: 名前のないビットフィールドは、パディングを外部から課されたレイアウトに適合させるのに役立ちます。--end note ] 特殊なケースとして、幅が 0 の名前のないビット フィールドは、アロケーション ユニット境界での次のビット フィールドの配置を指定します。名前のないビット フィールドを宣言する場合にのみ、定数式の値をゼロにすることができます。
¶3 ビットフィールドはスタティックメンバーであってはならない。ビットフィールドは、整数型または列挙型 (3.9.1) を持つ必要があります。プレーン (明示的に符号付きでも符号なしでもない) char、short、int、long、または long long ビットフィールドが符号付きか符号なしかは実装定義です。bool 値は、ゼロ以外の任意のサイズのビット フィールドに正常に格納できます。アドレス演算子 & はビットフィールドには適用されないため、ビットフィールドへのポインタはありません。非 const 参照は、ビット フィールド (8.5.3) にバインドされません。[ 注: 型 const T& の参照の初期化子がビットフィールドを参照する左辺値である場合、参照はビットフィールドの値を保持するために一時的に初期化されたものにバインドされます。参照はビットフィールドに直接バインドされていません。8.5.3 を参照してください。—終わりのメモ]
¶4 true または false の値が、任意のサイズの bool 型のビット フィールド (1 ビットのビット フィールドを含む) に格納される場合、元の bool 値とビット フィールドの値は等しくなります。列挙子の値が同じ列挙型のビットフィールドに格納され、ビットフィールドのビット数がその列挙型のすべての値を保持するのに十分な大きさである場合 (7.2)、元の列挙子の値とビットフィールドの値は等しいものとします。[ 例:
enum BOOL { FALSE=0, TRUE=1 };
struct A {
BOOL b:1;
};
A a;
void f() {
a.b = TRUE;
if (a.b == TRUE) // yields true
{ /* ... */ }
}
—終わりの例]
ISO/IEC 9899:2011 — C2011 規格
C 標準は基本的に同じ効果がありますが、情報の表示方法が多少異なります。
6.7.2.1 構造体および共用体指定子
¶4 ビットフィールドの幅を指定する式は、コロンと式を省略した場合に指定される型のオブジェクトの幅を超えない非負の値を持つ整数定数式でなければなりません。122)値がゼロの場合、宣言には宣言子がありません。
¶5 ビットフィールドは、 、 、 、またはその他の実装定義型の修飾または非修飾バージョンである型を持つものと_Bool
しsigned
int
ますunsigned int
。アトミック型が許可されるかどうかは実装定義です。
¶9 ... さらに、メンバーは指定された数のビット (もしあれば符号ビットを含む) から構成されると宣言することができます。このようなメンバーはビットフィールドと呼ばれます。124)幅の前にコロンが付きます。
¶10 ビットフィールドは、指定されたビット数で構成される符号付きまたは符号なしの整数型を持つものとして解釈されます。125)値 0 または 1 が _Bool 型の非ゼロ幅ビットフィールドに格納される場合、ビットフィールドの値は格納された値と等しくなります。_Bool ビット フィールドには、_Bool のセマンティクスがあります。
¶11 実装は、ビットフィールドを保持するのに十分な大きさのアドレス指定可能なストレージユニットを割り当てることができます。十分なスペースが残っている場合、構造内の別のビットフィールドの直後に続くビットフィールドは、同じユニットの隣接するビットにパックされます。十分なスペースが残っていない場合、収まらないビットフィールドを次のユニットに配置するか、隣接するユニットとオーバーラップするかは実装定義です。ユニット内のビットフィールドの割り当て順序 (上位から下位、または下位から上位) は実装定義です。アドレス可能なストレージ ユニットのアラインメントは指定されていません。
¶12 宣言子がなく、コロンと幅のみのビットフィールド宣言は、名前のないビットフィールドを示します。126)特殊なケースとして、幅が 0 のビットフィールド構造体メンバーは、前のビットフィールドが配置されていた場合、それ以上のビットフィールドがユニットにパックされないことを示します。
122)オブジェクトのビット数は_Bool
少なくともCHAR_BIT
ですが、_Bool の幅 (符号と値のビット数) はちょうど 1 ビットかもしれません。
124)単項 & (アドレスの) 演算子は、ビットフィールド オブジェクトには適用できません。したがって、ビット フィールド オブジェクトへのポインタや配列はありません。
125)上記の 6.7.2 で指定されているように、使用される実際の型指定子が int または int として定義された typedef-name である場合、ビットフィールドが符号付きか符号なしかは実装定義です。
126)名前のないビットフィールド構造体メンバーは、外部から課されたレイアウトに適合するためのパディングに役立ちます。
標準の付属書 J は移植性の問題を定義し、§J.3 は実装定義の動作を定義します。部分的に、それは言います:
J.3.9 構造体、共用体、列挙、およびビットフィールド
¶1 — ''plain'' int ビットフィールドを signed int ビットフィールドとして扱うか、unsigned int ビットフィールドとして扱うか (6.7.2, 6.7.2.1)。
— _Bool、signed int、および unsigned int (6.7.2.1) 以外の許容されるビットフィールド型。
— ビットフィールドにアトミック型を許可するかどうか (6.7.2.1)。
— ビットフィールドがストレージユニットの境界をまたぐことができるかどうか (6.7.2.1)。
— ユニット内のビットフィールドの割り当て順序 (6.7.2.1)。