20

次のようなコードがアプリにありました。バグに関するフィードバックがありましたが、恐ろしいことにデバッガーを置いてみると、-5 と 0 の間の MAX が -5 であることがわかりました!

NSString *test = @"short";
int calFailed = MAX(test.length - 10, 0);                      // returns -5

MAX マクロを調べたところ、両方のパラメーターが同じ型である必要があることがわかりました。私の場合、「test.length」は unsigned int で、0 は signed int です。そのため、単純なキャスト (いずれかのパラメーター) で問題が解決します。

NSString *test = @"short";
int calExpected = MAX((int)test.length - 10, 0);                    // returns 0

これは、このマクロの厄介で予期しない副作用のようです。コンパイラが型の不一致について警告する MIN/MAX を実行するための iOS への別の組み込みメソッドはありますか? これはコンパイル時の問題であり、デバッガーが把握する必要があるものではないようです。私はいつでも自分で書くことができますが、他の誰かが同様の問題を抱えているかどうかを確認したかった.

4

2 に答える 2

30

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-1u1unsigned intintunsigned int

sの型をに変更すると、さらに混乱しlongます。次に、32 ビット プラットフォーム (iOS) でも同じ (間違った) 結果が得られますが、64 ビット Mac アプリでは問題なく動作します。(説明:longは 64 ビット型なので、すべての 32 ビットunsigned int値を表すことができます。)

要するに、特に符号付きの値が負の可能性がある場合は、符号なしと符号付きの整数を比較しないでください。

于 2013-04-29T04:59:34.210 に答える
12

十分なコンパイラ警告がオンになっていない可能性があります。オンにすると-Wsign-compare( でオンにできます-Wextra)、次のような警告が生成されます。

warning: signed and unsigned type in conditional expression [-Wsign-compare]

これにより、必要に応じて適切な場所にキャストを配置でき、MAX または MIN マクロを書き直す必要がなくなります。

于 2013-04-29T01:17:07.933 に答える