1

配列があるとしましょう

int aVar[10];
...
...
for(i=0; i<10; i++)
    aVar[i] = i*10;

ここで、私が知っているのは、配列がポインターとして参照され、インデックス付きの値の場所が次のようなもので計算されるということです(base address of aVar) + sizeof(int) * i。私が間違っている場合は、私を修正してください。

私の質問:

この計算は、実行可能ファイルを実行する前にコンパイラによって既に行われていますか?それとも、実行中に行われた配列内の正確な位置を見つけるための算術計算ですか?

もちろん、aVarコンパイル時に のアドレスを取得することはできません。

4

3 に答える 3

4

「もちろん、コンパイル時に aVar のアドレスを取得することはできませんか?」

はい、実際には、できるか、十分に近いです。それがグローバルまたは静的である場合、リンカーが解決できる固定の場所を占有します-コンパイル時ではなく、実行時ではありません。また、スタック上にある場合、スタック ポインターからのオフセットはコンパイル時にわかっているため、アドレスがオフセット + レジスターの内容の形式をとるマシン (PC など) で計算する必要はありません。

于 2012-06-28T10:13:52.037 に答える
2

名目上は実行時に行われますが、結果が正しい場合、標準は気にしません (標準はこれを「as-if」ルールと呼んでいます)。それは、C 実装を作成した人次第であり、使用する最適化オプションに依存する場合があります。

コンパイラがループをアンロールすると、スタック ポインタからのオフセットを知るのと同じように、スタック ポインタから などのオフセットaVar[0]を知ることができます。したがって、次のようなコードに避けられない障害はありません。aVar[1]aVar

store 0 at some constant offset from the stack pointer
store 10 at a slightly larger constant offset from the stack pointer
etc.
于 2012-06-28T10:03:25.967 に答える
0

「コンパイル時に aVar のアドレスを取得できません」。確かに、コンパイラはループに入るにそのアドレスを計算するコードを生成でき、パフォーマンスに関しては、ループ前のコードではなく、最適化が必要なループ本体です。

これを最適化する優れたコンパイラは、そのベースアドレスをループ内のレジスタに配置して、再フェッチする必要がないようにするか、配列アクセスがインデックスと同じように配列をスイープすることに気付きます。本当に賢いコンパイラは、i*10 が配列インデックスから計算され、他には何もないことを認識し、インデックスも置き換えます。したがって、コンパイルされた最適化されたコードは、おそらく次のようになります。

int aVar[10];
...
...
register int* p = &aVar;  // my C syntax may be slight wrong here
for(i=0; i10<10*10; i10+=10)
    *p = i10;

(ループを逆にすることもできるため、i10 は 100 から 0 までカウントできます。広く使用されているマシンでは減算によって条件コードが生成されることが多いため、これにより比較命令を節約できます。)

于 2012-06-28T10:51:42.683 に答える