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

第一个链接很好,但第二个无法链接。我只是不明白为什么在大小为 13 时,编译器突然决定依赖 memcpy 来解决这个问题。我尝试了-O0和-O3。我的编译器是一个名为 cl470 的 Windows 可执行文件,不确定它来自哪里。

另一个奇怪的事情是,当我把它放在一个函数中时,这是有问题的,但是如果我全局声明数组,那么就没有问题了。

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

另一种选择是以编程方式生成数组;看起来像一个简单的for循环就可以了。

第三种选择是自己编写和链接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 回答