2013-02-22 46 views
1

我用陣列,其中一個例子被給出爲以下時閱讀本鏈路How do I use arrays in C++?,部分5常見的問題:常見的問題:信任類型不安全連接

// [numbers.cpp] 
int numbers[42] = {1, 2, 3, 4, 5, 6, 7, 8, 9}; 

// [main.cpp] 
extern int* numbers; 
int main() 
{} 

由於「數字」 numbers.cpp是數組的名稱,它通常可以衰減到一個指針等於'&數字[0]',我期望在main.cpp值'數字'仍然是'&數字[0]'。但不是!而是'數字[0]',即'1'。

或比方說,我是編譯器,在「numbers.cpp」,我看到的符號「數字」作爲地址指向「1」,這是爲什麼同樣的符號變更爲值1' 'main.cpp中'?

我明白這就是作者所說的「類型不安全鏈接」。但我不知道爲什麼編譯器 這樣做,即使編譯器只是提出一個類型不匹配的鏈接錯誤對我更有意義。

有人可以幫忙解釋一下嗎?

感謝,

評論:

我想我的理解是,編譯見下文等同,使連接成功,否則將有 '無法解析的外部' 錯誤:

// [numbers.cpp] 
int tmp[42] = {1, 2, 3, 4, 5, 6, 7, 8, 9}; //{1,..9} starts at global address 0x1234 
int *numbers = &tmp[0];     //numbers == 0x1234 

// [main.cpp] 
extern int* numbers;      //numbers == 0x1234 
int main() 
{} 

的真實情況

// [numbers.cpp] 
int numbers[42] = {1, 2, 3, 4, 5, 6, 7, 8, 9}; //{1,..9} starts at global address 0x1234 

// [main.cpp] 
extern int* numbers;      //numbers == numbers[0] == 1 
int main() 
{} 
+1

因爲'int numbers [42]'不是一個指針。 – 2013-02-22 03:51:31

+0

@Seth Carnegie。是的,但似乎編譯器確實從int數組轉換爲int指針。否則,鏈接器將引發「無法解析的外部」錯誤。我絕不能理解爲什麼它給main.cpp中的'數字'賦值'1'。 – user1559625 2013-02-22 04:05:16

+0

@ user1559625,數字[42]將降級爲指針,因此*數字指向值爲1的整數。 – 2013-02-22 04:06:50

回答

2

是否說明的以下方式,幫助:

a是陣列(即,當編譯器知道的a類型是陣列類型),則語法a[i]被解釋爲:返回第i個數組的元素。

在另一方面,當a是一個指針(到數組的第一元素),則相同的語法,a[i],將被解釋爲:查找存儲在a地址,添加的字節數對應到i元素,並返回存儲在那裏的值。

main.cpp,它認爲numbers是一個指針(*)並應用相應的操作。也就是說,它查找存儲在numbers中的值,將其視爲地址,添加一定數量的字節並返回存儲在該地址的值

(*)這樣做是因爲numbers被聲明爲指針。編譯器不知道它確實是一個數組,因爲main.cppnumbers.cpp分開編譯(即它是一個單獨的翻譯單元)。所以它不會將數組衰減爲一個指針–它只是假設它已經是指針了。

+0

還是......不明白,對不起。首先,爲了使連接器成功,'數字'在main.cpp&numbers.cpp中是相同的類型,對吧? (將'數字'定義爲純粹的int,但不是數字中的int數組將會導致連接器失敗。而在單獨編譯中,由於C沒有'數組'類型,numbers.cpp中的'numbers'仍然被解釋爲int *?在你的評論中,'在main.cpp中,它認爲數字是一個指針(*)並應用相應的動作。也就是說,它查找存儲在數字中的值,將其視爲地址,'爲什麼數字[0]被認爲是存儲在數字中的值? – user1559625 2013-02-22 05:40:44

+0

@ user1559625如果鏈接器可以檢查類型,它可能會這樣做。對於可能因爲它們總是被名稱改變(即由編譯器生成並存儲在目標文件中的符號名稱包括類型信息)的函數而言可能有效。但對於變量而言,情況並非一定如此。我的GCC在Linux上生成的代碼沒有爲全局變量命名。當你說C沒有數組類型時,你是什麼意思?它確實如此。 'int []'是一個數組,'int *'是一個指針。這是C和C++中的兩種不同類型。 – jogojapan 2013-02-22 05:54:02

+0

@ user1559625它查找爲變量'numbers'存儲的值,認爲它是一個指針。編譯器知道每個變量引用的內存地址(通常是從堆棧幀指針開始的偏移量)。因此,它生成的代碼轉到該地址,並且認爲它正在處理一個指針,從該地址中提取與指針值(即地址)相對應的儘可能多的字節。在許多與int相同的系統上,它將存儲在數組中的第一個「int」錯誤地解釋爲地址。 (在sizeof(int)!= sizeof(int *)''將會不同的系統上。) – jogojapan 2013-02-22 05:58:35

1

如果數字是一個數組,例如。數字[],那麼你不能改變它指向的內容。目標文件將符號「數字」映射到實際數組{1,2,...}但如果數字是一個指針,例如。 *數字,那麼你可以改變它指向的內容,而目標文件會將符號「數字」映射到單個指針值(它本身可能指向數組的開始,但我們不知道)。

數組和指針的行爲相似,但不是同一回事。

+0

謝謝,我也知道。但是,在這種情況下,爲什麼'number-as-pointer'的值爲'number-as-array [0]'。這是不正確的行爲在C + +碰巧有這個結果? – user1559625 2013-02-22 04:12:54

+0

爲什麼?因爲編譯器相信你,當你說符號「數字」指的是一個指針。實際上,符號指的是第一個元素爲1的數組,因此當您以指針的形式訪問它時,就是這樣。如果你看看生成的彙編語言,它會很有意義。無論哪種方式,不要向編譯器介紹另一個模塊中的符號類型,因爲它無法檢查並會失敗。 – 2013-02-22 04:15:11

+0

'number-as-pointer'的值爲'number-as-array [0]',你確定你不是指'* number-as-pointer'的值是'number-as-array' [0]' – 2013-02-22 04:15:34

-3

在C++中,典型的答案是「使用容器」。對於類似數組的容器,std :: vector通常是你想要的。如果你想獲得最大性能,std :: valarray可能是一個更好的選擇(當然,如果你像使用gslice一樣使用它,那麼性能就是如此)。

更好的文章是「我什麼時候會用C風格的數組」。答:幾乎從不,除非你必須連接到其他軟件。

+0

downvoter care to comment? – 2013-02-22 04:05:22

+1

回答「爲什麼編譯器/鏈接器到這個」不是「使用std :: vector」,雖然這可能仍然是一個有用的評論:) – 2013-02-22 04:07:28

+0

@MichaelDay,標題是「常見的陷阱......」同意我的答案是不包括「爲什麼」,而是「如何避免常見陷阱」的短路。哦,活着,學習我猜... – 2013-02-22 04:10:11