ビットフラグを扱う時にビットフィールドを使いますか?

[戻る]
夢幻 2004/04/29(木) 18:58:28
ビットフラグを扱う時に、ビットフィールドを使用したほうが、
ビット演算子を組み合わせなくても出来るので簡単でいいと思うのですが、
ビット演算子を使うメリット、デメリット。またビットフィールドのメリット、デメリットを
あったら教えて頂きたいと思います。

シャノン 2004/04/30(金) 21:21:12
使いますか?と聞かれたら、俺は使いません。
夢幻さんが使いたいのでしたらご自由にどうぞ。
ただ、Windows API がビットフラグを使ってるので、それとの一貫性を取るためにビッ
トフラグにすることはあるんじゃないでしょうか。
ビットフィールドがあまり使われないことの理由としては、そのくらいしか思いつきま
せんでした。

RAPT 2004/04/30(金) 22:58:59
σ(^^)はビットフィールドは使っていません。

昔はメモリ資産が厳しかったのでメモリ節約のためにビットフィールドを使う事が
あったかもしれませんが、今はメモリ資産は結構ゆとりがあるので、ビットフィールド
を使う必要はないと思います。

聞いた話ですが、ビットフィールドを使うと、演算するとき、ビットの位置を特定する
ためにわざわざ計算しているそうで、今となってはメモリサイズよりは速度優先かな、
と思っています。

それよりも、これが一番重要なことですが、ビットフィールドを使わない方が可読性が
上がると思います。


# 無論、例外もあって、ハードウェア系など、メモリ資産が厳しいところなどで、
# メモリ節約を優先するようなコーディングを要求される場合は別ですが。


まとめ。
▼ビットフラグ
・メリット:可読性。アクセスが速い(?)
・デメリット:メモリが多く必要。

▼ビットフィールド
・メリット:メモリが節約できる。
・デメリット:可読性が下がる事も。アクセスが遅い(?)

※補足
・可読性については好みかもしれません。
・また、ビットフィールドと同じような事をビット演算をせずともunionを使えば、
 ビットフラグ式で行なう事もできます。

シャノン 2004/05/02(日) 10:49:38
あー、いまさら思いついたんだけど
ビットフィールドだとマスク演算が面倒だぁね。

ゆう 2004/05/02(日) 15:05:56
横槍すみません。

ビットフィールドって
boolみたいに処理系依存の型に1か0しか入らないようにした物の事を
言うのではなかったでしょうか。
(残りのビットは無駄になるけど if(XX != TRUE)みたいな誤動作を防げる。)

ビットフラグとは例えば32bitの型を32個のフラグとして扱って、
AND演算で任意の桁が1か0かを取りです物という認識なんですが...

そう考えるとRAPTさんの考え方の正反対になるんですけど...

認識違っているのでしょうか。

シャノン 2004/05/02(日) 17:35:09
ビットフィールドってのは

struct
{
 unsigned long bit0 : 1;
 unsigned long bit1 : 1;
 unsigned long bit2 : 1;
 // 中略
 unsigned long bit31 : 1;
}
bits1;

bits2.bit28 = 0;
bits1.bit31 = 1;

こんな風に書くと、32 ビットを 1 ビットずつ扱えるというシロモノです。

struct
{
 unsigned long low8 : 8;
 unsigned long high24 : 24;
}
bits2;

のように、数ビットずつ扱うこともできますし

struct
{
 unsigned short lowbyte : 8;
 unsigned short highbyte : 8;

 unsigned char bit0 : 1;
 unsigned char bit1 : 1;
 // 残り 6 ビットは無駄
}
bits3;

32 ビットに限った話でもないし、無駄な領域があっても構いません。

jacta 2004/05/02(日) 18:44:51
ビットフィールドの型には、int, signed int, unsigned int, および_Boolしか使えま
せん。
といっても、ほとんどのコンパイラではcharやlongなども使えてしまいますが...

シャノン 2004/05/02(日) 18:55:38
あや、これは失敬。
やっぱり一度、規格書読まなきゃダメだなぁ…

ゆう 2004/05/02(日) 18:59:55
本当に横槍ですみませんが、参考になりました。
手持ちの本に説明がある物が無くて、たまたま何かのソースをみて思い違いをして
いました。(その中では1bitしか使っていなかったのかなあ。)
ありがとうございました。> シャノンさん
大変失礼しました。 > 夢幻さん

夢幻 2004/05/02(日) 21:59:34
返信遅くなってすみませんでした

ある変数を
struct
{
    UINT m_uCoordX : 8;    //X座標
    UINT m_uCoordY : 8;    //Y座標
    UINT m_uAngle  : 8;    //角度
    UINT m_uFlag1  : 1;    //フラグ1
    UINT m_uFlag2  : 1;    //フラグ2
    UINT m_uFlag3  : 1;    //フラグ3
} v;

のようにすれば、
    v.m_uCoordY = 50;
    v_m_uFlag1  = 1;
のように書け、必要以上の代入を防げますし、解りやすいですが
普通の変数では
    v = (v & 0xffff00ff) | (50 << 8);
    v = (v & 0xfeffffff) | (1  << 24);
のように書かなければならないと思います。

これが面倒だったのでそれぞれのメリット、デメリットはなんなのか、
皆さんはどうしているのか、という質問だったのですが
皆さんはあまり使っていないようですね。

しかし、ビットフィールドでマスク演算というのはなんなんでしょうか?

シャノン 2004/05/03(月) 00:13:30
うぅむ…なんかよく考えずに発言してしまったラシイ
そもそもビットフィールドを使えばマスクの必要ないんですねぇ…

YuO [E-Mail] 2004/05/03(月) 00:32:11
vを
struct {
    unsigned char m_uCoordX;
    unsigned char m_uCoordY;
    unsigned char m_uAngle;
    unsigned char m_uFlags;
} v;
と定義すれば,
>     v.m_uCoordY = 50;
は当然通りますし,
>     v_m_uFlag1  = 1;
は
v.m_uFlags |= 1;
で済みます。

フラグであればビット演算,加算等を行うのであればbit-fieldでない型を使うので,
bit-fieldは使いませんね。
フラグの初期設定が非常に面倒なことになりますし。
v.m_uFlags = 1 | 2;
が,
v.m_uFlag1 = 1;
v.m_uFlag2 = 1;
v.m_uFlag3 = 0;
のようになってしまうのは面倒なだけです。

はる 2004/05/03(月) 05:55:04
ビットフィールドは、Bitに名前をつけることでソースコードの可読性を
容易にし、コードの変更を容易にしますが、処理速度が低下するという認識
をもっています。
ただ、速度については、最近のコンパイラを評価していませんので
変わらないかもしれません。

1Chipマイコンでメモリが厳しいときや、通信データなど、データ量を
減らしたい場合にビット変数を使用していますが、
ビットフィールドとするかビット列のままにするかは
名前をつける必要があるかないかで決めています。
 

jacta 2004/05/03(月) 08:25:58
> ただ、速度については、最近のコンパイラを評価していませんので
> 変わらないかもしれません。

シフト・論理演算を使って同じことを実現する場合と比べれば、速度低下は特にないは
ずです。
効率が悪くなるのは、ビットフィールドの値を(まじめに)取り出す場合で、符号拡張
などの操作が入るために大きく遅いコードが展開されます。真偽の判定だけならそれほ
ど効率は低下しません。

> 1Chipマイコンでメモリが厳しいときや、通信データなど、データ量を
> 減らしたい場合にビット変数を使用していますが、

1チップマイコンの場合には、RAMは確かに節約できますがROMを著しく浪費します。ま
た、制御レジスタへのアクセスにビットフィールドを使うのも色々と問題があります。
通信データの場合は確かにデータ量が減るのですが、ビットフィールドの展開のされか
たは処理系によって大きく異なるので、厄介な問題が付いて回ります。

シャノン 2004/05/03(月) 11:33:33
規格なのか実装なのかわかりませんが、ビットフィールドを使うと1ビットずつ消費す
るんですか?
たとえば、

struct
{
 short onebit : 1;
}
s;

とした場合、消費されるのは1ビットだけなんでしょうか?
この場合、short だから2バイト、あるいは最適化されても1バイト消費されることに
なるのでは…?

jacta 2004/05/03(月) 12:36:33
> この場合、short だから2バイト、あるいは最適化されても1バイト消費されること> なるのでは…?

先にも書いたように、規格上はビットフィールドにshort型を使うことはできません。
(処理系定義の型を使うことは可能なので、short型のビットフィールドを処理系がサポ
ートしていればOKですが)
したがって、short型を使った場合の振る舞いは完全に処理系に依存します。

仮にsigned int型などを使った場合でも、ビットフィールドを格納するために十分な大
きさであれば、実際に構造体のサイズがどうなるかは処理系定義です。さらに、ビット
フィールドの連続性や、ビットフィールド割り付けの順序(MSBからかLSBからか)など
も処理系定義です。

それから、最適化の有無で構造体のサイズが変わると、翻訳単位間のバイナリ互換性が
なくなるので、普通はそのような実装は行われません。

シャノン 2004/05/03(月) 15:38:48
> 先にも書いたように
あーあー何見てるんだ俺は。ゴメンナサイ。

処理系定義、っすか。
つまり処理系によっては、ビットフィールドを使ったからといって必ずしもメモリの節
約につながるとは限らない、と。
了解でした。

はる 2004/05/04(火) 19:26:03
シャノンさんへ
>処理系定義、っすか。
>つまり処理系によっては、ビットフィールドを使ったからといって必ずしも
>メモリの節約につながるとは限らない。

根本的に勉強しなおしてから書き込んではいかがでしょうか?
たぶんまだ誤解してます

はる 2004/05/04(火) 19:48:25
jactaさんの言葉はアセンブラを頭で展開できない人には理解し難い。

>> ただ、速度については、最近のコンパイラを評価していませんので
>> 変わらないかもしれません。
>シフト・論理演算を使って同じことを実現する場合と比べれば、速度低下は特にないは
>ずです。
>効率が悪くなるのは、ビットフィールドの値を(まじめに)取り出す場合で、符号拡張
>などの操作が入るために大きく遅いコードが展開されます。真偽の判定だけならそれほ
>ど効率は低下しません。

これはつまりビットフィールドの処理速度は確実に低下するということです。


>> 1Chipマイコンでメモリが厳しいときや、通信データなど、データ量を
>> 減らしたい場合にビット変数を使用していますが、
>1チップマイコンの場合には、RAMは確かに節約できますがROMを著しく浪費します。

ROMが増えるのはアセンブラで展開される命令コードが増えるという意味です。
つまり、処理速度は確実に低下します。

>また、制御レジスタへのアクセスにビットフィールドを使うのも色々と問題が
>あります。

Cプログラマは、レジスタのアクセスにビットフィールドを使用しちゃうのか^^;;
レジスタ系はアセンブラ命令でアクセスすることを推奨します。

>通信データの場合は確かにデータ量が減るのですが、ビットフィールドの展開の
>されかたは処理系によって大きく異なるので、厄介な問題が付いて回ります。

処理系によってintのサイズも変わりますので、インタフェースの構造体を別の処理系で
そのまま使用できると思ってもらっては困ります。

jacta 2004/05/04(火) 21:55:47
> jactaさんの言葉はアセンブラを頭で展開できない人には理解し難い。

そうかもしれませんね。
ただ、ビットフィールドのオーバーヘッドが気になるような状況では、いずれにしても
アセンブラを理解できないと話にならないと思います。

> これはつまりビットフィールドの処理速度は確実に低下するということです。

確実に低下するとまではいえません。
CPUのアーキテクチャにもよりますが、速度低下を防ぐには、ビットフィールドは必ず1
ビットにすべきで、そのまま真偽値(すなわちif等の条件式として使う)として使えば
よいのです。ビットフィールドの値を使って加算等の演算をしたり、他の変数に代入し
たりすると、とたんに速度低下が起こります。

> Cプログラマは、レジスタのアクセスにビットフィールドを使用しちゃうのか^^;;

ルネサスなどでは、制御レジスタにアクセスするためのビットフィールドを宣言したヘ
ッダを配布したりしています。純正以外のコンパイラを使うとたちまち破綻するのです
が...

> レジスタ系はアセンブラ命令でアクセスすることを推奨します。

専用のI/O命令が必要な場合か、μsec以下のオーダーで制御するような場合を除けば、必
ずしもアセンブラを使う必要はないと思います。

> 処理系によってintのサイズも変わりますので、インタフェースの構造体を別の処理系
> でそのまま使用できると思ってもらっては困ります。

データ型のサイズはまだ管理しやすいのですが、もっとやばいのはバイトオーダーや境
界調整です。(最悪の場合は1バイトのビット数まで違う)
ビットフィールドの場合はフィールド内のビットオーダーや構造体の最小サイズも違っ
てしまうので、外部とバイナリでやりとりするのはまず無理です。

PATIO 2004/05/06(木) 11:57:12
その昔、ビットフィールドってのがあるから使いたいと言う話を先輩にしたら、
遅くなるからやめろって言われてその時は使うのをやめた記憶が有ります。
まあ、余計なコードが出てきそうだからなるほど遅くなるだろうなぁ
と納得していたんですが、一ビットのビットフィールドはそうでもないんですね。

移植性やらなにやら考えたらビットフィールドを使うのはやめた方がよさそうですね。
クロスプラットフォームを意識するならなおさらの事。

最近は、動作環境がリッチになってビット単位でフラグ操作をするような状況は
リソースが厳しいかぎられた環境だけになったみたいですね。
動作速度の低下についても一般に出回っているWindows用のPCで動かす限りは
目くじら立てるほどではなくなってきているだろうし。
そうなると可読性を上げるためにビットフィールドという選択肢もありかなと
思います。

メリットとデメリットに関する知識は確かに必要だと思いますけれど。

謎の怪鳥 2004/05/10(月) 12:22:20
何か面白そうな話題が・・・

以前RTPを実装する作業に携わったことがありましたが、
そのときに、RTPヘッダやRTCPヘッダを定義するRFCのサンプルコードで
ビットフィールドが使われているのを見かけました。

RTPではリアルタイム性を考慮して
データ項目のサイズを必要最小に制限して、
無理やり4バイト単位のパケットデータに詰め込んでいます。
そのため、ヘッダの中では3ビット幅だの5ビット幅だのという
項目が多数定義されており、そのような項目を操作する場合には
マスクをかけて必要なビットを残す、といった操作を行うよりは、
ビットフィールドを使用したほうがはるかにわかりやすく記述になります。

もちろんそのサンプルでは、CPUのEndianの違いも考慮されていて、
ちゃんとLittle-Endian用の定義とBig-Endian用の定義が
用意されていました。

サスライの旅人 2004/05/12(水) 00:34:48
わたしはよくビットフィールドを使いますよ。って圧縮のソフトを作る時だけですけど。圧縮ソ
フトを作る時、ビット単位で少しでもファイルが小さくなるように…と、考えながら作る時、ソ
ースが見やすくなります。でも、遅くなるとは…知りませんでした。それ以外で使うことは全く
ないですね。

ボコノン教徒 2004/05/12(水) 15:08:40
> その昔、ビットフィールドってのがあるから使いたいと言う話を先輩にしたら、
> 遅くなるからやめろって言われてその時は使うのをやめた記憶が有ります。

シフトやビット演算を行う時間や、コードが複雑になることを考えると、
ビットフィールド使っちゃいますね。
処理系依存になっちゃうのは、この際目をつぶっちゃいます。

#それにしても、MPEG-TSのヘッダー何とかして欲しいよー。

古いスレッドには追加発言できません。