一部のコードで、符号付きの値が関数に渡されているのを見ました (符号なしの値を取ります)。返された値は再び署名された値に変換されます (元の署名された値を取得する目的で)..現在は機能していますが..この場合、考えられるすべてのシナリオで、元の署名された値が保持されるのでしょうか?
キャストは良くないので、できる限り避けるべきだと言われました。コンピュータ サイエンスの専門家から、このコードがいつ破られる可能性があるか教えてもらえますか?
私が知っているすべての実装 (さまざまなプラットフォームで 20 年以上 C++ を使用した後) は、符号付き <-> 符号なし整数を何もせずに変換します。つまり、バイナリ値は変更されずに渡されます。
このような動作の根源は、C での整数オーバーフローの処理 (つまり、処理しない) にあります。このようなレガシーを持つことで、この符号付き/符号なし変換を行うことは合理的です。これは今後も変わることはないと思います。
要約すると、符号付き -> 符号なし -> 符号付きおよび符号なし -> 符号付き -> 符号なしの変換は 100 % 安全です。偶数の変換がある場合、数値の値が最上位ビットを使用していない非負の整数であることに注意する必要があります。この場合、型が符号付きか符号なしかは重要ではありません。それ以外の場合は、独自のコードで範囲外の値を明示的に処理する必要があります。
標準の整数昇格セクションは、符号付きと符号なしの変換を非常によくカバーしています。あなたの質問には C と C++ のタグが付けられています。両方の標準を削除することもできますが、ここでは C99 標準の関連部分を示します。
符号付き整数が符号なし整数の範囲内にない (つまり、ゼロ (0) 未満である) 場合に、同様のサイズの符号付き整数を符号なし整数に昇格することについて:
C99 6.3.1.3-p2
それ以外の場合、新しい型が符号なしの場合、値が新しい型の範囲内になるまで、新しい型で表現できる最大値よりも 1 多い値を繰り返し加算または減算することによって、値が変換されます。
これは本質的に「追加(UINT_MAX+1)
」を意味します。
たとえば、典型的な 32 ビット システムでUINT_MAX
は、 0xFFFFFFFF
. ここで、次のものを変換するとします。
int ival = -1;
unsigned int uval = (unsigned int)ival;
標準によると 、次のival
ように昇格さunsigned int
れます。
uval = (UINT_MAX+1) + (-1);
もちろん、結果はuval = UINT_MAX;
. これは規格で定義されています。
unsigned から signed への変換は別の話です。上記の標準の前のセクションの直後は次のとおりです。
C99 6.3.1.3-p3
それ以外の場合、新しい型は署名され、値を表現できません。結果が実装定義であるか、実装定義のシグナルが発生します。
これは本質的に、unsigned の値が signed int の許容範囲内にない場合、同様のサイズの signed 結果への unsigned 変換に依存して実装に依存しない動作を行うことができないことを意味します。言い換えれば、これは:
unsigned int uval = INT_MAX;
int val = (int)uval;
INT_MAX は符号付き整数の範囲内の値であるため、動作が定義されていますが、これは次のとおりです。
unsigned int uval = INT_MAX + n;
int val = (int)uval;
n
( INT_MAX+n
<= UINT_MAX
) が実装定義であるような任意のもの。したがって、それに依存しないでください。これを避けるようにあなたを怖がらせるはずの標準の特定の部分は、「実装定義のシグナルが発生する」という可能性です。うわぁ。やらないでください。
上記のテキストの例
#include <stdio.h>
#include <stdlib.h>
#include <limits.h>
int main(int argc, char *argv[])
{
int ival = -1;
unsigned int uval = (unsigned int)ival;
printf("%d : %u\n", ival, uval);
uval = INT_MAX;
ival = (int)uval;
printf("%d : %u\n", ival, uval);
uval += 1;
ival = (int)uval;
printf("%d : %u\n", ival, uval);
return 0;
}
出力プラットフォーム: Apple LLVM バージョン 4.2 (clang-425.0.28)
-1 : 4294967295
2147483647 : 2147483647
-2147483648 : 2147483648