web-dev-qa-db-ja.com

固定幅整数型(uint8_tなど)を使用しない理由はありますか?

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

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

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

これに続いて、固定幅整数を使用するか、通常の整数型を使用し、何も仮定せず、sizeofを使用することをお勧めします。

52
DanielGibbs

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

移植性の高いコードを記述しようとしている場合は、固定幅型はすべてオプションであることを覚えておく必要があります。

CHAR_BIT8より大きいC99実装では、int8_tはありません。規格では、パディングビットが必要になるため、その存在を禁止し、intN_tタイプは、パディングビットがないように定義されています(7.18.1.1/1)。 uint8_tは、実装がuint8_tなしでint8_tを定義することを許可されていないため、禁止されています。

したがって、非常に移植可能なコードでは、127までの値を保持できる署名された型が必要な場合は、signed charintint_least8_tまたはint_fast8_tのいずれかを使用する必要がありますコンパイラーに依頼するかどうかに応じて、次のようにします。

  • c89で動作します(signed charまたはint
  • 算術式での意外な整数の昇格を避ける(int
  • 小さい(int_least8_tまたはsigned char
  • 高速(int_fast8_tまたはint

255までの符号なしの型の場合も同様で、unsigned charunsigned intuint_least8_tuint_fast8_tが含まれます。

非常に移植可能なコードで256を法とする算術演算が必要な場合は、自分で係数を取得するか、ビットをマスクするか、ビットフィールドでゲームをプレイできます。

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

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

34
Steve Jessop

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

たとえば、宣言uint32_t i;が指定されている場合、iがゼロのときの(i-1) > 5の動作は、uint32_tintより小さいかどうかによって異なります。たとえば、 intは64ビット(およびuint32_tlong shortのようなものです)、変数iintに昇格されます。減算と比較は符号付きで実行されます(-1は5未満です)。 intが32ビットであるシステムでは、減算と比較はunsigned intとして実行されます(減算により、5より大きい実際に大きな数が生成されます)。

型キャストがない場合でも、符号なしの型を含む式の中間結果をラップする必要があるという事実に依存するコードの量はわかりません(IMHO、ラップ動作が必要な場合、プログラマーは型キャストを含める必要がありました)(uint32_t)(i-1) > 5)。現在、この規格では余裕がありません。型キャストまたは型強制がない場合に、少なくとも許可コンパイラがオペランドをより長い整数型に昇格させるルールがある場合、どのような問題が発生するのでしょうか。 uint32_t i,jを指定すると、j = (i+=1) >> 1;がそうであるように、j = (uint32_t)(i+1) >> 1;のような割り当てがオーバーフローを取り除くために必要になりますが、j = (i+1)>>1はそうしません]?または、その問題について、コンパイラーの製造元が、中間結果がすべて最大の符号付き型に収まり、非定数の量による右シフトを伴わない整数型の式でも同じ結果が得られることを保証するのはどれほど難しいでしょうか。すべての計算がそのタイプで実行されたかのように結果は? intが32ビットであるマシンでは、次のように不快に思われます。

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

acのそれぞれ1ビットをクリアしますが、bの上位33ビットをクリアします。ほとんどのコンパイラーは、2番目の式について何かが「異なる」というヒントを与えません。

12
supercat

標準の整数型の幅はプラットフォームごとに変わる可能性がありますが、その最小値widthではありません。

たとえば、C標準では、intは少なくとも16-bitであり、longは少なくとも32-bit幅であると規定されています。

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

7
ouah

固定幅タイプは、幅について想定する場合にのみ使用してください。

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

それ以外の場合は、size_tuintptr_tptrdiff_tなどの "セマンティック" typedefを使用します。これは、データについて考えていることをはるかによく反映しているためです。基本タイプを直接使用することはほとんどありません。intはエラーの返却にのみ使用し、shortを使用したことを覚えていません。

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

4
Jens Gustedt

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

これがアートをプログラミングする理由です。何が重要かを示すためです。

3
Aki Suihkonen

を使用したい理由はたくさんあります。intcharなどのsemantic型と呼びましょう_uint8_t_のような固定幅型:

既存のAPIと一致

標準C libは、どこでも_char*_を使用します。そのAPIと通信するときに別のタイプを使用してユーザーを混乱させる(そしてバグの可能性を招く)のはなぜですか?

同様に、printf()形式の文字列は、これらのセマンティックタイプに関して定義されます。固定サイズタイプを印刷する場合は、_PRIu64_内の_stdint.h_などのマクロを使用して、古いprintfフォーマット文字列。

速度

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

現在、これは少し論争の的となっている回答です...それは元々の意図でしたが、初期のC/C++ではstdintが利用できないため、多くのプラットフォーム(32ビットWindowsまたはmacOS Xなど) intlongのサイズを保証しました。したがって、64ビットの移動中、これらのサイズの一部は同じままでした(特に、_long long_などの楽しい新しい型につながります)。そのため、leastおよびfastタイプを取得しました。

コードの移植性

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

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

コメント: _int64_t_、_int32_t_などのフォーマット文字列を導入しなかった理由を聞かないでください。おそらく文字が不足していましたか?たぶん、あまりにも多くのコードベースが独自のフォーマット文字列を定義し、壊れていたのでしょうか?

1
uliwitness