62

C99(または単にstdint.h)をサポートするコンパイラを使用していると仮定すると、uint8_tなどの固定幅の整数型を使用しない理由はありますか?

私が知っている理由の1つは、この質問で述べたように、scharを使用する代わりに、文字を処理するときにsを使用する方がはるかに理にかなっていることです。(u)int8_t

しかし、数値を格納することを計画している場合、その大きさがわからない型をいつ使用したいですか?unsigned shortつまり、どのような状況で、?を使用する代わりに、8、16、または32ビットであるかどうかを知らずに数値を格納したいuint16tですか?

これに続いて、固定幅の整数を使用するか、通常の整数型を使用し、何も想定せず、使用sizeofしているバイト数を知る必要がある場所で使用する方がよいと考えられますか?

4

6 に答える 6

46

タイプの正確なサイズを知らなくても、数値を格納することは実際には非常に一般的です。私のプログラムには、20億を超えないと合理的に想定できる、または超えないように強制できる量がたくさんあります。しかし、それは私がそれらを格納するために正確な32ビットタイプが必要であることを意味しません。少なくとも20億に数えることができるどんなタイプでも私は問題ありません。

非常に移植性の高いコードを作成しようとしている場合は、固定幅タイプはすべてオプションであることに注意する必要があります。

がないCHAR_BITよりも大きいC99実装の場合。標準では、パディングビットが必要になるため、存在することが禁止されており、タイプにはパディングビットがないように定義されています(7.18.1.1 / 1)。したがって、(ありがとう、ouah)実装が。なしで定義することは許可されていないため、これも禁止されています。8int8_tintN_tuint8_tuint8_tint8_t

したがって、非常に移植性の高いコードでは、127までの値を保持できる符号付き型が必要な場合は、、、、またはコンパイラにそれを作成するように依頼するかどうかに応じて、のいずれかを使用する必要がありsigned charますintint_least8_tint_fast8_t

  • C89で動作する(signed charまたはint
  • 算術式での驚くべき整数昇格を回避する(int
  • 小さい(int_least8_tまたはsigned char
  • 高速(int_fast8_tまたはint

unsigned char同じことが、、、、およびを含む255までの符号なしタイプにも当てはまりunsigned intます。uint_least8_tuint_fast8_t

非常に移植性の高いコードでモジュロ256演算が必要な場合は、モジュラスを自分で取得するか、ビットをマスクするか、ビットフィールドでゲームをプレイすることができます。

実際には、ほとんどの人は、移植性のあるコードを書く必要はありません。現時点CHAR_BIT > 8では、専用ハードウェアでのみ発生し、汎用コードは使用されません。もちろん、それは将来変更される可能性がありますが、PosixやWindows(どちらも保証CHAR_BIT == 8)について仮定するコードが非常に多いと思われる場合は、コードの非移植性に対処することは、その新しいプラットフォームにコードを移植するための多大な努力。そのような実装はおそらく、コードを起動して実行する方法を心配するずっと前に、インターネット(オクテットを扱う)に接続する方法について心配する必要があります:-)

とにかくそれを想定しているのであれば、C89でコードを機能させたい場合を除いCHAR_BIT == 8て、避けるべき特別な理由はないと思います。(u)int8_tC89でもstdint.h、特定の実装のバージョンを見つけたり作成したりすることはそれほど難しくありません。ただし、型が保持できないことを要求するのではなく、型が保持できることだけを要求するようにコードを簡単に記述できる場合は、への依存を回避することをお勧めします。255256CHAR_BIT == 8

于 2012-11-16T09:38:08.683 に答える
17

まだ言及されていない問題の1つは、固定サイズの整数型を使用すると、コンパイラが、などに異なるサイズを使用しても変数のサイズが変更されないことを意味しますがintlong必ずしも保証されないということです。そのコードは、サイズが定義されている場合でも、さまざまな整数サイズのマシンで同じように動作します。

たとえば、宣言が与えられた場合、がゼロのときuint32_t i;の式の動作は、aが。よりも小さいかどうかによって異なります。たとえばが64ビット(およびのようなもの)であるシステムでは、変数は;にプロモートされます。減算と比較は符号付きで実行されます(-1は5未満です)。が32ビットのシステムでは、減算と比較は次のように実行されます(減算により、5より大きい非常に大きな数が生成されます)。(i-1) > 5iuint32_tintintuint32_tlong shortiintintunsigned int

型キャストがない場合でも、符号なし型を含む式の中間結果をラップする必要があるという事実にどの程度のコードが依存しているかはわかりません(IMHO、ラップ動作が必要な場合は、プログラマーはタイプキャストを含める必要があります) (uint32_t)(i-1) > 5)が、現在、標準では余裕がありません。型キャストや型の強制がない場合に、コンパイラがオペランドをより長い整数型にプロモートすることを少なくとも許可uint32_t i,jするルールがあれば、どのような問題が発生するのでしょうかj = (i+=1) >> 1;j = (uint32_t)(i+1) >> 1;、 しかしj = (i+1)>>1しません]?または、そのことについては、中間結果がすべて最大の符号付き型に収まり、一定でない量による右シフトを含まない積分型式が同じ結果になることをコンパイラーメーカーが保証するのはどれほど難しいでしょうすべての計算がそのタイプで実行されたかのように結果?int32ビットのマシンでは次のようになります。

  uint64_t a、b、c;
  ..。
  &=〜0x40000000;
  b&=〜0x80000000;
  c&=〜0x100000000;

aとのそれぞれ1ビットをクリアしますが、 ;cの上位33ビットをクリアします。bほとんどのコンパイラは、2番目の式について何かが「異なる」ことを示唆することはありません。

于 2012-11-16T20:49:29.663 に答える
7

標準の整数型の幅はプラットフォームごとに変わる可能性がありますが、最小は変わらないことは事実です。

たとえば、C標準では、aはint少なくとも16-bit、alongは少なくとも32-bit幅が広いと指定されています。

オブジェクトを保存するときにサイズの制約がない場合は、これを実装に任せることができます。たとえば、最大符号付きの値がに収まる場合は、16-bitを使用できますint。次に、実装が対象としているアーキテクチャの自然なint幅を実装に最終的に伝えます。

于 2012-11-16T09:38:10.460 に答える
4

コードは、何が重要かをカジュアルな読者(およびプログラマー自身)に明らかにする必要があります。それは単なる整数なのか、符号なし整数なのか、それとも符号付き整数のか。サイズについても同じことが言えます。一部の変数がデフォルトで16ビットであることがアルゴリズムにとって本当に重要ですか?それとも、それは単に不必要なマイクロマネジメントと最適化の試みの失敗ですか?

これがプログラミングを芸術にするものです-何が重要かを示すためです。

于 2012-11-16T09:48:34.397 に答える
4

幅について推測する場合にのみ、固定幅タイプを使用する必要があります。

uint8_tunsigned charほとんどのプラットフォームで同じですが、すべてではありません。を使用uint8_tすると、8ビットのアーキテクチャを想定していて、他のアーキテクチャではコンパイルされないという事実が強調されるcharため、これは機能です。

typedefそれ以外の場合は、、などsize_tの「セマンティック」を使用します。これはuintptr_tptrdiff_tデータに関して考えていることをはるかによく反映しているためです。基本型を直接使用することはほとんどなくint、エラーが返されるだけで、を使用したことを覚えていませんshort

編集: C11を注意深く読んだ後、存在する場合は、その型が符号なしであってもuint8_t、存在する必要がunsigned charあり、存在することはできないと結論付けます。charこれは、7.20.1 p1の要件に由来し、すべてが対応するintN_t符号付きおよび符号なしタイプuintN_tである必要があります。文字タイプのそのようなペアはとだけです。signed charunsigned char

于 2012-11-16T09:40:55.513 に答える
3

を使用する理由はたくさんあります。それらを次のようなまたは固定幅タイプのようなセマンティックタイプと呼びましょう。intcharuint8_t

既存のAPIとのマッチング

標準Cライブラリはchar*どこでも使用します。そのAPIと通信するときに別のタイプを使用して、ユーザーを混乱させる(そして起こりうるバグを導入する)のはなぜですか?

同様に、printf()フォーマット文字列はこれらのセマンティックタイプに関して定義されます。固定サイズのタイプを印刷する場合は、古いフォーマット文字列で固定サイズのタイプを印刷するための正しいフォーマット文字列を取得できるようにするために、PRIu64などのマクロが必要です。stdint.hprintf

スピード

セマンティックタイプは通常、現在のCPUのパフォーマンス特性に最適に機能するように選択されます。CPUのレジスタサイズであり、不要な変換などを節約できるため、選択したサイズよりもわずかに大きいサイズにバンプされる可能性があります。

最近、これは少し異議を唱えられた答えです...それは当初の意図でしたがstdint、初期のC / C ++では利用できなかったため、多くのプラットフォーム(32ビットWindowsやmacOS Xなど)はサイズintlong。したがって、64ビットの移動中、これらのサイズの一部は同じままでした(long long特に、のような楽しい新しいタイプにつながります)。そのため、leastfastタイプを取得しました。

コードの移植性

セマンティックタイプは、32ビットプラットフォームよりも64ビットプラットフォームの方が大きくなる場合があります(たとえば、配列インデックスがすべてのメモリを埋めることができるようにするため)。したがって、異なるプラットフォームで実行している場合size_t、固定タイプの代わりにセマンティックタイプ(私の定義では利用可能な場合はそれを含む)を使用することは、より優れたハードウェアを利用し、任意の制限を追加しないことを意味します。

もちろん、これはあなたのアルゴリズムだけを移植可能にします。データをバイトにシリアル化して異なるプラットフォーム間で交換する必要がある場合、コードは移植可能になりますが、ネットワークパケットや出力ファイルは移植できません。したがって、その場合は、実際には固定タイプに固執して、データの移植性を維持する必要がありますが、コードの実行が耐えられないほど遅くなるか、特定のプラットフォームでコンパイルされないという犠牲が伴います。

コメント:int64_tなぜ彼らがなどのフォーマット文字列を導入しなかったのか私に聞かないでくださいint32_t。多分彼らは文字が足りなかったのでしょうか?多分あまりにも多くのコードベースが独自のフォーマット文字列を定義し、壊れていただろうか?

于 2020-02-15T11:21:41.360 に答える