これを行うと、コンパイラは何をしますか?
struct something {
int number;
}
int とはメモリが異なりますか?
C コンパイラは単一のメンバーで構造体を最適化しますか?
それはコンパイラに完全に依存します。そして現在のコンパイラ設定。そしてコンパイル中のコード。そしてプラットフォーム。そしてシリウスと月の相対位置。わかりません。特定のアーキテクチャで、特定のコンパイラ フラグがオンになっている特定のコンパイラについて質問すると、特定のコードをコンパイルするときに、それをコンパイルし、生成されたアセンブリを見て、その動作を確認できます。しかし、それなら、自分でそれを行うこともできます。
とはいえ、一般的に、合理的に高いレベルの最適化がオンになっている場合、最新の最適化コンパイラーがこの変換を行うことを期待しています。
編集:どのような違いがあるかについて:私は構造体内のメンバーのオフセットについて話しているだけではありません。他の人が正しく観察したように、そのような構造のメンバーにアクセスするときとスタンドアロンで操作するときに、まったく同じマシンコードが生成される可能性が非常に高くなりint
ます。
ただし、変化する構造に関連する他のプロパティがあります。二つの例が思いつきます。
1つ目は、割り当てに関するものです。に代入するとstruct
、特定のコンパイラがmemcpy()
関数の呼び出しを発行しますが、 への代入ではint
、mov [address], [value]
.
2 つ目の動作の違いは、関数の戻り値に関連しています。一部の (古い) ABIstruct
では、戻り値をスタックにプッシュする関数を返すように指示されていますが、単純なプリミティブ戻り値 (組み込みのコア言語型を意味します) はしばしばレジスタに配置されます。したがって、理論的には、非最適化コンパイラの場合、戻り値レジスタを使用する代わりにpush
、シングルをスタックに置く命令を生成することができます。int
「最適化」とはどういう意味ですか? または、より具体的には、この場合、「最適化されていない」マシン コードはどのように見えるでしょうか?
シングルを含む構造体に対するマシンのセマンティクスは、スタンドアロン変数int
の場合と同じです。int
これは、コンパイラが最適化を行うかどうかに関係なく、マシンコードは通常同じであることを意味します。int
つまり、そのような構造は、すでに本質的にスタンドアロン変数と同じです。
言い換えれば、この場合、「最適化されていない」コードがどのように表示されるかわかりません。それを「非最適化」する自由はどこにありますか?
「非最適化」の機会の 1 つは、おそらく struct サイズのアライメントです。これは、何らかのあいまいな理由で、スタンドアロンのサイズよりも大きくなる可能性がありint
ます。しかし、これは通常、明示的に要求しない限り、実際には発生しません。
0
また、唯一のフィールドのアドレスを取得するために構造体の先頭アドレスに常に追加を試みるコンパイラも想像できます。しかし、実際にそのようなことを行うコンパイラは見たことがありません。
新しい質問に答えるには:
int とはメモリが異なりますか?
実際には、答えはノーです。実装では、プレーンよりも構造体に高いアラインメント要件を課すことが許可されているため、アラインメントに一致するように構造体のサイズint
も拡張されます。ただし、そうすることには何の利点もありません。また、そうするコンパイラを知りません。(確かに、すべての主要な CPU アーキテクチャとオペレーティング システムの標準 ABI では、これが許可されていません。)
他の側面については:
可能性はありますが、ほとんどありませんが、コンパイラが構造体型の代入に対して効率の悪いコードを生成する可能性があります。H2CO3 が述べたようmemcpy
に、単一ワードのロード/ストアが実行される場合でも、構造割り当ての呼び出しが常に生成される可能性があります。
誰も触れていない側面の 1 つは、呼び出し規約での引数の受け渡しと戻り値です。引数または戻り値として構造体型を受け取ったり返したりする関数を作成することはめったにありません(そして一般的に眉をひそめられます)。通常、代わりにポインターが使用されます。ただし、C では許可されており、単一の を含む構造体を渡したり返したりするための呼び出し規則int
が type のオブジェクトの規則と一致int
するか、またはより大きな構造体に一般化する他のメカニズムを使用するかは、アーキテクチャ/ABI 固有です。
質問は単一の を含む構造を前提としていint
ますが、単一の浮動小数点変数を含む構造はさらに興味深いものです。多くのプロセッサには、残りの CPU から多少分離された浮動小数点ユニット (FPU) があります。通常、メイン CPU はメモリとの間でレジスタをロードおよびストアでき、FPU も同様にレジスタをロードおよびストアできますが、メイン CPU のレジスタと FPU のレジスタの間に直接的なパスはありません。
一般に、関数を呼び出すときは、パラメーターと関数の戻り値をレジスターに渡す方が、呼び出し元がそれらをメモリーに格納し、呼び出された関数がそれらを読み取るよりも高速です。ただし、メイン CPU レジスタを使用して浮動小数点パラメーターを渡し、値を返すことは、考えられる最悪のメカニズムdouble foo(double x) {return x*2.0}
です。が呼び出されy=foo(w+1.0)+3.0;
た場合、システムはw
FPU レジスタにロードし、1.0 を追加してメモリに格納し、CPU レジスタにロードして呼び出すfoo
必要があり、FPU にロードできるようにメモリに格納する必要があります。次に、2.0 を掛けてメモリに格納し、CPU レジスタにロードしてから戻るため、メイン コードはそれをメモリに格納し、FPU にロードし、3.0 を加算し、最後に Y に格納します。汚い。
FPU レジスタを使用して浮動小数点パラメーターを渡すと、パフォーマンスが大幅に向上する可能性があります。ただし、一般に、これは浮動小数点プリミティブ型でのみ行われます。単一の浮動小数点変数を含む構造体は、私が知っているすべてのシステムで、メイン CPU レジスタまたはメモリを使用して渡されます。これは、呼び出し元および/または呼び出されたコードが値に対して浮動小数点演算を実行する必要がある場合には悪いことかもしれませんが、まれに、特にメイン CPU が何かをしなければならない場合には良いことかもしれません。問題の値で。たとえば、値をメイン CPU レジスタにロードし、コンペア アンド スワップを実行させることで、4 バイトの浮動小数点数でコンペア アンド スワップ操作を実行できます。しかし、FPU レジスタ内の値を取るコンペア アンド スワップ ルーチンは、メモリまたはメイン CPU レジスタ内のいずれかにあるパラメータを取るルーチンよりも多少遅くなります。パフォーマンス上の利点が構造体の使用を正当化するのに十分かどうかはわかりませんが、それらを認識しておくことは良いことかもしれません。