web-dev-qa-db-ja.com

「長い」禁止は意味がありますか?

今日のクロスプラットフォームC++(またはC)の世界では、 have

Data model  | short |   int |   long | long long | pointers/size_t  | Sample operating systems
... 
LLP64/IL32P64   16      32      32     64           64                Microsoft Windows (x86-64 and IA-64)
LP64/I32LP64    16      32      64     64           64                Most Unix and Unix-like systems, e.g. Solaris, Linux, BSD, and OS X; z/OS
...

これが今日意味することは、「一般的な」(符号付き)整数の場合、intで十分であり、C++アプリケーションコードを作成するときにデフォルトの整数型として引き続き使用できる可能性があるということです。また、現在の実用的な目的のために-プラットフォーム間で一貫したサイズになります。

ユースケースに少なくとも64ビットが必要な場合、今日はlong long、ただし ビット数を指定するタイプ または__int64typeの方がわかりやすいかもしれません。

これにより、longが途中に残り、アプリケーションコードからlongの使用を完全に禁止することを検討しています

これは理にかなっていますか、またはクロスプラットフォームを実行する必要がある最新のC++(またはC)コードでlongを使用するケースがあります? (プラットフォームはデスクトップ、モバイルデバイスですが、マイクロコントローラー、DSPなどではありません)


おそらく興味深い背景リンク:

109
Martin Ba

今日longを使用する唯一の理由は、それを使用する外部インターフェイスを呼び出すか実装するときです。

あなたの投稿で言っているように、shortとintは今日のすべての主要なデスクトップ/サーバー/モバイルプラットフォームでかなり安定した特性を持っています。ですから、一般的にそれらを避ける理由はほとんどありません。

一方、longはごちゃごちゃです。すべての32ビットシステムで、次の特徴があることを認識しています。

  1. サイズはちょうど32ビットでした。
  2. メモリアドレスと同じサイズでした。
  3. これは、通常のレジスターに保持され、単一の命令で処理できるデータの最大単位と同じサイズでした。

これらの特性の1つ以上に基づいて大量のコードが記述されました。ただし、64ビットへの移行により、それらすべてを保持することはできませんでした。 Unixライクなプラットフォームは、特性1を犠牲にして特性2と3を維持するLP64を採用しました。Win64は、特性2と3を犠牲にして特性1を維持するLLP64を採用しました。その結果、これらの特性のいずれにも依存できなくなりました。 IMOはlongを使用する理由をほとんど残していません。

サイズが正確に32ビットの型が必要な場合は、int32_tを使用する必要があります。

ポインタと同じサイズの型が必要な場合は、intptr_t(またはより適切なuintptr_t)を使用する必要があります。

単一のレジスター/命令で処理できる最大の項目であるタイプが必要な場合、残念ながら、標準では提供されていないと思います。 size_tはほとんどの一般的なプラットフォームで適切ですが、 x32 にはありません。


追伸.

「高速」または「最小」タイプは気にしません。 「最小」タイプは、移植性を気にしてCHAR_BIT != 8であるアーキテクチャを本当に不明瞭にする場合にのみ重要です。実際の「高速」タイプのサイズはかなり任意のようです。 Linuxはそれらを少なくともポインタと同じサイズにするようです。これは、x86-64やarm64などの高速32ビットサポートを備えた64ビットプラットフォームでは愚かです。 IIRC iOSはそれらを可能な限り小さくします。他のシステムが何をしているのかわかりません。


P.P.S

unsigned long(プレーンlongではない)を使用する理由の1つは、モジュロ動作が保証されているためです。残念ながら、Cのめちゃくちゃなプロモーションルールにより、intより小さい符号なしの型はモジュロ動作をしません。

今日のすべての主要なプラットフォームでuint32_tはintと同じかそれより大きいため、モジュロ動作をします。ただし、歴史的に存在し、intが64ビットであり、したがってuint32_tがモジュロ動作を行わない将来のプラットフォームにも理論的には存在する可能性があります。

個人的には、式の最初に「1u *」または「0u +」を使用してモジュロ動作を強制するという習慣を身に付ける方が良いと思います。これは、あらゆるサイズの符号なしの型で機能するためです。

18
Peter Green

あなたの質問で述べたように、現代のソフトウェアはすべて、インターネット上のプラットフォームとシステムの間の相互運用に関するものです。 CおよびC++標準では、特定のサイズではなく整数型のサイズの範囲が指定されます(JavaおよびC#などの言語とは対照的)。

異なるプラットフォームでコンパイルされたソフトウェアが同じ方法で同じデータで動作するようにするおよび他のソフトウェアが同じサイズを使用してソフトウェアとやり取りできるようにするには、固定サイズを使用する必要があります-サイズの整数。

入る - <cstdint> これは正確にそれを提供し、すべてのコンパイラおよび標準ライブラリプラットフォームが提供する必要がある標準ヘッダーです。注:このヘッダーはC++ 11の時点でのみ必要でしたが、いずれにせよ、多くの古いライブラリ実装で提供されていました。

64ビットの符号なし整数が必要ですか?使用する uint64_t。符号付き32ビット整数?使用する int32_t。ヘッダーのタイプはオプションですが、最新のプラットフォームはそのヘッダーで定義されているすべてのタイプをサポートする必要があります。

他のシステムとの通信に使用されるデータ構造などでは、特定のビット幅が必要になる場合があります。それ以外の場合はそうではありません。それほど厳密ではない状況では、<cstdint>は最小幅のタイプを提供します。

leastバリアントがあります:int_leastXX_tは、最小XXビットの整数型になります。 XXビットを提供する最小のタイプを使用しますが、タイプは指定されたビット数より大きくすることができます。実際には、これらは通常、正確なビット数を与える上記のタイプと同じです。

fastバリアントもあります:int_fastXX_tは少なくともXXビットですが、特定のプラットフォームで高速に実行されるタイプを使用する必要があります。この文脈での「高速」の定義は規定されていません。ただし、実際には、これは通常、CPUのレジスタサイズよりも小さい型がCPUのレジスタサイズの型にエイリアスになる可能性があることを意味します。たとえば、Visual C++ 2015のヘッダーでは、int_fast16_tは32ビット整数です。32ビット演算は、x86では16ビット演算よりも全体的に高速であるためです。

プラットフォームに関係なく、プログラムが実行する計算の結果を保持できる型を使用できるはずなので、これはすべて重要です。整数オーバーフローの違いにより、プログラムがあるプラットフォームでは正しい結果を生成し、別のプラットフォームでは正しくない結果を生成する場合、それは悪いことです。標準の整数型を使用することにより、使用される整数のサイズに関して、異なるプラットフォームでの結果が同じであることを保証します(もちろん、プラットフォーム間に他の違いがある可能性があります)整数幅)。

したがって、はい、longは最新のC++コードから禁止する必要があります。 intshortlong long

203
user22815

いいえ、組み込みの整数型を禁止するのはばかげています。しかし、彼らは虐待されるべきではありません。

正確にである整数が必要な場合 N ビット幅、std::intN_t(またはunsignedバージョンが必要な場合はstd::uintN_t)を使用します。 intを32ビット整数として、long longを64ビット整数として考えるのは間違っています。現在のプラットフォームではこのようになる可能性がありますが、これは実装定義の動作に依存しています。

固定幅整数型の使用は、他のテクノロジーとの相互運用にも役立ちます。たとえば、アプリケーションの一部がJavaで記述され、その他がC++で記述されている場合、整数型を一致させて一貫した結果を得ることができます。( Javaはセマンティクスが明確に定義されていますが、C++のsignedオーバーフローは未定義の動作であるため、一貫性が高い目標です。)異なるコンピューティングホスト間でデータを交換する場合にも重要です。

正確に必要ない場合 N ビット、ただし十分に広いのタイプのみの場合は、std::int_leastN_t(スペース用に最適化)またはstd::int_fastN_t(速度用に最適化)の使用を検討してください。繰り返しますが、両方のファミリにもunsignedの対応物があります。

それで、組み込み型をいつ使うのですか?さて、規格はそれらの幅を正確に指定していないので、しないでください実際のビット幅ではなく他の特性に注意するときにそれらを使用してください。

charは、ハードウェアがアドレス可能な最小の整数です。言語は実際には、任意のメモリのエイリアスに使用することを強制します。また、(狭い)文字列を表すための唯一の実行可能なタイプでもあります。

intは通常、マシンが処理できる最高速のタイプです。これは、1つの命令で(ビットをマスクしたりシフトしたりせずに)ロードおよび格納できるほど十分に広く、(最も)効率的なハードウェア命令で操作できるように十分に狭くなります。したがって、intは、データを渡し、オーバーフローが問題にならない場合に算術演算を行うのに最適な選択肢です。たとえば、列挙型のデフォルトの基本タイプはintです。可能であれば、32ビット整数に変更しないでください。また、–1、0、および1のみにできる値がある場合は、intが最適な選択肢です。ただし、それらの巨大な配列を格納する場合を除いて、個々の要素にアクセスするために高い価格を支払う必要があるという犠牲を払って、よりコンパクトなデータ型。より効率的なキャッシングは、おそらくこれらに報いるでしょう。多くのオペレーティングシステム関数もintで定義されています。彼らの議論と結果を前後に変換するのはばかげたことでしょう。これはおそらく導入オーバーフローエラーです。

longは通常、単一の機械語命令で処理できる最も広い型になります。これは、特にunsigned longを生データやあらゆる種類のビット操作に関するものを扱うために非常に魅力的にします。たとえば、ビットベクトルの実装でunsigned longが表示されることを期待しています。コードが慎重に記述されている場合、タイプが実際にどのくらい広いかは問題ではありません(コードが自動的に適応するため)。ネイティブマシンワードが32ビットであるプラットフォームでは、ビットベクトルのバッキング配列をunsigned 32ビット整数の配列にすることが最も望ましいです。とにかく不要なビットをシフトしてマスクするためだけに、高価な命令を介してロードされます。一方、プラットフォームのネイティブのWordサイズが64ビットの場合、「最初のセットを見つける」などの操作が最大2倍の速度で実行される可能性があるため、そのタイプの配列が必要です。したがって、あなたが説明しているlongデータ型の「問題」は、そのサイズがプラットフォームごとに異なり、実際にはfeatureであり、十分に使用できます。これは、組み込み型を特定のビット幅の型と考えた場合にのみ問題になります。

charintおよびlongは、上記のように非常に便利なタイプです。 shortlong longは、セマンティクスがあまり明確でないため、それほど有用ではありません。

38
5gon12eder

別の答えは、cstdint型とその中のあまり知られていないバリエーションについてすでに詳述しています。

私はそれに追加したいと思います:

ドメイン固有のタイプ名を使用する

つまり、パラメーターと変数をuint32_t(確かにlong!ではない)と宣言せずに、channel_id_typeroom_count_typeなどの名前を付けます。

図書館について

longまたはwhatnotを使用するサードパーティのライブラリは、特にそれらへの参照またはポインタとして使用される場合、煩わしい場合があります。

bestはラッパーを作ることです。

私の戦略は、一般に、使用されるキャストのような関数のセットを作成することです。それらはオーバーロードされ、対応する型と完全に一致する型だけを受け入れ、必要なポインタなどのバリエーションも受け入れます。これらはos/compiler/settingsに固有に定義されています。これにより、警告を削除しながら、「正しい」変換のみが使用されるようにすることができます。

channel_id_type cid_out;
...
SomeLibFoo (same_thing_really<int*>(&cid_out));

特に、32ビットを生成するさまざまなプリミティブ型では、int32_tの定義方法の選択がライブラリー呼び出しと一致しない場合があります(Windowsのintとlongなど)。

キャストのような関数documentsクラッシュ、関数のパラメーターに一致する結果のコンパイル時チェックを提供し、警告またはエラーを削除しますif if only only実際のタイプ関連する実際のサイズに一致します。つまり、(Windowsで)int*またはlong*を渡すとオーバーロードされて定義され、そうでない場合はコンパイル時エラーが発生します。

したがって、ライブラリが更新されたり、誰かがchannel_id_typeを変更した場合、これは引き続き検証されます。

6
JDługosz