(32ビットまたは64ビットに応じて)データ型または構造のサイズを決定するのは誰ですか?コンパイラまたはプロセッサ?たとえば、sizeof(int)
は32ビットシステムでは4バイトですが、64ビットシステムでは8バイトです。
また、32ビットと64ビットの両方でcompilerを使用してコンパイルした場合、sizeof(int)
は4バイトであると読みました。
CPUが32ビットと64ビットの両方のアプリケーションを実行でき、データのサイズコンパイラまたはプロセッサの決定で主な役割を果たすと仮定しますか?
最終的にはコンパイラーです。コンパイラの実装者は、CPUが最も効率的に処理するものに関係なく、適合すると思われる整数サイズをエミュレートすることを決定できます。ただし、C(およびC++)標準は、コンパイラの実装者が自由に最速かつ最も効率的な方法を選択できるように作成されています。多くのコンパイラでは、実装者はintを32ビットとして保持することを選択しましたが、CPUはネイティブで64ビットintを非常に効率的に処理します。
これは、32ビットマシンが最も一般的で、intが32ビットであり、もはや32ビットマシンではないことを期待していたときに作成されたプログラムへの移植性を高めるために部分的に行われたと思います(ユーザー ser3386109が指摘する のように、32ビットデータのほうがスペースが少なくて済むため、より高速にアクセスできるため、好まれます。)
したがって、64ビット整数を確実に取得したい場合は、int
の代わりにint64_t
を使用して変数を宣言します。値が32ビットに収まることがわかっている場合、またはサイズを気にしない場合は、int
を使用して、コンパイラが最も効率的な表現を選択できるようにします。
struct
などの他のデータ型については、int
などの基本型から構成されます。
CPU、コンパイラ、オペレーティングシステムではありません。同時に3つすべてです。
コンパイラーは単に物事を補うことはできません。オペレーティングシステムが提供する正しいABI [1]に準拠する必要があります。オペレーティングシステムによって提供される構造体とシステムコールが特定のサイズとアライメント要件を持つ型を持っている場合、コンパイラ開発者がオペレーティングシステムが提供するすべてのラッパー関数を再実装することを望まない限り、コンパイラは実際に自由に現実を作り上げることはできません。次に、オペレーティングシステムのABIを完全に構成することはできません。CPUで合理的に実行できることを実行する必要があります。そして、1つのオペレーティングシステムのABIは、同じCPU上の他のオペレーティングシステムの他のABIと非常によく似ています(コンパイラなどで)行った作業を再利用できるためです。
32ビットと64ビットの両方のコードをサポートするコンピューターの場合、両方のモードでプログラムの実行をサポートするために、オペレーティングシステムによって実行される作業が必要です(システムは2つの異なるABIを提供する必要があるため)。一部のオペレーティングシステムはそれを行わず、選択できないものもあります。
[1] ABIはApplication Binary Interfaceの略です。これは、プログラムがオペレーティングシステムと対話する方法に関する一連のルールです。オペレーティングシステムで実行できるようにプログラムをディスクに格納する方法、システムコールを実行する方法、ライブラリとリンクする方法などを定義します。ただし、たとえばライブラリにリンクできるようにするには、プログラムとライブラリが同意する必要がありますプログラムとライブラリの間で関数呼び出しを行う方法(およびその逆)、およびプログラムとライブラリの両方で関数呼び出しを行う方法については、スタックレイアウト、レジスタの使用法、関数呼び出し規則などについて同じ考えが必要です。また、関数呼び出しの場合は、パラメーターの意味、およびサイズ、サイズ、型の符号付きなどに同意する必要があります。
Sizeof(int)の値を決定するのは、厳密に100%完全にコンパイラーです。システムとコンパイラの組み合わせではありません。これは単なるコンパイラ(およびC/C++言語仕様)です。
IPadまたはiPhoneアプリを開発する場合、Macでコンパイラーを実行します。 MacとiPhone/iPacは異なるプロセッサを使用しています。お使いのMacについては、iPadでintに使用するサイズをコンパイラに伝えません。
プロセッサ設計者は、使用可能なレジスタと命令、効率的なアクセスのためのアライメントルール、メモリアドレスの大きさなどを決定します。
C標準は、組み込み型の最小要件を設定します。 「char」は少なくとも8ビット、「short」と「int」は少なくとも16ビット、「long」は少なくとも32ビット、「long long」は少なくとも64ビットでなければなりません。また、「char」は、プログラムがアドレス指定できるメモリの最小単位と同等である必要があり、標準タイプのサイズの順序を維持する必要があるとも述べています。
他の規格も影響を与える可能性があります。たとえば、「単一Unix仕様」のバージョン2では、intは少なくとも32ビットでなければならないという。
最後に、既存のコードが影響します。ポーティングはすでに十分に困難です。誰も彼らがしなければならないより難しくすることを望みません。
OSとコンパイラを新しいCPUに移植する場合、「C ABI」と呼ばれるものを定義する必要があります。これは、バイナリコードが相互に通信する方法を定義します。
一般的に、ABIはCPUファミリとOSの組み合わせに対して定義されており、あまり変更されません(「long double」変更など、より不明瞭なタイプのサイズが変更されることはありません)。それを変更すると、比較的小さな利益で多数の破損が発生します。
同様に、既存のプラットフォームと類似した特性を持つプラットフォームにOSを移植する場合は、通常、OSが移植された以前のプラットフォームと同じサイズを選択します。
実際には、OS /コンパイラベンダーは通常、基本的な整数型のサイズのいくつかの組み合わせの1つに決めています。
通常、64ビットプロセッサは32ビットと64ビットの両方のバイナリを実行できます。通常、これはOSに互換性レイヤーを持たせることで処理されます。したがって、32ビットバイナリは、32ビットシステムで実行する場合と同じデータ型を使用し、互換性レイヤーはシステムコールを変換して、64ビットOSで処理できるようにします。
コンパイラーは、基本型の大きさ、および構造のレイアウトを決定します。ライブラリが型を宣言する場合、それらがどのように定義されるか、したがって、それらのサイズが決定されます。
ただし、多くの場合、既存の標準との互換性、および他のコンパイラによって作成された既存のライブラリにリンクする必要があるため、特定の実装に特定の選択を強いることがあります。たとえば、言語標準ではwchar_t
は16ビットより広くなければならず、Linuxでは32ビット幅ですが、Windowsでは常に16ビットであるため、Windowsコンパイラはすべて互換性があることを選択します。言語標準の代わりにWindows APIを使用します。 LinuxとWindowsの両方の多くのレガシーコードは、long
が正確に32ビット幅であると想定していますが、他のコードはタイムスタンプを秒単位またはIPv4アドレスまたはファイルオフセットまたはビットを保持するのに十分な幅であると想定しています(1つのコンパイラがint
を64ビット幅として、long
を32ビット幅として定義した後)、言語標準はint
をそれ以上広くできないという新しい規則を作成しましたlong
。
その結果、今世紀の主流のコンパイラはint
を32ビット幅として定義することを選択しますが、歴史的には16ビット、18ビット、32ビット、64ビット、その他のサイズとして定義されています。一部のコンパイラでは、long
が一部のレガシーコードが想定するように正確に32ビット幅になるか、他のレガシーコードが想定するようにポインタと同じ幅になるかを選択できます。
これは、あるタイプが常に32ビット幅であるような今日の仮定が、将来あなたに噛み付くためにどのように戻ってくるかを示しています。これは、32ビットおよび64ビットコードへの移行中に、Cコードベースで既に2回発生しています。
しかし、実際に何をすべきかse?
int
タイプは最近ではほとんど役に立ちません。通常、あなたが得るものをより強力に保証する他のタイプが使用できます。 (これには1つの利点があります:int
ほど広くない型は、自動的にint
に拡張される可能性があります。これにより、符号付きと符号なしの型を混在させると、 int
は、int
より短くならないことが保証されている最小の型です。)
特定のAPIを使用している場合、通常は同じタイプを使用する必要があります。標準ライブラリには、クロックティック用のclock_t
や秒単位の時間用のtime_t
など、特定の目的のために多数のタイプがあります。
少なくとも16ビット幅の最速タイプが必要な場合、それはint_fast16_t
であり、他の同様のタイプがあります。 (特に指定しない限り、これらの型はすべて<stdint.h>
で定義されています。)少なくとも32ビット幅の最小の型が必要な場合、ほとんどのデータを配列にパックするには、int_least32_t
です。可能な限り幅広いタイプが必要な場合は、intmax_t
です。正確に32ビットが必要なことがわかっている場合は、およびコンパイラにそのような型があります、それはint32_t
です32ビットマシンで32ビット幅、64ビット幅で何かが必要な場合64ビットマシンであり、常にポインターを格納するのに適切なサイズであるintptr_t
です。配列のインデックス付けとポインター計算を行うのに適した型が必要な場合は、ptrdiff_t
の<stddef.h>
です。 (これは、C99ではなくC89からのものであるため、別のヘッダーにあります。)
あなたが本当に意味するタイプを使用してください!
コンパイラについて話すとき、あなたはbuild|Host|target
、つまり、「クロスコンパイル」は「」と非常に異なるため、構築するマシン(ビルド)、構築するマシン(ホスト)、およびGCCがコードを生成するマシン(ターゲット)です。ネイティブコンパイル」。
「誰がデータ型と構造のサイズを決定するか」という質問については、バイナリをビルドするようコンパイラーに指示したターゲットシステムに依存します。ターゲットが64ビットの場合、コンパイラはsizeof(long)を8に変換し、ターゲットが32ビットのマシンの場合、コンパイラはsizeof(long)を4に変換します。これらはすべて、ビルドに使用したヘッダーファイルによって事前定義されていますあなたのプログラム。 `$ MAKETOP/usr/include/stdint.h 'を読むと、データ型のサイズを定義するtypedefがあります。
サイズの違いによるエラーを回避するには、 Googleコーディングスタイル-Integer_Types int16_t、uint32_t、int64_tなどの型の使用を推奨します。これらは<stdint.h>
。
上記は、intなどの「Plain Old Data」のみです。構造について話す場合、構造のサイズは パッキング配置 、構造内の各フィールドの境界配置に依存するため、別の話があります。これは構造のサイズに影響を与えます。
それはコンパイラーであり、より正確にはそのコード生成コンポーネントです。
もちろん、コンパイラーはアーキテクチャーに対応しており、それに合った選択を行います。
場合によっては、作業は2つのパスで実行されます。1つは中間コードジェネレーターによるコンパイル時、もう1つはジャストインタイムコンパイラーによる実行時です。しかし、これはまだコンパイラです。