72

intを選択する理由をよりよく理解したいと思いますunsigned

個人的には、正当な理由がない限り、符号付きの値は好きではありません。たとえば、配列内のアイテムの数、文字列の長さ、メモリ ブロックのサイズなどです。これらの値が負になることはありません。このような値には意味がありません。intそのようなすべての場合に誤解を招くようなものを好むのはなぜですか?

intBjarne Stroustrup と Chandler Carruth の両方がこちらを好むようにアドバイスしたので、私はこれを尋ねunsigned ます (約 12:30') .

intovershortまたはlong-を使用する引数はint、ターゲット マシン アーキテクチャの「最も自然な」データ幅であることがわかります。

しかし、署名されていない署名は常に私を悩ませてきました。署名された値は、典型的な最新の CPU アーキテクチャで本当に高速ですか? それらをより良くするものは何ですか?

4

13 に答える 13

40

intコメントのリクエストによると:私は代わりに好きunsignedです...

  1. 短いです(本気です!)

  2. 1 - 2それはより一般的でより直感的です (つまり、あいまいな巨大な数ではなく、-1 であると仮定できるようにしたい)

  3. 範囲外の値を返すことでエラーを通知したい場合はどうすればよいですか?

もちろん反論もありますが、これらが私が整数を のint代わりに として宣言したい主な理由ですunsigned。もちろん、これは常に正しいとは限りません。それ以外の場合は、unsignedは単にタスクのためのより優れたツールです。私は、「署名付きのデフォルトを好む理由」という質問に具体的に答えているだけです。

于 2013-09-13T21:30:20.623 に答える
33

専門家が簡潔に言ったように、ビデオを言い換えさせてください。

アンドレイ・アレクサンドレスク

  • 簡単なガイドラインはありません。
  • システム プログラミングでは、さまざまなサイズと符号の整数が必要です。
  • 多くの変換と難解な規則が算術演算 ( for などauto) を支配するため、注意が必要です。

チャンドラー・カルース

  • 簡単なガイドラインを次に示します。
    1. 2 の補数演算またはビット パターンが必要でない限り、符号付き整数を使用します。
    2. 十分な最小の整数を使用してください。
    3. それ以外の場合は、項目を数えることができると思われる場合に使用intし、数えたい以上の場合は 64 ビット整数を使用します。
  • 心配するのはやめて、別のタイプやサイズが必要なときにツールを使用して知らせてください。

ビャルネ・ストロストラップ

  • intやらない理由がわかるまで使ってください。
  • ビット パターンにのみ unsigned を使用します。
  • 署名付きと未署名を混在させないでください

署名ルールについての警戒はさておき、専門家からの私の一言は次のとおりです。

適切な型を使用し、わからないint場合はわかるまで an を使用します。

于 2013-09-13T22:33:06.747 に答える
20

いくつかの理由:

  1. 算術演算ではunsigned常に符号なしが生成されます。これは、合理的に負の結果になる可能性がある整数量を減算するときに問題になる可能性があります。金額を減算して残高を生成するか、配列インデックスを減算して要素間の距離を生成することを考えてみてください。オペランドが符号なしの場合、完全に定義された結果が得られますが、ほぼ確実に無意味な結果になり、result < 0比較は常に false になります (これについては、最新のコンパイラが幸いなことに警告します)。

  2. unsigned符号付き整数と混合される演算を汚染するという厄介な特性があります。そのため、signed と unsigned を足して、結果が 0 より大きいかどうかを尋ねると、特に unsigned 整数型が の後ろに隠れている場合は、噛まれる可能性がありますtypedef

于 2013-09-13T21:30:57.550 に答える
9

C および C から派生した多くの言語の整数型には、2 つの一般的な使用例があります。数値を表す場合と、抽象代数環のメンバーを表す場合です。抽象代数に慣れていない人のために説明すると、環の背後にある主な概念は、環の 2 つの項目を加算、減算、または乗算すると、その環の別の項目が生成されるということです。つまり、クラッシュしたり、環の外に値を生成したりしてはなりません。32 ビット マシンでは、符号なし 0x12345678 を符号なし 0xFFFFFFFF に追加しても「オーバーフロー」しません。単純に結果 0x12345677 が生成されます。これは、2^32 を法とする整数のリングに対して定義されています (これは、0x12345678 を 0xFFFFFFFF に追加した算術結果が、つまり 0x112345677 は 0x12345677 mod 2^32 と合同です)。

概念的には、両方の目的 (数値を表す、または 2^n を法として合同な整数の環のメンバーを表す) は、符号付きと符号なしの両方の型によって提供され、多くの操作は両方の使用例で同じですが、いくつかの違いがあります。とりわけ、2 つの数値を加算しようとしても、正しい算術和以外の結果が得られるとは予想されません。言語がコードを生成しないことを保証するために必要なコードを生成する必要があるかどうかについては議論の余地がありますが (たとえば、代わりに例外がスローされるなど)、数値を表すために整数型を使用するコードの場合、そのような動作が望ましいと主張することができます。算術的に正しくない値を生成し、コンパイラがそのように動作することを禁止されるべきではありません。

C 標準の実装者は、数値を表すために符号付き整数型を使用し、2^n を法として合同な整数の代数環のメンバーを表すために符号なし型を使用することを決定しました。対照的に、Java はそのようなリングのメンバーを表すために符号付き整数を使用します (ただし、コンテキストによっては解釈が異なります。たとえば、異なるサイズの符号付き型間の変換は、符号なしのものとは異なる動作をします)。すべての非例外的なケースで数値として振る舞うプリミティブな整数型。

言語が数値と代数環数の両方に対して符号付きおよび符号なしの表現の選択肢を提供する場合、符号なしの数値を使用して、常に正になる量を表すことが理にかなっている可能性があります。ただし、符号なしの型だけが代数環のメンバーを表し、数値を表す型が符号付きの型だけである場合、値が常に正であっても、数値を表すように設計された型を使用して表す必要があります。

なお、(uint32_t)-1 が 0xFFFFFFFF であるのは、符号付きの値を unsigned にキャストすることは、unsigned の 0 を加算することと同じであり、unsigned の値に整数を加算することは、その大きさを加算または減算することと定義されているためです。 X=YZ の場合、X は X+Z=Y のようなその環の唯一のメンバーであることを指定する代数環の規則に従った符号なし値。符号なしの数学では、0xFFFFFFFF は、符号なしの 1 に加算すると符号なしのゼロになる唯一の数値です。

于 2013-09-13T22:16:37.703 に答える
8

最新のアーキテクチャーでも速度は同じです。問題unsigned intは、予期しない動作が発生する場合があることです。これにより、他の方法では表示されないバグが作成される可能性があります。

通常、値から 1 を引くと、値は小さくなります。ここで、変数signedunsigned int変数の両方を使用すると、1 を減算すると、はるかに大きな値が作成される場合があります。unsigned intとの主な違いはintunsigned int逆説的な結果を生成する値が一般的に使用される値 --- 0 --- であるのに対し、signed の場合、数値は通常の操作から安全に遠く離れていることです。

エラー値に対して -1 を返す限り --- 現代の考え方では、戻り値をテストするよりも例外をスローする方がよいと考えられています。

コードを適切に防御すれば、この問題が発生しないことは事実です。また、どこでも宗教的に unsigned を使用すれば問題ありません (加算のみを行い、減算を行わず、MAX_INT に近づくことがない場合)。どこでも unsigned int を使用しています。しかし、それには多くの規律が必要です。int多くのプログラムでは、他のバグに時間を費やすことができます。

于 2013-09-13T21:31:04.817 に答える
7

実際の質問に答えるには: 膨大な数のことについて、それは実際には問題ではありません。int2 番目のオペランドが最初のオペランドよりも大きい減算などを処理するのが少し簡単になり、それでも「期待される」結果が得られます。

符号付きと符号なしの数値で異なる唯一の命令は次のとおりであるため、99.9% のケースで速度の違いはまったくありません。

  1. 数字を長くする (符号付きの場合は符号を、符号なしの場合はゼロで埋める) - 両方を行うには同じ努力が必要です。
  2. 比較 - 符号付きの数値。プロセッサは、どちらかの数値が負かどうかを考慮する必要があります。しかし、繰り返しになりますが、符号付きまたは符号なしの数値と比較するのは同じ速度です-「最高ビットが設定されている数値は、最高ビットが設定されていない数値よりも小さい」と言うために異なる命令コードを使用しているだけです(本質的に)。[皮肉なことに、ほとんどの場合、異なるのは比較の RESULT を使用する操作です-最も一般的なケースは条件付きジャンプまたは分岐命令です-しかし、いずれにしても、入力がわずかに異なることを意味するように取られるだけで、同じ努力です。 ]。
  3. 掛け算と割り算。明らかに、符号付き乗算の場合、結果の符号変換が必要です。入力の 1 つの最上位ビットが設定されている場合、符号なしは結果の符号を変更すべきではありません。繰り返しますが、努力は (私たちが気にかけている限り) 同じです。

(他にも 1 つまたは 2 つのケースがあると思いますが、結果は同じです。署名されているか署名されていないかは実際には問題ではありません。操作を実行する労力は両方で同じです)。

于 2013-09-13T21:38:43.457 に答える
3

型は、型intよりも数学的な整数の動作によく似ていますunsigned

unsigned状況によって負の値を表す必要がないという理由だけで、型を好むのは単純です。

問題は、unsignedタイプがゼロのすぐ隣で不連続な動作をすることです。小さな負の値を計算しようとする操作は、代わりに大きな正の値を生成します。(さらに悪いことに、実装定義のものです。)

そのような代数関係は、とのような小さな値であっても、 が符号なしドメインで壊れていることをa < b意味します。a - b < 0a = 3b = 4

のような下降ループは、無署名for (i = max - 1; i >= 0; i--)にすると終了しませんi

符号なしの癖は、そのコードが正の量のみを表すことを期待しているかどうかに関係なく、コードに影響を与える問題を引き起こす可能性があります。

符号なし型の利点は、符号付き型のビット レベルで移植可能に定義されていない特定の操作が、符号なし型の場合に移植可能であることです。unsigned 型には符号ビットがないため、符号ビットによるシフトとマスキングは問題になりません。unsigned 型は、ビットマスク、およびプラットフォームに依存しない方法で正確な演算を実装するコードに適しています。符号なし操作は、非 2 の補数マシンでも 2 の補数セマンティクスをシミュレートします。多倍精度 (bignum) ライブラリを作成するには、符号付きの型ではなく、符号なしの型の配列を表現に使用する必要があります。

The unsigned types are also suitable in situations in which numbers behave like identifiers and not as arithmetic types. For instance, an IPv4 address can be represented in a 32 bit unsigned type. You wouldn't add together IPv4 addresses.

于 2013-09-13T22:00:40.483 に答える
2

int最も一般的に使用されるため、推奨されます。unsigned通常、ビット操作に関連付けられています。を見るたびにunsigned、ちょっとしたいじりに使われていると思います。

より大きな範囲が必要な場合は、64 ビット整数を使用してください。

インデックスを使用して何かを繰り返し処理している場合、通常、型には がありsize_type、署名付きか未署名かは気にする必要はありません。

速度は問題ではありません。

于 2013-09-13T21:33:09.133 に答える
2

考えられる正当な理由の 1 つは、オーバーフローが検出された場合です。

配列内の項目数、文字列の長さ、メモリ ブロックのサイズなどのユース ケースでは、unsigned int をオーバーフローさせることができ、変数を見ても違いに気付かない場合があります。符号付き int の場合、変数はゼロ未満であり、明らかに間違っています。

変数を使用するときに、変数がゼロかどうかを確認するだけです。このように、unsigned int の場合のように、すべての算術演算の後にオーバーフローをチェックする必要はありません。

于 2013-09-13T21:43:03.303 に答える