web-dev-qa-db-ja.com

CおよびC ++のプラットフォーム固有の整数型(short、int、long)が非推奨にならないのはなぜですか?

TL; DR:「本当に必要な場合を除いて、shortint、およびlongを使用しないでください。する必要がある!」


理論的には、タイプshortint、およびlongを使用することにより、コンパイラーに、特定のプロセッサーにとって最も効率的な長さを選択させることを理解しています。

しかし、これは時期尚早の最適化の事例がすべての悪の根源なのでしょうか?

常に1〜1000の数値を保持することがわかっている整変数があるとします。私の理解では、2〜4バイトのメモリの違いについて心配していなければ、short/int/longを指定すると、コンパイラーはプロセッサーの効率に応じて16ビットまたは32ビットを選択できるため、その変数をintにすることができます。もし私がそれをuint16_t、コンパイラは非常に高速なコードを作成できない場合があります。

しかし、現代のハードウェアではそれは本当ですか?むしろ、(もしあれば)私を得る速度は、不正確な型を使用するとプログラムに重大なバグが発生する可能性が非常に高い可能性がありますか?たとえば、プログラム全体でintを使用し、32ビット値を表すと考えます。これは、過去20年間使用してきたすべてのプラットフォームでそれが表されているためですが、コードは次のようにコンパイルされます。 intが2バイトであり、あらゆる種類のバグが発生する珍しいプラットフォーム。

そして、バグは別として、プログラマーがデータについて話すのが面倒で不正確な方法のように思えます。例として、2019年にGUID構造に対して Microsoftが与える の定義を次に示します。

typedef struct _GUID {
  unsigned long  Data1;
  unsigned short Data2;
  unsigned short Data3;
  unsigned char  Data4[8];
} GUID;

Uuidが何であるかにより、その長いは32ビットを意味するであり、それらの短いは16ビットを意味し、その文字は8ビットを意味します。では、なぜ「短い」、「長い」、そして(天国が私たちを助ける)「長い長い」というこの不正確な言葉で話し続けるのですか?

11
M Katz

理論的には、short、int、およびlong型を使用することにより、コンパイラーに、特定のプロセッサーにとって最も効率的な長さを選択させることを理解しています。

それは部分的にのみ本当です。これらすべての型には、ANSI Cでは 最小サイズの保証 があります(ANSI C89でもAFAIK)。これらの最小サイズのみに依存するコードは、移植可能です。タイプの最大サイズが移植性に影響するケースは、それほど頻繁ではありません。とは言っても、intが最低32ビットであると想定され、最低でも32ビット以上のCPUが搭載された環境向けに明確に記述されたコードが何年にもわたって(そして記述されて)います。

しかし、これは時期尚早の最適化のケースでしょうか[...]?

時期尚早の最適化とは、速度を最適化することだけではありません。これは、コードに余分な労力を投資し、コードをより複雑にすることです。 、(しばしば病理学的)「念のため」の理由で。 「遅くなる場合に備えて」は、これらの潜在的な理由の1つにすぎません。したがって、「念のため」にintを使用しないようにすることは、将来的に16ビットプラットフォームに移植される可能性があり、この種の移植が行われる可能性が低い時期尚早の最適化の形式と見なすこともできます。

つまり、あなたがintについて書いた部分はある程度正しいと思います。32ビットから16ビットのプラットフォームにプログラムが移植される可能性があるという証拠がある場合は、それに依存しないことが最善です。 intは32ビットで、longを使用するには int32_tint_least32_tのような特定のC99データ型 が不明な場合16ビットで十分かどうか。グローバルtypedefを使用して、C99に準拠していないプラットフォームでint32_tを定義することもできます。これらはすべて、少し余分な労力です(少なくとも、プロジェクトで使用された特殊なデータ型とその理由をチームに教えるには)。

この古いSO記事 も参照してください。一番上の答えは、ほとんどの人はその程度の移植性を必要としないというものです。

GUID=構造についての例:示されているデータ構造はほとんど問題ないようですが、各ANSI準拠プラットフォームの各パーツに対して十分な大きさが保証されているデータ型を使用しています。したがって、誰かがこの構造を使用して移植可能なコードを記述しようとしても、それは完全に可能です。

ご自分が気付いたように、誰かがこの構造体をGUIDの仕様として使用しようとすると、ある程度不正確であり、明確な仕様を取得するにはドキュメントを完全に読む必要があるという事実について不平を言う可能性があります。これは、タイプの最大サイズが重要になる可能性のある、それほど頻繁ではないケースの1つです。

各フィールドの個々の最大サイズ、または合計サイズが正確に128ビットであると仮定しながら、そのようなstructのコンテンツが文字列形式、バイナリシリアル化、どこかに格納または送信されると、他の問題が発生する可能性があります。エンディアン、またはそれらのデータ型の正確なバイナリエンコーディング。ただし、GUID構造体のドキュメントでは、基礎となるバイナリ表現について何も約束されていないため、移植可能なコードを記述しようとする場合、それについていかなる仮定もすべきではありません。

7
Doc Brown

それらを非推奨にする理由がないので、非推奨ではありません。

正直に言って、本当に言う必要があることはそれほど多くないので、私はそれをそのままにしようとする気がします。それらを非推奨にしても、まったく何も成し遂げられないでしょう。そのような論文を書くのに苦労している人は誰でも(おそらく、エイプリルフールのジョークとして、またはその順序で何かを除いて)。

しかし、intの典型的な使用法を考えてみましょう:

for (int i=0; i<10; i++)
    std::cout << "something or other\n";

さて、誰かがiint_fast8_tint_fast16_t、または類似のものに変更することで何かを得ますか?私はその答えは響き渡る「いいえ」だと思います。基本的に何も得られません。

さて、int8_tint16_tint32_t(またはそれらの符号なしバリアント)などの明示的なサイズの型を使用することが理にかなっている状況があることは確かに事実です。

しかし、CおよびC++の意図の一部はシステムプログラミングをサポートすることであり、そのために、ターゲットマシンのレジスターの正確なサイズを反映するタイプが必要な場合があります。これがCとC++の両方の明示的な意図であることを考えると、それをサポートする型を非推奨にすることはまったく意味がありません。

実際のところ、非常に単純です:はい、特定のビット数の型が必要な場合があります-そして、必要な場合は、CおよびC++が、指定したサイズと正確に一致することが保証されている型を提供します。ただし、使用している範囲に十分な大きさである限り、サイズをあまり気にしない場合もあります。CとC++は、そのニーズを満たす型も提供します。

そこから、プログラマーがあなたが本当に望んでいることを知り、適切に行動するかどうかはあなた次第です。はい、あなたは誰かが(少なくとも間違いなく)悪い選択をしたケースを指摘しました。しかし、それは常にそれが常に悪い選択である、または必ずしもほとんどの場合必ずしも悪い選択であるという意味ではありません。

もう1つ覚えておかなければならないのは、移植性が重要な場合もありますが、重要ではない場所も多く、まったく重要でない場所もあります。ただし、少なくとも私の経験では、整数型のサイズが移植性の重要な要素になることはめったにありません。一方で、現在のコードの多くを見ると、標準で指定されている16ビットではなく、実際にintが少なくとも32ビットであることに依存するかなりのビットが存在することは間違いありません。しかし、そのコードの大部分を(たとえば)16ビットのintsを使用するMS-DOSのコンパイラに移植しようとすると、次のような大きな問題にすぐに遭遇します。そのintを使用して約1000万の配列にインデックスを付けますdoubles--そして、コードを移植する際の実際の問題は、そのintを使用すると、8000万を格納するよりもはるかに少なくなります640Kのみをサポートするシステムのバイト。

8
Jerry Coffin

今日廃止されたことは、明日なくなることを意味します。

これらの型をCおよびC++から削除するコストは信じられないほど高くなります。不要な作業を引き起こすだけでなく、至る所でバグを引き起こす可能性があります。

1
gnasher729

MicrosoftのGUIDに関するドキュメントは、 MicrosoftのC++コンパイラ これらの値のプラットフォーム固有の定義と併せて読む必要があります。 ANSI C/C++標準の定義したがって、ある意味では、これらのGUID=フィールドのサイズは、Microsoftのコンパイラで適切に定義されています。

GUIDヘッダーは、Microsoft以外のプラットフォームではもちろんバグがありますが、ここでのエラーは、Microsoftが標準およびその他の実装について気の利いたものだと考えていることです。

0
Lie Ryan

コンパイルされたCコードは(通常)ネイティブに実行され、ネイティブのWordサイズはさまざまです(70年代初頭にCが最初に開発されたときは、特に変動しました)。 16ビットマシン、ワードサイズが2の累乗ではないマシン(9ビットバイト、36ビットワード)、パディングビットを使用するマシンなどでコードが実行されています。

各タイプは、それが値の最小範囲を表すことができることを保証します。 int少なくとも範囲[-32767..32767]の値を表すことが保証されており、これは少なくとも 16ビット幅であることを意味します。最近のデスクトップおよびサーバーシステムでは、32ビット幅になる傾向がありますが、これは保証されていません。

いいえ、charshortintlongなどのビット幅は固定されていません。これはgood Cの観点から見たもの。これにより、Cをさまざまなハードウェアに移植できるようになりました。

0
John Bode

話しているようなものです。

自分に話しかけると、どんな言語や音などを使ってもかまいません。おそらく自分を理解するでしょう。

他の人と話す場合、両当事者が明確に理解するためにmustに従う特定のルールがあります。languageが重要です。language事項に関するgrammarルール。意味特定のフレーズや単語が重要です。languageと書いた場合、spellingが重要で、layoutページが重要です。

規則や基準を自由に遵守することは自由ですが、他の当事者が理解する可能性は低く、あいまいな表現を侮辱したり使用したりすることで損害を与えることさえあります。コミュニケーションの失敗により、戦争が繰り広げられた。

ソフトウェアには類似のルールと標準があります。

ソフトウェアが他のシステムと情報を交換する必要がない場合、はい、処理中のデータが定義または使用するコンテナーに収まる限り、ほとんどの場合、short/longの使用は不要です-オーバーフローは依然として可能です。

一方、ソフトウェアが別のシステムと情報を交換する場合、そのソフトウェアはmust情報がどのように構造化されているかに注意してください。

例えば:

Networking-パケットは絶対にmust正しいバイト順である-少し-エンディアンvsビッグエンディアン-パケット内のフィールドmustは正しいビット数である必要があります。 JSONのような「明白な」データを送信していると思われる場合でも、そのデータは、JSONストリームの合計データよりもはるかに短いネットワークパケットに変換する必要があり、パケットには、シーケンス用のパケットタイプのフィールドもあります-したがって、受信側でデータを再構成できます-エラーの検出と修正のために、多くのmuchmuchもっと。考えられるすべてのネットワークパケットmustは、送信側または受信側のどちらにも曖昧さがないように定義されています。これを可能にするには、must既存のシステムおよびそれらのパケットを使用するシステムで機能するパケットフィールドの正確なサイズを指定できる未来。

デバイス制御-よく考えるとnetworkingによく似ています-パケットの「フィールド」はおおよそデバイスレジスタ、ビット、メモリなどに対応し、特定のデバイスの制御はおおよそ特定のNICまたはネットワークIPアドレスの使用に対応します。送信します'特定の場所にビットを書き込むことで「パケット」、特定の場所からビットを読み取ることで「パケット」を受信します。デバイスの作成者でない場合(通常)、あなたmust作成者がデバイスに記述した「プロトコル」に従いますdatasheetfields(レジスタ)は正しいサイズで、ビットは正しい位置にある必要があります。レジスタmustシステムのアドレスまたはI/Oスペースに正しく配置されています。デバイスの作成者は、デバイスとデータを交換するための「プロトコル」を通知します。システムデザイナは、「プロトコル」(アドレススペースとマッピング)を通知します。 AC用デバイスを停止します。

作成したソフトウェアで自由に何をしてもかまいませんが、ネットワークの受信者や特定のデバイスなど、相手が自分のやっていることを理解していない可能性が高く、場合によってはシステムに損傷を与えることさえあります。

Ping-of-Death は、意図的なパケット形式違反によりネットワークレシーバーがクラッシュし、ネットワークパケットが正しく形成されていると推定されるネットワークの例です。

Fork-Bomb は、システムフォーク「プロトコル」を意図的に乱用すると、再起動するまでシステムをハングさせる可能性があるシステムの例です。

Buffer-Overrun は、誰か(プログラマーであるあなた自身でも)がデータを保持できないコンテナに入れすぎると、「すべてがうまくいく」という仮定が失敗するプログラムの例です。

0