
#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を使わないことを明示するためです。
これをしなかった場合,警告をあげるコンパイラがあるためです。
回答ありがとうございます。わかりやすい説明ありがとうございました。ついでにもう1つ質問したいのですが、(void)を抜いてやっても警告が出なかったのですが、これはやはり処理系依存ということでしょうか。
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)
とする方がより安全なのかもしれない.
もちろんこれですらダメな処理系も「全くあり得ない」とは言い切れないだろうけど, そこまで驚異的に変態な処理系は使いたくない.
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)
でよいかと思います。
No.3
- 回答日時:
こうしたかったのかな
/* 符号付き整数の右シフトが論理シフトかどうか */
#define SHIFT_RIGHT_SIGNED_USES_SHL \
((~0 >> 1) != ~0)
No.2
- 回答日時:
そことは別に, 下のシフトのやつも正確ではないですね.
int が 32ビット (以下) じゃないとうまく動かないし, そもそもマクロの名前が間違ってる.
#define SHIFT_RIGHT_SIGNED_USES_SAR \
((~0 >> 1) == ~0)
の方が, 多分より正しい....
あれ? 最後のマクロって無意味じゃない? これ, int が 32ビット以上であれば必ず 1 のような気がするし, それ以前に「符号なし整数の右シフト」は必ず論理シフトのはず....
お探しのQ&Aが見つからない時は、教えて!gooで質問しましょう!
関連するカテゴリからQ&Aを探す
おすすめ情報
デイリーランキングこのカテゴリの人気デイリーQ&Aランキング
-
構造体を引数とする、クラス間...
-
main.c:7:43: warning: implici...
-
【#define】 defineで定義した...
-
C++/CLIでネイティブの構造体を...
-
2重定義って??
-
構造体の要素すべてに対する四...
-
C++でboolにintの値を代入する...
-
C++のコンストラクタを宣言する...
-
C#でオセロを作っているのです...
-
64bit → 32bit型へのキャスト
-
DWORDの実際の型は何でしょうか
-
C言語のコンパイルエラー
-
相互参照するクラス、俺こんな...
-
24ビットの変数
-
long型の定数の末尾にLを付ける...
-
最早開始時間と最遅完了時刻を...
-
Aの値からBの値を除するとは??
-
信頼区間の1.96や1.65ってどこ...
-
#define _CRT_SECURE_NO_WARNIN...
-
C言語 エラーの原因がわからな...
マンスリーランキングこのカテゴリの人気マンスリーQ&Aランキング
-
main.c:7:43: warning: implici...
-
long型の定数の末尾にLを付ける...
-
DWORDの実際の型は何でしょうか
-
visualstudio C# テキストボッ...
-
2重定義って??
-
C++のfor文について
-
変数の型を定義しなかった場合...
-
ハンドルされていない例外が発...
-
intとINTの違いは?
-
C++でboolにintの値を代入する...
-
構造体の要素すべてに対する四...
-
プログラムの中で別のmainを呼...
-
エラー「invalid conversion fr...
-
GCCで暗黙の型変換の警告を出し...
-
【#define】 defineで定義した...
-
sshdログの意味
-
DDVによるメッセージの変更
-
typedef enumの使い方を教えて...
-
構造体を生成時にわざわざ初期...
-
DLLでLIBファイルが作成されない
おすすめ情報