私はどこでも符号なしintを使用していますが、そうすべきかどうかはわかりません。これは、データベースの主キーID列からカウンタなどになります。数値が負の値にならないようにする場合は、常にunsigned intを使用します。
しかし、他のコードでは、他の誰もこれを行っていないようです。私が見落としている重要なものはありますか?
編集:この質問以来、私はCでは、C ++のように例外をスローするのではなく、エラーに対して負の値を返すことが一般的であることに気付きました。
私はどこでも符号なしintを使用していますが、そうすべきかどうかはわかりません。これは、データベースの主キーID列からカウンタなどになります。数値が負の値にならないようにする場合は、常にunsigned intを使用します。
しかし、他のコードでは、他の誰もこれを行っていないようです。私が見落としている重要なものはありますか?
編集:この質問以来、私はCでは、C ++のように例外をスローするのではなく、エラーに対して負の値を返すことが一般的であることに気付きました。
回答:
私が見落としている重要なものはありますか?
計算に符号付き型と符号なし型の両方と異なるサイズが含まれる場合、型の昇格の規則が複雑になり、予期しない動作が発生する可能性があります。
これがJavaが符号なしint型を省略した主な理由だと思います。
int
ただし、このような困難はありませんが、符号なしの型は小さくなります(計算がに進むためint
)。符号なしバイト型の欠如について、私は何も言うことがありません。
Michaelには有効なポイントがあると思いますが、IMOが常に(特にfor (int i = 0; i < max, i++
)intを使用する理由は、そのように学んだからです。「プログラミングの学習方法」の本のすべての例がループで使用さint
れているfor
場合、その実践に疑問を呈する人はほとんどいません。
もう1つの理由は、int
よりも25%短いことuint
です。
++
ループインデックスが反復子またはその他の非基本型(またはコンパイラーが本当に高密度)である場合、その特定の動作はほとんど必要なく、コピーを無意味に混乱させることさえあるという事実にもかかわらず、増分時に誰もが後置記号を使用する理由です。
範囲情報をタイプにエンコードするのは良いことです。コンパイル時に適切な数値を使用することを強制します。
多くのアーキテクチャには、int
-> float
変換を処理するための特別な指示があるようです。からの変換unsigned
が遅くなる場合があります(ほんの少し)。
署名されたタイプと署名されていないタイプを混在させると、苦痛の世界に陥ります。そして、すべての符号なし型を使用することはできません。なぜなら、負の数値を含む有効な範囲を持つものに遭遇するか、エラーを示す値が必要であり、-1が最も自然だからです。そのため、最終的な結果は、多くのプログラマーがすべての符号付き整数型を使用することです。
私にとって、タイプとはコミュニケーションに関するものです。明示的に符号なし整数を使用することにより、符号付きの値は有効な値ではないことを教えてくれます。これにより、コードを読み取るときに変数名に加えていくつかの情報を追加できます。理想的には、匿名ではないタイプの方が詳細を教えてくれますが、あらゆる場所でintを使用した場合よりも多くの情報が得られます。
残念ながら、誰もが自分のコードが伝えることについてあまり意識していないわけではありません。それはおそらく、値が少なくとも符号なしであっても、どこにでもintを見る理由です。
実際に符号付きintの制限に近づいたり、超えたりする可能性のある整数を扱う場合は、これに注意する必要があります。32ビット整数の正の最大値は2,147,483,647であるため、a)負にならず、b)2,147,483,648に達する可能性があることがわかっている場合は、unsigned intを使用する必要があります。データベースキーとカウンターを含むほとんどの場合、これらの種類の数値にアプローチすることはないため、数値に符号ビットを使用するのか、符号を示すのに悩む必要はありません。
私は言うだろう:あなたがあなたが署名されていないintを必要とすることを知っていない限りintを使用する。
シンプルさと信頼性のトレードオフです。コンパイル時にキャッチできるバグが多いほど、ソフトウェアの信頼性は高くなります。さまざまな人々や組織が、そのスペクトルに沿ってさまざまなポイントにいます。
Adaで高信頼性プログラミングを行う場合、フィート単位の距離とメートル単位の距離などの変数に異なるタイプを使用し、誤って一方に割り当てた場合にコンパイラがフラグを立てます。これは、誘導ミサイルのプログラミングには最適ですが、Webフォームを検証している場合はやり過ぎです(しゃれを意図しています)。要件に適合する限り、どちらの方法でも必ずしも問題はありません。
私はジョエル・イーサトンの推論に同意する傾向があるが、反対の結論に達する。数字が符号付き型の限界に近づきそうにないことを知っていたとしても、負の数が発生しないことを知っていれば、型の符号付きバリアントを使用する理由はほとんどありません。
いくつかの選択したインスタンスで、SQL Serverテーブルで(32 BIGINT
ビット整数)ではなく(64ビット整数)を使用しているのと同じ理由でINTEGER
。妥当な時間内にデータが32ビットの制限に達する可能性はごくわずかですが、発生した場合、状況によっては非常に壊滅的な結果になる可能性があります。言語間でタイプを適切にマッピングするようにしてください。そうしないと、本当に面白い奇妙な結果になってしまいます...
ただし、データベースの主キー値など、署名されたものまたは署名されていないものについては、実際には問題ではありません。それは識別子であり、それ以上のものではありません。これらの場合、署名の正確な選択よりもおそらく一貫性が重要です。そうしないと、明らかなパターンのない、署名された外部キー列と署名されていない他の列ができあがります。
スペースに制約のあるデータストレージコンテキストとデータ交換コンテキストの外側では、一般に署名された型を使用することをお勧めします。32ビットの符号付き整数は小さすぎるが、今日では32ビットの符号なしの値で十分な場合、ほとんどの場合、32ビットの符号なしの値が十分に大きくなる前に長くなりません。
符号なしの型を使用する主な時間は、複数の値を大きな値に組み立てる(4バイトを32ビット数に変換する)か、大きな値を小さな値に分解する(例:32ビット数を4バイトとして格納する) )、または定期的に「ロールオーバー」することが予想される数量があり、それに対処する必要がある場合(住宅用ユーティリティメーターの場合、ほとんどの場合、読み取り間でロールオーバーしないように十分な桁がある) 1年に3回読まれても、メーターの耐用年数内でロールオーバーしないようにするには十分ではありません)。符号なしの型は、しばしばセマンティクスが必要な場合にのみ使用されるほど十分な「奇妙さ」を持っています。
size_t
署名されておらず署名されている理由がありptrdiff_t
ます。
符号なしintを使用して、コードとその意図をより明確にします。符号付き型と符号なし型の両方で算術を行うときに予期しない暗黙的な変換を防ぐために行うことの1つは、符号なし変数に符号なしshort(通常2バイト)を使用することです。これはいくつかの理由で効果的です:
一般的な原則は、署名された型への昇格を保証するために、署名されていない変数の型は、署名された変数の型よりも低いランクを持つ必要があるということです。そうすれば、予期しないオーバーフロー動作は発生しません。明らかにこれを常に保証することはできませんが、(ほとんどの場合)これを保証することは可能です。
たとえば、最近、次のような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であるため、リテラル「2」と同じ型に昇格されます。これは符号付きintであり、すべて正常に機能します。
安全性を高めるために:実装しているアーキテクチャが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バイトのアーキテクチャではコンパイルされません。
for(unsigned int n = 10; n >= 0; n --)
(無限ループ)に注意してください