#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で質問しましょう!
似たような質問が見つかりました
- C言語・C++・C# c言語の問題です 3 2023/01/10 16:15
- C言語・C++・C# c言語でユーザ関数を利用して入力された文字列を反転させるプログラムを作りたいです。 3 2023/01/29 19:47
- C言語・C++・C# C言語で再起関数とポインタを用いて文字列反転をする方法がわかりません。 4 2023/04/29 20:32
- C言語・C++・C# c言語の問題の説明、各所ごとに 5 2023/07/26 11:03
- C言語・C++・C# C言語 プログラミング 4 2022/05/22 11:53
- Java java 入力 3 4 3 出力 ABC DEFG HIJ このようなプログラムの書き方を教えてくだ 2 2022/07/15 14:18
- C言語・C++・C# C言語 共用体について コマンドライン引数で値を2つ入力したときに、argv[2]の値をUNI u1 4 2022/04/25 20:34
- C言語・C++・C# 質問です 下記のコードを分かりやすく解説お願いします 初心者です #include ‹stdio.h 3 2022/05/26 22:03
- C言語・C++・C# 並列プログラミングのπ計算について 1 2022/07/16 22:30
- C言語・C++・C# 宣言する関数の形が決まっている状態で、 str1とstr2の文字列をこの順に引っ付けてstrに保存し 2 2022/05/30 18:21
関連するカテゴリからQ&Aを探す
おすすめ情報
デイリーランキングこのカテゴリの人気デイリーQ&Aランキング
-
DWORDの実際の型は何でしょうか
-
visualstudio C# テキストボッ...
-
long型の定数の末尾にLを付ける...
-
VC6でlong longでエラー?
-
構造体の要素すべてに対する四...
-
typedef enumの使い方を教えて...
-
コンパイルすると error C1083 ...
-
ハンドルされていない例外が発...
-
2重定義って??
-
C++ クラスをメンバにもつクラ...
-
スレッドのスケジューリングポ...
-
enumについて
-
C++のfor文について
-
#defineについて
-
void func( void )について
-
intとINTの違いは?
-
コンパイルエラー
-
C++でboolにintの値を代入する...
-
main.c:7:43: warning: implici...
-
エラー「invalid conversion fr...
マンスリーランキングこのカテゴリの人気マンスリーQ&Aランキング
-
DWORDの実際の型は何でしょうか
-
long型の定数の末尾にLを付ける...
-
2重定義って??
-
visualstudio C# テキストボッ...
-
C++のfor文について
-
構造体の要素すべてに対する四...
-
typedef enumの使い方を教えて...
-
変数の型を定義しなかった場合...
-
関数の実体定義にヘッダファイ...
-
ハンドルされていない例外が発...
-
intとINTの違いは?
-
C++でboolにintの値を代入する...
-
【#define】 defineで定義した...
-
構造体の宣言でエラーが出ます。
-
プログラムの中で別のmainを呼...
-
main.c:7:43: warning: implici...
-
void func( void )について
-
エラー「invalid conversion fr...
-
C言語 宣言した変数になにも代...
-
0除算を判定したい
おすすめ情報