• ベストアンサー

#defineについて

#define STATIC_ASSERT(expr) { \ char __STATIC_ASSERTION[(expr) ? 1 : -1]; \ (void)__STATIC_ASSERTION; \ } /* 符号付き整数の右シフトが算術シフトかどうか */ #define SHIFT_LEFT_SINGNED_USES_SAL \ (((signed int)0xffffffff >> 1) == 0xffffffff) /* 符号無し整数の右シフトが論理シフトかどうか */ #define SHIFT_LEFT_UNSIGNED_USES_SHL \ (((unsigned int)0xffffffff >> 1) == 0x7fffffff) とは一体どういう意味なのでしょうか? なぜ、ブロックの中にchar型が宣言されているのでしょうか? なぜ、(void)とキャストされているのでしょうか? できたらわかりやすくご教授よろしくお願いします。

質問者が選んだベストアンサー

  • ベストアンサー
回答No.1

> とは一体どういう意味なのでしょうか? ・コンパイル時にエラーという形でAssertionを起こすためのマクロ ・実装定義である負の値を右シフトした場合の挙動を調べるマクロ x 2 です。 > なぜ、ブロックの中にchar型が宣言されているのでしょうか? char型の宣言ではなく,char型を要素とする配列型の宣言であることが重要です。 exprが真の場合, STATIC_ASSERTは { char _STATIC_ASSERTION[1]; (void)_STATIC_ASSERTION; } となり,コンパイルを通ります。 しかし,exprが負の場合, STATIC_ASSERTは { char _STATIC_ASSERTION[-1]; // 要素数が負であることは許されないのでエラー (void)_STATIC_ASSERTION; } となり,コンパイルを通りません。 通常,expr部分には「コンパイル時に真であるはずの式」を記述しておきます。 たとえば,intが4バイト以上あることを前提としたプログラムであるならば, STATIC_ASSERT(sizeof(int) >= 4) と書いておきます。 > なぜ、ブロックの中にchar型が宣言されているのでしょうか? スコープを作るためです。 一つのスコープに同じ名前を二度定義することはできません。 このため,ブロックを作らなかった場合,一つのブロックにで,STATIC_ASSERTは一回しか使えなくなってしまいます。 > なぜ、(void)とキャストされているのでしょうか? この文が,_STATIC_ASSERTIONを使わないことを明示するためです。 これをしなかった場合,警告をあげるコンパイラがあるためです。

79562
質問者

お礼

回答ありがとうございます。わかりやすい説明ありがとうございました。ついでにもう1つ質問したいのですが、(void)を抜いてやっても警告が出なかったのですが、これはやはり処理系依存ということでしょうか。

その他の回答 (4)

  • Tacosan
  • ベストアンサー率23% (3656/15482)
回答No.5

世の中には, 「定義したけど使っていない変数」や「副作用のない式文」に対して警告を出す処理系があります. そのような処理系に対処するために (void) をつけている, はず. 蛇足: 「完全な可搬性」という点では #define SHIFT_RIGHT_SIGNED_USES_SHL ((~0 >> 1) > 0) でもびみょ~に危険な気がしました>#4. 「1の補数」が意外と強敵で, 規格上は ・「すべてのビットが 1 のときにトラップ表現とする」 (この場合 ~0 の振る舞いは未定義) ・「負の 0 は許すんだけど負の 0 が出てくるような演算では (普通の) 0 になる」 (この場合もちろん ~0 は 0 になる) という処理系も考えられます. ということで, #define SHIFT_RIGHT_SIGNED_USES_SHL ((-2 >> 1) > 0) とする方がより安全なのかもしれない. もちろんこれですらダメな処理系も「全くあり得ない」とは言い切れないだろうけど, そこまで驚異的に変態な処理系は使いたくない.

79562
質問者

お礼

回答ありがとうございます。この文を読んでまだまだ勉強不足だということがわかりました。ありがとうございました。

  • jacta
  • ベストアンサー率26% (845/3158)
回答No.4

> #define STATIC_ASSERT(expr) { \ > char __STATIC_ASSERTION[(expr) ? 1 : -1]; \ > (void)__STATIC_ASSERTION; \ > } やっていることは既に回答が出ているとおりですが、いくつか問題点があります。 1. __STATIC_ASSERTIONが予約済み識別子であり、これを使うと未定義の動作になる。 2. 関数の外で使えない。 3. 余計なオブジェクトを作ってしまう。 普通は、 #define STATIC_ASSERT(expr) extern char static_assert_[(expr) ? +1 : -1] のようにするか、 #define STATIC_ASSERT(expr) extern void static_assert_func_(char [(expr) ? +1 : -1]) のようにします。 > #define SHIFT_LEFT_SINGNED_USES_SAL \ > (((signed int)0xffffffff >> 1) == 0xffffffff) これも既に指摘されているように、マクロ名が間違っていますし、int型が32ビット以下の場合しか使えません。 また、符号付き整数型へのキャストで元の値を表現できない場合(今回もそうです)には、処理系定義のシグナルが発生するか、処理系定義の値になるため、移植性がありません。 なお、算術シフトの判定を行うには、負の値の内部表現が符号ビットと絶対値の場合も考慮しなければなりません。 そのため、 #define SHIFT_RIGHT_SIGNED_USES_SHL ((~0 >> 1) > 0) でよいかと思います。

  • php504
  • ベストアンサー率42% (926/2160)
回答No.3

こうしたかったのかな /* 符号付き整数の右シフトが論理シフトかどうか */ #define SHIFT_RIGHT_SIGNED_USES_SHL \ ((~0 >> 1) != ~0)

  • Tacosan
  • ベストアンサー率23% (3656/15482)
回答No.2

そことは別に, 下のシフトのやつも正確ではないですね. int が 32ビット (以下) じゃないとうまく動かないし, そもそもマクロの名前が間違ってる. #define SHIFT_RIGHT_SIGNED_USES_SAR \ ((~0 >> 1) == ~0) の方が, 多分より正しい.... あれ? 最後のマクロって無意味じゃない? これ, int が 32ビット以上であれば必ず 1 のような気がするし, それ以前に「符号なし整数の右シフト」は必ず論理シフトのはず....

関連するQ&A