char型のビット演算について

解決


匿名希望  2005-06-07 05:29:27  No: 57724

初歩的なことですが少し教えて下さい。
ある処理ごとにフラグを立てて処理したいのですが、
n個の処理に対してn個のフラグ(変数)を用意するともったいないので、
char型を1つ用意し、各ビット毎をフラグと見なし、扱いたいと思いました。

switch (***)
{
  case 条件1:
    flg |= 0x01;  (0000 0001)
    break;
  case 条件2;
    flg |= 0x02;  (0000 0010)
    break;
  case 条件3;
    flg |= 0x04;  (0000 0100)
        ・
        ・
        ・
}


if (flg & 0x01) 処理1
if (flg & 0x02) 処理2
if (flg & 0x04) 処理3

フラグを下げるときは
flg &= 0xfe;  (& 1111 1110)
flg &= 0xfd;  (& 1111 1101)

と、いったかんじです。

いざ処理を実行すると・・・
プログラムが条件1を通過しただけなのに
処理1&処理2&処理3・・・が実行されてしまいます。

その後にcharをunsignedに変更してみるとうまくいきました。

char型では↑の処理では不具合があるのでしょうか?
実際に処理中の変数の中身をみてみると、思っている通りに
flg |= 0x01; 結果:1
flg |= 0x02; 結果:2

にはなっているのですが・・・
考え方自体が間違っているのでしょうか?

この原因を教えて頂きたいです。


まきじ  2005-06-07 07:30:47  No: 57725

>if (flg & 0x01) 処理1
>if (flg & 0x02) 処理2
>if (flg & 0x04) 処理3

処理1などで、flg が変更されて、処理2が実行されているとか
ではないでしょうか?

>その後にcharをunsignedに変更してみるとうまくいきました。

特定のビットが、0 か 1 だけを判定するのでしたら、
unsigned でも signed でも、よいと思います。


匿名希望  2005-06-07 17:33:49  No: 57726

まきじさん回答ありがとうございます。

処理1&処理2&処理3ではflgの値は触っていないです。。。

たしかにsignedでもunsignedでもビット判定なので同じですよね。

その後、特に他の部分を変更加えたりはせず、
unsignedやsinged等を試した後、char型に変更し直すと
なぜかうまくいってました。。。

結局原因がよくわからなかったのですが、
自分の求めた結果通りになりましたので解決とさせて頂きます。

お騒がせし、申し訳ないです。

↑の考え方自体がおかしいと思われた方がいらっしゃったら
解決とさせて頂きましたがアドバイス頂けたらと思います。

ありがとうございました。


とみぞぅ  2005-06-07 20:36:05  No: 57727

>結局原因がよくわからなかったのですが、
個人的には趣味レベルのプログラムであれば気にしませんが、
仕事がらみであればある程度の解析は必要かと思います。

とりあえずsigned と unsigned でアセンブラの展開を
比較してみてはいかがでしょうか?
shortやlongの場合にどうなるか等。

貼り付けているCソースは部分的ですし、
大目に見てもコンパイルが通るものではありませんので
コメントがつかないのだとおもいます。
処理1->処理2->処理3が動いてしまうときに
処理2の直前のflg、処理3の直前のflgの値は
確認していないのでしょうか?

#ビット位置のマジックナンバーは避けたほうがよいかと思います。
であであ♪


匿名希望  2005-06-07 22:04:58  No: 57728

解決チャック忘れてました^^;
とみぞぅさん回答ありがとうございます。

残念ながら
>処理1->処理2->処理3が動いてしまうときに
>処理2の直前のflg、処理3の直前のflgの値は
>確認していないのでしょうか?
確認していなかったです。。。

確認しようにももはや同じ状況を作り出すことができずorz

申し訳ないです。

ソースについては必要部分を抜粋するだけでも
ちょっと貼るには多すぎて苦しいかと思います。

少し質問してもよろしいでしょうか?
>#ビット位置のマジックナンバーは避けたほうがよいかと思います。
ビット位置のマジックナンバーとはなんなのでしょうか?

ご教授頂けたらと思います。


気分屋  2005-06-08 08:03:09  No: 57729

マジックナンバーは調べれば一発で出てきます。
結論から言うと直接、値を書かないほうがいいということです。

悪い例)
void main( void )
{
    unsigned long flg = 0;

    flg |= 0x01;
}

良い例)
#define FLG_01  0x01

void main( void )
{
    unsigned long flg = 0;

    flg |= FLG_01;
}

後、匿名希望さんはデータ型をcharで持っていますが
私個人的には、ビットフラグはunsigned longを使います。
後々、何か追加フラグが出てきたときに問題が起きにくいためです。

後、フラグを下げるときですが

flg &= ~FLG_01;

としたほうがいいとおもいます。


まきじ  2005-06-08 08:36:31  No: 57730

>結論から言うと直接、値を書かないほうがいいということです。

「直接」とはどういう事でしょうか?
#define FLG_01 でも 0x01 でも同じだと思いますが?
コーディングの仕方として、悪いという意味でしょうか?


気分屋  2005-06-08 09:20:11  No: 57731

はい、コーティングの仕方として悪いという意味になります。

例えば、一万行のプログラムを書いたとして
その中で、フラグを20個使っているとします。

一ヵ月後に修正が入るとき、すでにそのコードは
他人のプログラムとほぼ同じであり、解析は困難です。
作った本人以外の人が、そのプログラムを修正するのであれば
なおさらです。

そのときにフラグで0x01という値が使われていては
それが何の意味を持っているか、まったくわかりません。
さらにフラグ自体が20個あるため0x01で検索をかけたときに
自分が探している0x01がすぐにみつかりません。

なので、マジックナンバーは使わず、記号定数(define)を
使うことをあたりまえにすることは、重要なことなのです。

さっき書き忘れたのでついでに書きますが
定数の持ち方として

#define FLG_00 (1 << 0)  // 0x01
#define FLG_01 (1 << 1)  // 0x02
#define FLG_02 (1 << 2)  // 0x04

のように記述すると、どのビットを
使用しているのかが、分かりやすくていいと思います。
ただ、速度面を考えると微弱おそくなります。


Ono  2005-06-08 10:00:36  No: 57732

> ただ、速度面を考えると微弱おそくなります。

いまどきのコンパイラはまず定数を吐くと思う。
速度的なデメリットは考えなくていい。

そうすると、可読性や検索性(grep)で有利。
0x02|0x01とか、0x03 とか書くより、READ|WRITE とかの方が意味が明白。

#define よりも、static const unsigned 〜 の方が C++ っぽいか。


匿名希望  2005-06-08 19:02:25  No: 57733

返事送れて申し訳ないです。

様々なご指摘ありがとうございます。

確かに気分屋さんのおっしゃる通り
#define としたほうが後々わかりやすいと思いますので
ソースを訂正しときます。

また、Onoさんのおっしゃるように
#define とするか  static const 〜とするか迷います。
たしかに static const 〜のがC++っぽいくていいような気もします。

う〜ん、どうするか。。。


シャノン  2005-06-09 07:11:54  No: 57734

> #define とするか  static const 〜とするか迷います。
> たしかに static const 〜のがC++っぽいくていいような気もします。

迷わず static const をオススメします。
何故なら、static const には名前があり、型があり、スコープがありと、いろいろメリットがあるからです。
#define は単純すぎて強力すぎます。
というより、プリプロセッサなんてものは使わなくて済むならば使わないほうがいいと思っています。

もっと説得力のある意見をお望みでしたら、是非、Effective C++ の第1章をご覧ください。


匿名希望  2005-06-09 07:54:01  No: 57735

シャノンさん回答ありがとうございます。

Effective C++ですか。。。
読んでないですが、お金と相談してみます(((( ;゜д゜)))

ご指摘の通りとりあえず static constで記述しています。
また何か指摘がありましたらお願いします。


Ban  2005-06-09 09:05:53  No: 57736

確かに迷わず static const 推奨。
VC6 等で制限があるときでもせめて enum。
マクロは必要悪の認識なので極力使わないです。

Effective C++ は C++ 初心者->中級者の必読書だと思います。
値段に見合う価値はあるんじゃないかと。


匿名希望  2005-06-09 17:29:14  No: 57737

Effective C++購入真剣に考えてみる匿名希望です。
Banさん回答ありがとうございます。
#define なら enum のがマシなんですね。。。

私は独習にてC++を勉強させてもらいましたが
参考までにEffective C++との違い等について教えて頂ければと思います。

また、static const に対するVC6等の制限とはどういったものなのでしょうか?

ご教授頂けたらと思います。


tetrapod  2005-06-09 18:33:32  No: 57738

>また、static const に対するVC6等の制限とはどういったものなのでしょうか?
試してみたらすぐわかると思います。
struct hoge_t {
 static const int x=123;
};
コンパイルエラーになってしまいます。

struct piyo_t {
  enum e_constants {
    x=123
  };
};
こっちは正しくコンパイルされます。

# ああはやく VC++6 を捨てたい...


Ban  2005-06-09 18:47:56  No: 57739

> #define なら enum のがマシなんですね。。。

少なくとも、(プリプロセッサではなく)コンパイラが認識できるので
文法に従って処理されますし、namespace や class の中に入れて衝突を
回避したりできますから。
define による「文字列の置換」は文法すら無視できますし時に強力な
ツールですが、普段から使う手段だとは思ってません。
# 「インクルードガード」以外の用法はほぼ必要ないのが C++ の利点と
# 思ってます。(C だと「定数」などに使わざるを得ませんが.....)

> 私は独習にてC++を勉強させてもらいましたが
> 参考までにEffective C++との違い等について教えて頂ければと思います。

もしかして一般名詞の「独習」ではなくて「独習C++」(書名)を
指してますか。そうであれば、私は「独習C++」を持ってませんので
対比できません。

ただ、Effective C++ は「既にC++を知っている人」が対象の本であり
基本的な言語の説明など言わずもがなのものは一切ありませんし、
(独習C++のような?)言語自体の入門書や解説書とは、対象とする読者や
目的が違うようには思います。

それ以上は書店の店頭などで、ご自身で確認されるのがよろしいかと。

> また、static const に対するVC6等の制限とはどういったもの
> なのでしょうか?

古いコンパイラなので、最新の C++ の規格に対応してません。

class foo
{
   static const int BAR1 = 1;  // これ(正しい記述)が通らない
   enum { BAR2 = 2 };    // 仕方がないので、こんな風に回避.....
};


匿名希望  2005-06-09 20:06:44  No: 57740

tetrapodさんBanさん速やかな回答ありがとうございます。

>試してみたらすぐわかると思います。
>古いコンパイラなので、最新の C++ の規格に対応してません。
なるほど。理解しました。

後、「独習」は「独習C++」になります。
説明不足で申し訳ないです。

Effective C++ はちょっと書店にて確認してこようかと思います。

また、Banさんの解説、勉強になりました。
ありがとうございます。

# ああはやく VC++6 を捨てたい...
#tetrapodさん。。。
#気持ちはよくわかります。
#KIAIでVS2005...(´Д`*)


※返信する前に利用規約をご確認ください。

※Google reCAPTCHA認証からCloudflare Turnstile認証へ変更しました。






  このエントリーをはてなブックマークに追加