3

奇妙なバグに遭遇しました。

私はブートローダーのコードを書いているので、派手なライブラリなどはあまりありません。

コード自体は非常に単純です。

int array[32] = { 1, 2, 3, [...snip...], 31, 32 };

このコードは、memcpy がリンクされていないという未解決の外部問題につながります。ただし、このコードは正常にコンパイルおよびリンクされます

int array[12] = { 1, 2, 3, [...snip...], 11, 12 };

実際、エラーはその間に発生します

int array[12] = { 0 };

int array[13] = { 0 };

最初のものは問題なくリンクしますが、2 つ目はリンクできません。サイズが 13 のときに、コンパイラが突然 memcpy に依存することを決定する理由がわかりません。-O0 と -O3 の両方で試しました。私のコンパイラはcl470と呼ばれるWindows実行可能ファイルですが、それがどこから来たのかよくわかりません。

もう1つの奇妙なことは、これを関数内に配置すると問題になることですが、配列をグローバルに宣言すると問題ありません。

4

2 に答える 2

8

コンパイラは時空間のトレードオフを実行しています。

小さい配列の場合、コンパイラはスタック上の各配列スロットを初期化するための個別の命令を発行します。

mov [ebp-4], 1
mov [ebp-8], 2
mov [ebp-12], 3
...

より大きな配列の場合、コンパイラはデータをプログラムの読み取り専用データ セグメントに配置し、次を使用してスタックにコピーしますmemcpy

.rodata:
    _array_initialiser = { 1, 2, 3, ... }

push ebp-4
push _array_initialiser
push 32
call memcpy

これが、配列をファイル スコープにするか、または;staticを削除する理由です。memcpy配列はデータ セグメントに直接配置でき、コンパイル時に初期化できます。

より大きな配列に対してを使用すると、コード サイズが縮小され、命令キャッシュミスがmemcpy減少するため、より効率的です。

あなたが試すことができるいくつかのことは、配列をファイルスコープに移動するか、自分で静的にすることです。配列を介して毎回再初期化する必要がある場合は、ローカル配列に手動でコピーできます (ただし、コンパイラはそのようなループをmemcpy! に変換することもできます)。

static const int array_data[] = { 1, 2, 3, ... };
int array[sizeof(array_data) / sizeof(array_data[0]))];
for (size_t i = 0; i < sizeof(array_data) / sizeof(array_data[0])); ++i)
    array[i] = array_data[i];

もう 1 つのオプションは、配列をプログラムで生成することです。単純なforループが機能するようです。

3 番目のオプションは、自分で作成してリンクすることmemcpyです。数行以上のコードは必要ありません。

于 2012-08-31T18:57:31.167 に答える
1

次のコードは、実行可能ファイルに格納されているデータをスタックにコピーします。

int array[12] = { blah };

配列サイズが特定の数より大きい場合、オプティマイザーは memcpy を使用すると思います。

あなたはおそらくこれをしたいでしょう:

static int array[12] = { blah };

static キーワードを使用すると、静的データをスタックにコピーするコードがコンパイラによって生成されなくなります。

于 2012-08-31T19:03:39.910 に答える