「長い」禁止は理にかなっていますか?


109

今日のクロスプラットフォームC ++(またはC)の世界では、次のことができます

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が、ビットネスを指定する型のいずれかを使用するか、その__int64型の方が意味があるかもしれません。

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

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


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


14
longを使用するライブラリの呼び出しをどのように処理しますか?
アンヘル

14
long32ビットを保証する唯一の方法です。int16ビットにすることができるため、一部のアプリケーションでは十分ではありません。はい、int現代のコンパイラでは時々16ビットです。はい、マイクロコントローラーでソフトウェアを作成します。Arduinosなどの増加は言うまでもなく、iPhoneやAndroidデバイスの増加に伴い、PCよりもマイクロコントローラーのユーザーが多いソフトウェアを書く人が増えていると主張します
。– slebetman

53
なぜchar、short、int、long、long longを禁止し、[u] intXX_t型を使用しないのですか?
immibis

7
@slebetman私はもう少し掘り下げて、要件がまだあるように見えますが、§3.9.1.3には隠されていますが、C ++標準では次のように記述されています。 4.2.1。」そして、C標準の§5.2.4.2.1では、あなたが書いた通りに最小範囲を述べています。あなたは絶対に正しかった。:)明らかにC ++標準のコピーを所有しているだけでは十分ではなく、C標準のコピーも見つける必要があります。
トミーアンデルセン

11
DOSBox / Turbo C ++の世界intはまだありませんが、まだ16ビットです。言いたくありませんが、「今日のクロスプラットフォームの世界」について書こうとするなら、インド亜大陸全体を無視することはできません。
に軌道上

回答:


17

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ではそうではありません。


PS

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


PPS

使用する理由の1つunsigned long(ただしプレーンでlongはない)は、モジュロ動作を保証しているためです。残念なことに、Cの混乱したプロモーションルールのため、intモジュロ動作を持たないものよりも小さい符号なしの型があります。

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

個人的には、方程式の先頭に「1u *」または「0u +」を使用することで、モジュロ動作を強制する習慣を身に付けた方が良いと言えます。


1
すべての「指定サイズ」型は、組み込み型とは異なるセマンティクスを指定できれば、はるかに便利です。例えば、65535の数字0を保持することができるであろうが、任意の可能性タイプと一緒にかかわらず、「INT」の大きさのMOD-65536算術を使用するタイプ有することが有用であろうと必ずしも一貫して可能にそれより大きい番号を保持しています。ほとんどのマシンでは、どのタイプのタイプが最も速いかはコンテキストに依存するため、コンパイラーが任意に選択できるようにすると、速度が最適になります。
-supercat

204

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

お使いのソフトウェアは、異なるプラットフォーム上でコンパイルすることを確実にするために、同じデータと同じように動作し、他のソフトウェアが同じサイズを使用して、ソフトウェアと相互作用することができることを確実にするために、あなたは固定サイズの整数を使用する必要があります。

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

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

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

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

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

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

そのlongため、最新のC ++コードから禁止する必要があります。そうすべきであるintshortlong long


20
これをさらに投票するために、他の5つのアカウントがあればいいのにと思います。
スティーブンバーナップ16

4
+1、構造体のサイズがコンパイルしているコンピューターに依存する場合にのみ発生する奇妙なメモリエラーを処理しました。
ジョシュアスナイダー

9
@Wildcardは、C ++の一部でもあるCヘッダーです。「c」プレフィックスを参照してください。また、C ++コンパイルユニットでdを実行するstdときに名前空間にtypedefを配置する方法もあります#includeが、リンクしたドキュメントには記載されておらず、Visual Studioはそれらにアクセスする方法を気にしていないようです。

11
禁止intは...過剰かもしれませんか?。コードは、「アプリのコード」のためにそれを禁止すべてのようにあいまいなあいまいな(とない)プラットフォーム間で非常に移植する必要がある場合(私はそれを検討する当社の開発者と非常によく座ってないかもしれません。
マーティンBaの

5
@Snowman #include <cstdint>は、型を配置するために必要std::あり、(残念ながら)オプションでそれらをグローバル名前空間に配置することできます。#include <stdint.h>まさに逆です。同じことは、Cヘッダーの他のペアにも当てはまります。stackoverflow.com/a/13643019/2757035を参照してください。いくつかの実装によって確立された貧弱な慣習に一見屈するのではなく、それぞれがそれぞれの必要な名前空間に影響を与えることだけがスタンダードに要求されていたらよいのですが、まあ、ここにあります。
underscore_d

38

いいえ、組み込みの整数型を禁止するのはばかげています。ただし、これらも乱用されるべきではありません。

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

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

正確にNビットでなくても十分な幅の型だけが必要な場合は、(スペースに最適化)または(速度に最適化)の使用を検討してください。繰り返しますが、両方の家族にも対応しています。std::int_leastN_tstd::int_fastN_tunsigned

それでは、いつ組み込み型を使用するのでしょうか?まあ、標準では幅が正確に指定されていないため、実際のビット幅ではなく他の特性に関心がある場合に使用してください

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

int通常、機械が処理できる最速の型になります。単一の命令でロードおよび保存できるように(ビットをマスクまたはシフトすることなく)十分に広く、(最も)効率的なハードウェア命令で操作できるように十分に狭くなります。したがって、intデータを渡し、オーバーフローが問題にならない場合に算術を実行するには最適です。たとえば、デフォルトの基本的な列挙型はintです。可能だからといって、32ビット整数に変更しないでください。また、-1、0、1にしかなれない値がある場合、int巨大な配列を格納する場合を除き、最適な選択です。この場合、個々の要素にアクセスするために高い価格を支払う必要がありますが、よりコンパクトなデータ型を使用できます。より効率的なキャッシングは、おそらくこれらの利益をもたらします。多くのオペレーティングシステム機能もの観点から定義されていintます。引数と結果を相互に変換するのはばかげています。すべてこれはおそらく行う可能性がある紹介オーバーフローエラーを。

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

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


4
OPは、特にlongWindowsとUnixのサイズの違いを指摘しました。私は誤解しているかもしれませんがlong、「問題」ではなく「機能」であるというサイズの違いについての説明は、この特定の比較ではなく、32ビットと64ビットのデータモデルの比較に意味があります。この質問で尋ねられた特定の場合、これは本当に機能ですか?または、それは他の状況(つまり、一般的に)の機能であり、この場合は無害ですか?
ダン・ゲッツ

3
@ 5gon12eder:問題は、コードの動作が「int」のサイズに依存しないようにするためにuint32_tのような型が作成されたが、その意味が「uint32_tが32 「ビットシステム」は、動作が「int」のサイズに正しく依存しないコードの作成を、ほぼ正しいコードの作成よりもはるかに困難にします。
supercat

3
ええ、私は知っています...それが呪いの起源でした。オリジナルの作者は、コードを書いたときに32ビットOSが10年以上も離れていたため、リース抵抗の道を歩みました。
スティーブンバーナップ16

8
@ 5gon12eder残念ながら、supercatは正しいです。正確な幅のタイプの全ては、「ただのtypedef」され、整数昇格規則は上の算術ことを意味する、それらのいかなる通知を取らないuint32_t値として実施される署名されたintプラットフォーム上-width算術intある広い比べuint32_t。(今日のABIでは、これが圧倒的に問題になる可能性が高くなりますuint16_t。)
zwol

9
第一に、詳細な回答をありがとう。しかし:ああ。長い段落:「long通常、単一のマシン命令で処理できる最も幅の広いタイプです。...」-これはまったく間違っています。Windowsデータモデルを見てください。私見では、x64 Windowsでは32ビットのままであるため、次の例全体が壊れます。
マーティンBa

6

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

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

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

つまり、パラメータや変数をuint32_t(確かではないlong!)として宣言するのではなくchannel_id_typeroom_count_typeなどの名前を宣言します。

図書館について

long特に参照またはそれらへのポインタとして使用されている場合、使用するかどうかにかかわらず、サードパーティのライブラリは迷惑になることがあります。

最高のものは、ラッパーを作ることです。

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

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

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

キャストのような関数は衝突を文書化し、関数のパラメーターに一致する結果のコンパイル時チェックを提供し、実際の型が関連する実際のサイズに一致する場合にのみ警告またはエラーを削除します。つまり、(Windowsで)int*またはを渡すとオーバーロードおよび定義され、long*それ以外の場合はコンパイル時エラーが発生します。

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


なぜダウンコメント(コメントなし)?
JDługosz

このネットワークのほとんどのダウン票はコメントなしで表示されるため...-
ルスラン
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.