可変幅タイプは、最新のCの固定タイプに置き換えられましたか?


21

本日、Code Reviewのレビューで興味深い点に出会いました。@Veedracはにrecommened この回答可変サイズの種類(たとえばことintとがlong)のような固定サイズの種類に置き換えることuint64_tuint32_t。その答えのコメントからの引用:

intおよびlongのサイズ(およびそれらが保持できる値)はプラットフォームに依存します。一方、int32_tは常に32ビット長です。intを使用するということは、コードがプラットフォームごとに異なる動作をすることを意味しますが、これは一般的には望んでいないことです。

共通の型を修正しないという標準の背後にある理由は、@ supercatによって部分的に説明れています。Cは、当時システムプログラミングに通常使用されていたアセンブリとは対照的に、アーキテクチャ間で移植できるように作成されました。

設計意図はもともと、int以外の各タイプがさまざまなサイズの数を処理できる最小のものであり、そのintが+/- 32767を処理できる最も実用的な「汎用」サイズであると思いました。

私については、私はいつも使用intしており、代替案についてはあまり心配していません。私はいつも、これが最高のパフォーマンス、ストーリーの終わりを持つ最もタイプだと思っていました。固定幅が便利だと思った唯一の場所は、ストレージ用またはネットワーク経由の転送用にデータをエンコードする場合です。他の人が書いたコードで固定幅タイプを見ることはめったにありません。

私は70年代に行き詰まっintていますか、それともC99以降の時代に使用する理論的根拠はありますか?


1
一部の人は他の人を真似ています。私は、固定ビット型のコードの大部分が良心的でなくなったと信じています。サイズを設定しない理由もありません。私は主に16ビットプラットフォーム(MS-DOSおよび80年代のXenix)で作成されたコードを持っています。これは、現在64でコンパイルして実行し、新しいワードサイズとアドレッシングの利点をコンパイルするだけです。つまり、データをエクスポート/インポートするためのシリアル化は、移植性を維持するための非常に重要なアーキテクチャ設計です。
ルチアーノ

回答:


7

型がuint32_tプログラマーのサイズを心配することから救うといったタイプの一般的で危険な神話があるint。規格委員会がマシンに依存しないセマンティクスで整数を宣言する手段を定義すると役立つと思いuint32_tますが、署名のないタイプには、コードがクリーンで移植性のある方法で記述できないほど緩いセマンティクスがあります。さらに、int32多くのアプリケーションの方法で不必要に厳密に定義されたセマンティクスを持つような署名付きタイプは、そうでなければ有用な最適化を排除します。

例えば、考慮してください:

uint32_t upow(uint32_t n, uint32_t exponent)
{
  while(exponent--)
    n*=n;
  return n;
}

int32_t spow(int32_t n, uint32_t exponent)
{
  while(exponent--)
    n*=n;
  return n;
}

int4294967295を保持できないマシン、または18446744065119617025を保持できるマシンではn、とのすべての値に対して最初の関数が定義されexponent、その動作はのサイズの影響を受けませんint。さらに、標準では、それは、任意のサイズでマシン上で異なる動作をもたらすことを必要としないであろうint いくつかの値nexponent、しかし、それは4294967295はとして表現されるマシン上で未定義の動作を起動するようになりますintが、18446744065119617025ではありません。

第二の機能は、いくつかの値に対して、未定義の動作が得られますnし、exponentマシン上でint4611686014132420609を保持することはできませんが、すべての値に対して定義された動作が得られますnし、exponentそれは(のための仕様ができ、すべてのマシン上でint32_tそれのマシン上の2の補数ラッピング行動を暗示しますより小さいint)。

歴史的に、規格はコンパイラがintオーバーフローで何をすべきかについて何も述べていませんでしたがupow、コンパイラは、あたかもintオーバーフローしないほど十分に大きいかのように一貫して同じ動作をもたらしました。残念ながら、一部の新しいコンパイラは、標準で義務付けられていない動作を排除することにより、プログラムを「最適化」しようとする場合があります。


3
手動で実装したい人は誰でもpow、このコードは単なる例であり、対応していないことを忘れないでexponent=0ください!
マークハード

1
後置ではなく接頭辞の減分演算子を使用する必要があると思います。現在は1回の乗算を行っています。たとえばexponent=1、増分がチェックの前に実行されると、チェック後に減分が実行されるため、nが1回乗算されます(すなわち--exponent)、乗算は実行されず、n自体が返されます。
ALXGTV

2
@MarkHurd:実際に計算するのはであるため、関数の名前はあまりありませんN^(2^exponent)が、フォームのN^(2^exponent)計算は指数関数の計算でよく使用され、mod-4294967296指数は、ハッシュが知られています。
supercat

1
@ALXGTV:この関数は、電力関連の何かを計算する何かを説明するためのものでした。実際に計算するのはN ^(2 ^ exponent)です。これはN ^ exponentの効率的な計算の一部であり、Nが小さい場合でも失敗する可能性があります(uint32_t31の乗算を繰り返してもUBは得られませんが、 31 ^ Nを計算する方法は31 ^(2 ^ N)の計算を必要とします。これは
supercat

これは良い議論だとは思わない。目的は、すべての入力に対して機能を定義することではありません。サイズとオーバーフローについて推論できるようにするためです。int32_tオーバーフローを定義することもあれば、定義しないこともありますが、それは最初にオーバーフローを防止する理由を与えてくれるという事実に比べて、重要性が最小限に思えます。また、定義済みのオーバーフローが必要な場合は、何らかの固定値を法とする結果が必要になる可能性があります。したがって、とにかく固定幅型を使用しています。
-Veedrac

4

バッファーサイズ、配列インデックス、Windows 'などのポインター(したがって、アドレス可能なメモリの量)に密接に関連する値の場合lParam、アーキテクチャに依存するサイズの整数型を持つことは理にかなっています。したがって、可変サイズの型は依然として有用です。これは、私たちがのtypedefを持っている理由であるsize_tptrdiff_tintptr_t、など彼らが持っている組み込みのC型の整数のいずれもポインタのサイズする必要はないので、typedefであることを。

質問があるので、本当にいるかどうかcharshortintlong、とlong longまだ便利です。

IME、CやC ++プログラムがintほとんどのものに使用することは今でも一般的です。ほとんどの場合(つまり、数値が±32 767の範囲にあり、厳しいパフォーマンス要件がない場合)、これは問題なく機能します。

しかし、17〜32ビットの範囲の数値(大都市の人口など)を処理する必要がある場合はどうでしょうか。を使用できますintが、プラットフォームの依存関係をハードコーディングすることになります。厳密に標準に準拠する場合はlong、少なくとも32ビットが保証されているを使用できます。

問題は、C標準が整数型の最大サイズを指定していないことです。long64ビットの実装があり、メモリ使用量が2倍になります。そして、これらlongが数百万のアイテムを持つ配列の要素である場合、あなたは狂ったようにメモリをスラッシングします。

したがって、プログラムをクロスプラットフォームとメモリ効率の両方にしたい場合は、ここで使用するのに適したタイプintlongはありません。を入力しint_least32_tます。

  • I16L32コンパイラは32ビットlongを提供し、以下の切り捨ての問題を回避します。int
  • I32L64コンパイラは32ビットintを提供し、64ビットのメモリの浪費を回避しますlong
  • I36L72コンパイラは36ビットを提供します int

OTOH、あなたが思うしていない膨大な数や巨大な配列を必要とするが、あなたはスピードを必要としています。またint、すべてのプラットフォームで十分な大きさかもしれませんが、必ずしも最速の型であるとは限りませんint。通常、64ビットシステムにはまだ32ビットがあります。しかし、あなたは使用することができint_fast16_t、それはだかどうかを、「最速」型を取得intlongまたはlong long

したがって、の型には実用的なユースケースがあります<stdint.h>。標準の整数型は何の意味もありません。特にlong、32ビットまたは64ビットである可能性があり、コンパイラー作成者の気まぐれに応じて、ポインターを保持するのに十分な大きさである場合とそうでない場合があります。


のような型に関する問題uint_least32_tは、他の型との相互作用がの相互作用よりもさらに弱く指定されていることですuint32_t。私見は、標準のようなタイプを定義すべきであるuwrap32_tunum32_t、セマンティクスとタイプを定義任意のコンパイラは、そのuwrap32_t場合、それが促進されるように、本質的に同じ場合にunsigned型として推進しなければならないint32ビットであった、とタイプを定義任意のコンパイラがunum32_tいることを確認する必要があります基本的な算術プロモーションでは、常に値を保持できる符号付き型に変換します。
supercat

さらに、標準はまた、その貯蔵およびエイリアシングと互換性があったタイプ定義できるintN_tuintN_t、そしてその定義された行動であろう一貫した有するintN_tとしuintN_tたが、これはコンパイラをその範囲外の値を割り当てる場合、コード内のいくつかの自由度を与えるであろう[されたものと同様のセマンティクスを可能にしますおそらくを対象としていますがuint_least32_t、a uint_least16_tとan int32_tを追加することで符号付きまたはusnignedの結果が得られるかどうかなどの不確実性はありません。
-supercat
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.