C99(または単にstdint.h)をサポートするコンパイラーを使用していると仮定すると、uint8_tなどの固定幅整数型を使用しない理由はありますか?
この質問 で述べたように、(u)int8_t
sを使用するのではなく、文字を処理するときにchar
sを使用する方がはるかに理にかなっていることを私が知っている1つの理由です。
しかし、数値を格納することを計画している場合、それがどのくらい大きいかわからないタイプをいつ使用したいですか?つまりunsigned short
を使用する代わりに、それが8、16、または32ビットであるかどうかを知らずにuint16t
に数値を格納する状況を教えてください。
これに続いて、固定幅整数を使用するか、通常の整数型を使用し、何も仮定せず、sizeof
を使用することをお勧めします。
タイプの正確なサイズを知らなくても、数値を格納することは実際にはかなり一般的です。私のプログラムには、20億を超えない、または超えないことを強制できると合理的に想定できる量がたくさんあります。しかし、それは私がそれらを保存するために正確な32ビットの型を必要とすることを意味しません、少なくとも20億まで数えることができるどんな型でも私には問題ありません。
移植性の高いコードを記述しようとしている場合は、固定幅型はすべてオプションであることを覚えておく必要があります。
CHAR_BIT
が8
より大きいC99実装では、int8_t
はありません。規格では、パディングビットが必要になるため、その存在を禁止し、intN_t
タイプは、パディングビットがないように定義されています(7.18.1.1/1)。 uint8_t
は、実装がuint8_t
なしでint8_t
を定義することを許可されていないため、禁止されています。
したがって、非常に移植可能なコードでは、127までの値を保持できる署名された型が必要な場合は、signed char
、int
、int_least8_t
またはint_fast8_t
のいずれかを使用する必要がありますコンパイラーに依頼するかどうかに応じて、次のようにします。
signed char
またはint
)int
)int_least8_t
またはsigned char
)int_fast8_t
またはint
)255までの符号なしの型の場合も同様で、unsigned char
、unsigned int
、uint_least8_t
、uint_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
への依存を回避することもできます。
まだ言及されていない問題の1つは、固定サイズの整数型を使用すると、コンパイラがint
、long
などに異なるサイズを使用しても、変数のサイズは変更されないということですが、さまざまな整数サイズのマシンでコードが同じように動作することを必ず保証しますサイズが定義されている場合でも。
たとえば、宣言uint32_t i;
が指定されている場合、i
がゼロのときの(i-1) > 5
の動作は、uint32_t
がint
より小さいかどうかによって異なります。たとえば、 int
は64ビット(およびuint32_t
はlong short
のようなものです)、変数i
はint
に昇格されます。減算と比較は符号付きで実行されます(-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;
a
とc
のそれぞれ1ビットをクリアしますが、b
の上位33ビットをクリアします。ほとんどのコンパイラーは、2番目の式について何かが「異なる」というヒントを与えません。
標準の整数型の幅はプラットフォームごとに変わる可能性がありますが、その最小値widthではありません。
たとえば、C標準では、int
は少なくとも16-bit
であり、long
は少なくとも32-bit
幅であると規定されています。
オブジェクトを保存するときにサイズの制約がない場合は、これを実装に任せることができます。たとえば、符号付きの最大値が16-bit
に収まる場合は、int
を使用できます。次に、実装が対象とするアーキテクチャの自然なint
幅であるという最終的なWordを実装に持たせます。
固定幅タイプは、幅について想定する場合にのみ使用してください。
uint8_t
とunsigned char
は、ほとんどのプラットフォームで同じですが、すべてではありません。 uint8_t
を使用すると、8ビットのchar
のアーキテクチャを想定し、他のアーキテクチャではコンパイルしないと想定しているため、これが機能です。
それ以外の場合は、size_t
、uintptr_t
、ptrdiff_t
などの "セマンティック" typedef
を使用します。これは、データについて考えていることをはるかによく反映しているためです。基本タイプを直接使用することはほとんどありません。int
はエラーの返却にのみ使用し、short
を使用したことを覚えていません。
編集: C11を注意深く読んだ後、uint8_t
が存在する場合はunsigned char
でなければならず、そのタイプであってもchar
にすることはできないと結論付けます。は署名されていません。これは、すべてのintN_t
およびuintN_t
が対応する符号付きおよび符号なしの型でなければならないという7.20.1 p1の要件に由来します。文字タイプのそのようなペアはsigned char
とunsigned char
のみです。
コードは、何が重要かをカジュアルな読者(およびプログラマー自身)に明らかにする必要があります。それは単なる整数か符号なし整数、あるいは符号付き整数ですらありますか。サイズも同じです。一部の変数がデフォルトで16ビットであることは、アルゴリズムにとって本当に重要ですか?それとも、不必要なマイクロ管理と最適化の失敗した試みだけですか?
これがアートをプログラミングする理由です。何が重要かを示すためです。
を使用したい理由はたくさんあります。int
やchar
などのsemantic型と呼びましょう_uint8_t
_のような固定幅型:
標準C libは、どこでも_char*
_を使用します。そのAPIと通信するときに別のタイプを使用してユーザーを混乱させる(そしてバグの可能性を招く)のはなぜですか?
同様に、printf()
形式の文字列は、これらのセマンティックタイプに関して定義されます。固定サイズタイプを印刷する場合は、_PRIu64
_内の_stdint.h
_などのマクロを使用して、古いprintf
フォーマット文字列。
通常、セマンティックタイプは、現在のCPUのパフォーマンス特性に最適に機能するように選択されます。それらはCPUのレジスタサイズであり、不要な変換などを節約するため、選択したサイズよりもわずかに大きいサイズにぶつかることがあります。
現在、これは少し論争の的となっている回答です...それは元々の意図でしたが、初期のC/C++ではstdint
が利用できないため、多くのプラットフォーム(32ビットWindowsまたはmacOS Xなど) int
とlong
のサイズを保証しました。したがって、64ビットの移動中、これらのサイズの一部は同じままでした(特に、_long long
_などの楽しい新しい型につながります)。そのため、least
およびfast
タイプを取得しました。
セマンティックタイプは、64ビットプラットフォームの方が32ビットプラットフォームよりも大きくなる場合があります(たとえば、配列インデックスがすべてのメモリを満たすことを許可するため)。したがって、異なるプラットフォームで実行している場合、固定型の代わりにセマンティックタイプ(私の定義では_size_t
_が含まれます)を使用すると、より優れたハードウェアを利用し、任意の制限を追加しないことになります。
もちろん、これはあなたのアルゴリズムだけを移植可能にします。データをバイトにシリアル化して異なるプラットフォーム間で交換する必要がある場合、これによりコードが移植可能になることがありますが、ネットワークパケットや出力ファイルはそうではありません。そのため、その場合は、コードを実行するのが耐えられないほど遅くなるか、特定のプラットフォームでコンパイルされないという犠牲を払って、データを移植可能に保つために、実際には固定タイプに固執する必要があります。
コメント: _int64_t
_、_int32_t
_などのフォーマット文字列を導入しなかった理由を聞かないでください。おそらく文字が不足していましたか?たぶん、あまりにも多くのコードベースが独自のフォーマット文字列を定義し、壊れていたのでしょうか?