web-dev-qa-db-ja.com

署名されていない整数に関するベストプラクティスは何ですか?

私はどこでもunsigned intを使用しており、使用する必要があるかどうかわかりません。これは、データベースの主キーID列からカウンターなどへの可能性があります。数値が負にならないようにするには、常に符号なし整数を使用します。

しかし、私は他の人のコードから、誰もこれをしているようには見えないことに気づきました。私が見落としている重要なことはありますか?

編集:この質問から、Cでも、C++のように例外をスローするのではなく、エラーに対して負の値を返すのが一般的であることに気付きました。

45
wting

私が見落としている重要なことはありますか?

計算に符号付きタイプと符号なしタイプの両方、および異なるサイズが含まれる場合、タイプの昇格のルールが複雑になり、 予期しない動作 になる可能性があります。

これが、Java unsigned int型が省略された主な理由であると思います。

28

Michael は有効なポイントがあると思いますが、IMOは誰もが常にintを使用する理由(特にfor (int i = 0; i < max, i++)は、その方法で学習したことです。 「プログラミングの学習方法」のすべての例がintループでforを使用する場合、ごくわずかその実践に疑問を投げかけるでしょう。

もう1つの理由は、intuintより25%短いこと、そして私たち全員が怠惰であることです... ;-)

18
Treb

範囲情報を型にエンコードすることは良いことです。コンパイル時に適切な数値を使用することを強制します。

多くのアーキテクチャには、int-> float変換を処理するための特別な指示があるようです。 unsignedからの変換は遅くなる可能性があります(小さなビット)

11

符号付きタイプと符号なしタイプを混在させると、苦痛の世界に入り込む可能性があります。また、負の数を含む有効な範囲があるか、エラーを示すために値が必要で、-1が最も自然なことになるため、すべての符号なしの型を使用することはできません。したがって、最終的な結果として、多くのプログラマーはすべての符号付き整数型を使用します。

8
David Schwartz

私が使う unsigned int C++では、ほとんどの場合、配列インデックス、および0から始まるすべてのカウンターが対象です。「この変数は負にできません」と明示的に言うのが良いと思います。

7
quant_dev

私にとって、タイプはコミュニケーションに関するものです。 unsigned intを明示的に使用すると、署名された値は有効な値ではないことがわかります。これにより、コードを読み取るときに、変数名に加えていくつかの情報を追加できます。理想的には、非匿名タイプの方が多くのことを教えてくれますが、どこでもintを使用した場合よりも多くの情報が得られます。

残念ながら、誰もがコードが通信することについて非常に意識しているわけではありません。それがおそらく、値が少なくとも符号なしであっても、intがどこにでもある理由です。

7
daramarak

あなたが実際にsigned intの制限に近づくか、それを超えるかもしれない整数を扱うとき、あなたはこれに気をつけるべきです。 32ビット整数の正の最大値は2,147,483,647であるため、a)負になることは決してなく、b)2,147,483,648に達する可能性があることがわかっている場合は、unsigned intを使用する必要があります。データベースキーやカウンターを含むほとんどの場合、私はこれらの種類の数値にアプローチすることもないので、符号ビットが数値に使用されているのか、または符号を示すのに使用されているのか心配する必要はありません。

署名しないintが必要であることがわかっている場合を除いて、intを使用します。

3
Joel Etherton

これは、シンプルさと信頼性のトレードオフです。コンパイル時に検出できるバグが多いほど、ソフトウェアの信頼性が高くなります。人々や組織が異なれば、その範囲も異なります。

Adaで高信頼性プログラミングを行う場合、フィート単位の距離とメートル単位の距離などの変数に異なるタイプを使用し、誤って一方を他方に割り当てた場合、コンパイラーがフラグを立てます。これはガイド付きミサイルのプログラミングには最適ですが、Webフォームを検証する場合はやりすぎ(意図的にしゃれた)です。要件を満たしていれば、どちらの方法でも問題はありません。

3
Karl Bielefeldt

私はジョエル・イーサトンの推論に同意する傾向がありますが、反対の結論に至ります。私がそれを見る方法、あなたが数が署名されたタイプの限界に決して近づきそうにないことを知っているとしても、あなたがknow負の数が起こらないなら、それから使う理由はほとんどありません型の符号付きバリアント。

SQL ServerテーブルでBIGINT(32ビット整数)ではなくINTEGER(64ビット整数)を使用したのと同じ理由で、いくつかの選択インスタンスで使用しました。妥当な時間内にデータが32ビットの制限に達する確率はごくわずかですが、発生した場合、状況によっては非常に壊滅的な結果になる可能性があります。言語間でタイプを適切にマッピングすることを忘れないでください。そうしないと、本当に奇妙な奇妙なものになってしまいます...

つまり、データベースの主キー値など、署名されているかどうかに関係なく、壊れたデータやそれらの行に沿った何かを手動で修復しない限り、値を直接処理することはないためです。それは識別子であり、それ以上のものではありません。そのような場合、一貫性は、署名の正確な選択よりもおそらく重要です。それ以外の場合は、署名されている外部キー列と署名されていない外部キー列があり、明らかなパターンはありません。また、興味深い奇妙さもあります。

2
a CVn

スペースに制約のあるデータストレージおよびデータ交換コンテキストの外では、通常、署名された型を使用することをお勧めします。 32ビットの符号付き整数は小さすぎるが、今日は32ビットの符号なしの値で十分であるほとんどの場合、32ビットの符号なしの値も十分な大きさになる前に長くはありません。

符号なしの型を使用する必要があるのは、主に、複数の値を大きな値にアセンブルする(4バイトを32ビット数に変換するなど)、または大きな値を小さな値に分解する(32ビット数を4バイトとして格納する)場合です。 )、または定期的に「ロールオーバー」すると予想される量があり、それを処理する必要がある場合(住宅用のユーティリティメーターを考えてください。それらのほとんどは、測定値の間にロールオーバーしないように十分な桁数を持っています)年に3回読むが、メーターの耐用年数内にロールオーバーしないことを保証するのに十分でない場合)。符号なしの型は、多くの場合、その意味論が必要な場合にのみ使用されるべき十分な「奇妙さ」を持っています。

1
supercat

符号なし整数を使用して、コードとその意図をより明確にします。符号付き型と符号なし型の両方で算術演算を行うときに予期しない暗黙の変換を防ぐために行う1つのことは、符号なし変数に符号なしshort(通常は2バイト)を使用することです。これにはいくつかの理由があります。

  • 符号なしshort変数およびリテラル(int型)またはint型の変数を使用して算術演算を行う場合、これにより、式を評価する前に符号なし変数が常にintに昇格されます。 。これにより、当然ながら式の結果がsigned intに収まると想定して、符号付き型と符号なし型で演算を行う予期しない動作が回避されます。
  • ほとんどの場合、使用している符号なし変数は、符号なし2バイトshortの最大値(65,535)を超えません。

一般的な原則は、署名された変数の型は、署名された変数への昇格を確実にするために、署名された変数の型よりも低いランクを持つ必要があるということです。そうすれば、予期しないオーバーフロー動作は発生しません。もちろん、これを常に保証することはできませんが、(ほとんどの場合)これを保証することは現実的です。

たとえば、最近、次のようなforループがいくつかありました。

const unsigned short cuint = 5;
for(unsigned short i=0; i<10; ++i)
{
    if((i-2)%cuint == 0)
    {
       //Do something
    }
}

リテラル '2'はint型です。 iがunsigned shortではなくunsigned intの場合、部分式(i-2)では、2がunsigned intに昇格されます(unsigned intはsigned intよりも優先度が高いため)。 i = 0の場合、部分式は(0u-2u)=オーバーフローによる大きな値になります。 i = 1と同じ考え方です。ただし、iは符号なしshortであるため、int型が署名されているリテラル '2'と同じ型に昇格され、すべてが正常に機能します。

安全性を高めるために:実装しているアーキテクチャでintが2バイトになるというまれなケースでは、unsigned short変数が適合しない場合、算術式の両方のオペランドがunsigned intに昇格する可能性があります。符号付き2バイト整数に変換します。後者の最大値は32,767 <65,535です。 (詳細については https://stackoverflow.com/questions/17832815/c-implicit-conversion-signed-unsigned を参照してください)。これを防ぐには、次のようにプログラムにstatic_assertを追加するだけです。

static_assert(sizeof(int) == 4, "int must be 4 bytes");

また、intが2バイトのアーキテクチャではコンパイルできません。

1
AdmiralAdama