整数はデータ型として使いすぎていませんか?


9

ほとんどのアプリケーション開発者は、本当に符号なし整数を使用するつもりで符号付き整数を使用していますか?私はいつもそれをしているので、同僚もそうします。他の多くの広範なコードベース(Delphi VCL以外)は見たことがなく、インターネット上の例では通常整数を使用しています。一方、VCL開発者は独自のデータ型を使用します(これは、変数を宣言するための最も非遅延的な方法です)。

このようなコードは少し恐ろしいようです

TStuffRec = record
   recordID : Integer;
   thingID : Integer;
   otherThingID : Integer;
end;

それが次のように書けるとき

TStuffRec = record
   recordID : Cardinal;
   thingID : Cardinal;
   otherThingID : Cardinal;
end;

機能的には、これらのレコードはほぼ同じように機能します(64ビットDelphiでも同じように機能し続けることを期待しています)。ただし、非常に大きな数では変換の問題が発生します。

ただし、unsigned intを使用することにも欠点があります。主に2つを混合することがいかにうるさいかから生じます。

本当の質問は、これは実際にベストプラクティスについて考えられたり、ベストプラクティスに含まれたりすることですか?通常は開発者次第ですか?


5
ピーター、あなたはDelphi固有の答えだけを探していますか?
アダムリア

3
@Anna Delphiのデータ型がどのように機能するかを理解すると、最も優れた答えが得られます。Cプログラマーがこの質問を理解して答えることができると私は合理的に確信しています。
Peter Turner

回答:


9

Delphiで符号なし整数型をそれほど使用しない理由の1つは、符号付き整数と混合すると問題が発生する可能性があることです。ここに一度私を噛んだものがあります:

for i := 0 to List.Count - 1 do
  //do something here

私はi符号なし整数として宣言しました(結局のところ、それは0から始まるリストへのインデックスであり、負List.Countである必要はありませんよね?)。ただし、0の場合、期待どおりにループを短絡しません。0 - 1非常に高い正の数に評価されます。おっとっと!

符号付き整数と符号なし整数の混合に固有の潜在的な安全性の問題と範囲の問題の間(より大きい正の数が必要になるhigh(signed whatever)場合は、結局high(unsigned whatever)、それよりも大きい正の数も必要になる可能性が高いため、移動同じサイズの符号付きから符号なしに切り替えるのではなく、次に大きいサイズまでは、通常は正しいアクションです)ほとんどのデータを表す場合、私は実際には符号なし整数のあまり多くの用途を発見していません。


2
ある程度関連しているが、必要以上に小さい可能性のあるデータ型を使用することの主なリスクの1つは(単に署名されていないか、署名されているのではなく)、終了条件が計画よりも大きい場合、実際には無限ループになる可能性があることです。カウンターが何度もオーバーフローするので。後から考えると馬鹿げているように思えますが、私はかつてすべてのバイト値をループ処理することになっていたプログラムを書いたことがあり、バイトカウンターでは不可能だと最終的に納得するまでに約15分かかりました。
アーロンノート

@Aaronaught:Delphiではありません。(少なくとも、組み込みのオーバーフローチェックを無効にするなど、愚かなことをしない限り、そうではありません。)無限ループではなく、カウンターがオーバーフローすると例外が発生します。それはまだバグですが、追跡するのははるかに簡単です。
メイソンウィーラー、

そう言うなら。Delphiでは常にオーバーフローチェックを無効にしました。ハッシュコードやチェックサムのようなものからの偽陽性に際限なく攻撃された後、私はその「機能」を完全にあきらめました。しかし、私はあなたが正しいと思います、それはその特定のエラーをキャッチしたでしょう。
アーロンノート、

@Aaronaught:ええ、そうです、オーバーフローしてラップアラウンドするように特別に設計されたハッシュコードやチェックサムのようなものに対してはそれを無効にしたいと思います。しかし、オーバーフローしてラップアラウンドするように設計されていない汎用計算では、これは重要な安全機能であり、オフにすることは、シートベルトなしで運転するようなものです。
メイソンウィーラー、

おそらくあなたは忘れていたかもしれませんが、オーバーフローチェックとコンパイラディレクティブは、Delphiの古いバージョンでは信じられないほどバグが多かったです。{$ O-} / {$ O +}ブロックの真ん中でデバッガーが直接停止するのを見て、オーバーフローを陽気に報告した後、何度か髪を引き裂いたことを鮮明に覚えています。しばらくすると、私はそれを受け入れることができなくなり、単にグローバルに無効にしました。繰り返しになりますが、そうです、この問題は検出されたでしょうが、誤検知の数に見合うだけの価値があるとはまだ思っていません。もちろん、一人一人に!
アーロンノート

3

正直なところ、私は癖で整数を使う傾向があります。私は、それらがほとんどの状況に十分な範囲を提供し、負の値(-1など)を許容することに慣れました。実際、多くの場合、バイト/ワード/短整数を使用する方が適切です。今それについて考えて、私はこれらのスポットに焦点を合わせることができます:

  • 視点。タイルマップのサイズは192x192タイルに制限されているため、タイルとループのアドレス指定にバイトを使用できます。しかし、マップのサイズを大きくする場合は、すべての使用を経験して、たとえば単語に置き換える必要があります。マップ外のオブジェクトを許可する必要がある場合、smallintに変更するために再度移動する必要があります。

  • ループ。「i:= 0からCount-1まで」というループを書くことがよくあります。「i」がバイトで、Count = 0が0から255までのループである場合、どうなるでしょうか。

  • 制服。「var i:integer;」を覚えて適用する方が簡単です。それぞれの場合で停止して「うーん..ここでは0..120の範囲を処理しています。十分ではありません。または「なぜこの場所ではshortintではなくsmallintなのですか?」

  • 組み合わせる。2つ以上のクラスを組み合わせる必要がある場合、目的に応じて異なるデータ型を使用している可能性があります。より広い型を使用すると、不要な変換をスキップできます。

  • -1。値が0..n-1の範囲にある場合でも、「値なし/不明/初期化されていない/空」の値を設定する必要があることがよくあります。これは、一般的な慣例では-1です。

整数を使用すると、これらの問題をすべてスキップし、必要のない低レベルの最適化を忘れ、より高いレベルに移動して、より現実的な問題に集中できます。

PSいつ他のタイプを使用しますか?

  • カウンター。これらは決して負ではなく、クラス外では読み取り専用です。
  • パフォーマンス/メモリ上の理由により、特定の場所でより短いデータ型を使用することを強制します。

1

ベストプラクティスは、使用するデータ(期待されるデータ)のニーズに合ったデータ型を使用することです。

C#の例:0〜255のみをサポートする必要がある場合は、バイトを使用します。

1,000,000のネガティブとポジティブをサポートする必要がある場合は、int。

42億を超える場合は、longを使用します。

正しいタイプを選択することで、プログラムは最適な量のメモリを使用するだけでなく、タイプごとに異なる量のメモリを使用します。

これは、MSDNのC#intリファレンスです。

int 
 -2,147,483,648 to 2,147,483,647
 Signed 32-bit integer

uint 
 0 to 4,294,967,295
 Unsigned 32-bit integer

long 
 -9,223,372,036,854,775,808 to 9,223,372,036,854,775,807
 Signed 64-bit integer

ulong 
 0 to 18,446,744,073,709,551,615
 Unsigned 64-bit integer

C#(または一般に.net)では、128ビットマシンでlongおよびulongは128ビットになりますか?Delphiでは、Integerデータ型は32ビットマシンでは32ビットであり、64ビットマシンでは明らかに64ビットになるためです。
Peter Turner

1
@ピーターターナー:いいえ、C#では、コードが実行されるマシンに関係なくint、はの省略形ですSystem.Int32
nikie、2011

@nikie、それはちょうどそのようなものtype int System.Int32か何かですか?フレームワークの将来のバージョンで簡単に変更できるでしょうか?
Peter Turner

@ピーター・ターナー/ニキー(sizeof(int).ToString()); ==> 4(sizeof(Int64).ToString());を返します。==>私の64ビットWindows OSでは8を返します。ニキー、統計として、intは本当にちょうどInt32です。
ジョンレイナー、2011

1
注意すべき点の1つは、すべての型が共通言語仕様に準拠しているわけではないことです。uintはそのような非準拠タイプの1つです。つまり、ライブラリが記述されているもの以外の.NET言語でそのAPIを使用する機能を壊さないように、公開されたAPIで使用しないでください。これが、.NETフレームワークでもある理由です。 API自体がintどこで使用するuintかを使用しています。
アダム・リア

1

符号なし整数型は、基数を表す言語で基数を表すためにのみ使用する必要があります。Cを実行したコンピューターがたまたま機能したため、符号なし整数型はmod-2 ^ n代数環のメンバーとして振る舞い(オーバーフローした計算は予想通りに「折り返される」ことを意味します)、多くの場合、そのような型はそのような振る舞いが基数や数学的整数の振る舞いと矛盾する場合でも、抽象的な代数環として振る舞う必要があります。

プラットフォームが基数と代数リングの個別のタイプを完全にサポートしている場合、基数は基数タイプ(およびリングタイプを使用してラップする必要があるもの)を使用して処理することをお勧めします。このような型は、符号付き型の2倍のサイズの数値を格納できるだけでなく、そのような型のパラメーターを受け取るメソッドは、それが負であるかどうかをチェックする必要がありませんでした。

ただし、基数型が比較的少ないため、一般的には整数を使用して数学的整数と基数の両方を表すのが最善です。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.