FDinoff's answer で示唆されているように、有効-Wsign-compare
にすることは良い考えですが、これは非常に一般的な落とし穴であるため、この背後にある理由をもう少し詳しく説明する価値があると思いました。
問題は特にマクロにあるわけではありませんがMAX
、a) オーバーフローを引き起こす方法で符号なし整数から減算することと、b) (警告が示唆するように) コンパイラが符号付きと符号なしの比較を処理する方法にあることです。一般的な値。
最初の問題は説明が非常に簡単です。符号なし整数から減算すると、結果が負になる場合、符号なし整数は負の値を表すことができないため、結果は非常に大きな正の値に「オーバーフロー」します。したがって、[@"short" length] - 10
に評価され4294967291
ます。
もっと驚くべきことは、引き算がなくても、 のようなものMAX([@"short" length], -10)
は正しい結果をもたらさないということです (それは と評価され-10
ます[@"short" length]
が5
、明らかに大きいです)。これはマクロとは関係ありません。たとえば、if ([@"short" length] > -10) { ... }
同じ問題が発生する可能性があります (if ブロック内のコードは実行されません)。
したがって、一般的な質問は次のとおりです。符号なしの整数を符号付きの整数と比較すると、正確にはどうなりますか (そして、最初に警告が表示されるのはなぜですか)? コンパイラは、驚くべき結果につながる可能性がある特定の規則に従って、両方の値を共通の型に変換します。
整数変換ルールを理解する [cert.org]からの引用:
- 符号付き整数型のオペランドの型が符号なし整数型のオペランドの型のすべての値を表すことができる場合、符号なし整数型のオペランドは符号付き整数型のオペランドの型に変換されます。
- それ以外の場合、両方のオペランドは、符号付き整数型のオペランドの型に対応する符号なし整数型に変換されます。
(私のものを強調)
次の例を検討してください。
int s = -1;
unsigned int u = 1;
NSLog(@"%i", s < u);
// -> 0
( ) は明らかに( ) より小さいですが、結果は0
(false) になります。これは、 に含めることができるすべての値を表すことができないため、両方の値が に変換されるために発生します。s
-1
u
1
unsigned int
int
unsigned int
s
の型をに変更すると、さらに混乱しlong
ます。次に、32 ビット プラットフォーム (iOS) でも同じ (間違った) 結果が得られますが、64 ビット Mac アプリでは問題なく動作します。(説明:long
は 64 ビット型なので、すべての 32 ビットunsigned int
値を表すことができます。)
要するに、特に符号付きの値が負の可能性がある場合は、符号なしと符号付きの整数を比較しないでください。