2012-08-31 107 views
3

我遇到了一個奇怪的錯誤。將值賦給數組需要memcpy

我正在爲bootloader編寫代碼,所以我沒有很多花哨的庫和所有。

代碼本身是很簡單,這是

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可執行文件,不太確定它來自哪裏。

另一個奇怪的是,當我把它放在一個函數中時,這是有問題的,但是如果我在全局聲明數組,那麼就沒有問題了。

+0

問題是,可能沒有足夠的堆棧來容納一個13元素的int數組,所以編譯器突然想要將它移動到堆上 - 也許使數組'static'可以解決問題。 – 2012-08-31 18:56:25

+1

哦,+1,因爲這個問題真的沒有意義。 (yupp I <3 Windows) – 2012-08-31 18:56:47

+0

即使解決了這個問題,您正在使用的編譯器顯然不是爲引導加載器目標編譯而設計的,至少不是用於它的交換機。所以你可能會遇到其他問題。 –

回答

8

您的編譯器正在執行time-space tradeoff

對於較小的陣列,所述編譯器發射單個指令來初始化的堆棧上的每個陣列插槽:

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

對於較大的陣列,所述編譯器將數據放置在該程序的read-only data segment並將其複製到使用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中編寫和鏈接;它不應該需要超過幾行代碼。

1

以下代碼將存儲在可執行文件中的數據複製到堆棧。

int array[12] = { blah }; 

我猜優化器在數組大小大於某個數時使用memcpy。

你可能想這樣做:

static int array[12] = { blah }; 

通過使用static關鍵字可以防止編譯器產生的代碼拷貝靜態數據到堆棧中。