web-dev-qa-db-ja.com

最近のCでは、可変幅型は固定型に置き換えられましたか?

Code Review のレビューで、今日興味深い点に出会いました。 @Veedracは この答え で推奨されています。可変サイズタイプ(intlongなど)は、uint64_tuint32_tなどの固定サイズタイプに置き換えられます。その回答のコメントからの引用:

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

標準が一般的な型を修正しない理由は、@ supercatによって部分的に説明されています here 。 Cは、当時システムプログラミングに通常使用されていたAssemblyとは異なり、アーキテクチャ間で移植できるように作成されています。

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

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

70年代に留まっているのですか、それとも実際にC99の時代以降にintを使用する根拠はありますか?

21
jacwah

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;
}

intが4294967295を保持できないか、18446744065119617025を保持できるマシンでは、最初の関数はnexponentのすべての値に対して定義され、その動作には影響しませんintのサイズさらに、規格では、intのサイズが異なるマシンで異なる動作を生成する必要はありませんが、nおよびexponentの値によっては、未定義の動作が呼び出される場合があります。 4294967295はintとして表現できますが、18446744065119617025は表現できないマシンでは。

2番目の関数は、nが4611686014132420609を保持できないマシンで、exponentおよびintのいくつかの値に対して未定義の動作を生成しますが、nおよびexponentが可能なすべてのマシン(int32_tの仕様は、intより小さいマシンでの2の補数の折り返し動作を意味します)。

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

7
supercat

バッファーサイズ、配列インデックス、WindowsのlParamなどのポインター(つまり、アドレス可能なメモリの量)に密接に関連する値の場合、アーキテクチャに依存するサイズの整数型を使用することは理にかなっています。したがって、variable-sized型は引き続き役立ちます。 typedefがsize_tptrdiff_tintptr_tなどになっているのはこのためです。組み込みのC整数型では必要ないため、それらはtypedefになりますhaveポインタサイズである。

したがって、問題は、charshortintlong、およびlong longがまだ有用かどうかということです。

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

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

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

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

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

OTOH、あなたがしないでください巨大な数または巨大な配列が必要であると仮定しますが、速度が必要です。また、intはすべてのプラットフォームで十分な大きさである可能性がありますが、必ずしも最速のタイプであるとは限りません。64ビットシステムには通常、32ビットintがまだあります。しかし、intlongint_fast16_tのいずれであっても、long longを使用して「最速」のタイプを取得できます。

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

4
dan04