2013-01-06 71 views
33

我想了解類型衰減的性質。例如,我們都知道數組在特定的上下文中衰變成指針。我的嘗試是瞭解int[]等於int*但二維數組如何與預期的指針類型不匹配。下面是測試情況:爲什麼int * []衰減爲int **而不是int [] []?

std::is_same<int*, std::decay<int[]>::type>::value; // true 

此預期返回true,但這並不:

std::is_same<int**, std::decay<int[][1]>::type>::value; // false 

爲什麼這是不是真的?我終於找到了一種方法,使其返回true,這是通過使第一維指針:

std::is_same<int**, std::decay<int*[]>::type>::value; // true 

和斷言的任何類型的指針,但最後被數組也是如此。例如(int***[] == int****; // true)。

我可以解釋爲什麼會發生這種情況嗎?爲什麼數組類型不像預期的那樣對應於指針類型?

回答

61

爲什麼int*[]衰減到int**而不是int[][]

因爲用它做指針運算是不可能的。

例如,int p[5][4]意味着(長度-4陣列int)的陣列。沒有涉及指針,它只是一個連續的大小爲5*4*sizeof(int)的內存塊。當你要求一個特定的元素時,例如int a = p[i][j],編譯器真的這樣做:

char *tmp = (char *)p   // Work in units of bytes (char) 
      + i * sizeof(int[4]) // Offset for outer dimension (int[4] is a type) 
      + j * sizeof(int); // Offset for inner dimension 
int a = *(int *)tmp;   // Back to the contained type, and dereference 

很明顯,它只能做到這一點,因爲它知道「內部」尺寸(S)的大小。投到int (*)[4]保留此信息;它是一個指針(長度爲4的int)。但是,int **不是;它只是一個指針(指向int)。

對於另取此,看到C FAQ的以下部分:

(這是所有C,但這種行爲在C++中基本不變)。

+3

+1。好答案。 – Nawaz

+0

+1簡潔的解釋。 –

+0

另一種看待這個問題的方法是:'int [M] [N]'到'int **'需要兩次轉換(這是不允許的),而不是一次轉換(這是允許的)。第一次轉換需要從'int [M] [N]'轉換爲*指針*到數組的第一個元素。 [first]元素的類型是'int [N]',因此'int [M] [N]'首先轉換成'int(*)[N]',然後需要轉換成'int **' * inner *數組的第一個元素'int [N]'轉換爲'int *'。 – Nawaz

7

Be原因int[M][N]int**是不兼容的類型。

但是,int[M][N]可衰減爲int (*)[N]類型。所以如下:

std::is_same<int(*)[1], std::decay<int[1][1]>::type>::value; 

應該給你true

+1

很簡單,很好的答案Nawaz! –

3

二維數組不是作爲指向指針的指針存儲的,而是作爲連續的內存塊存儲的。

聲明爲int[y][x]類型的對象是大小sizeof(int) * x * y的塊而,int **類型的對象是一個指向int*

+1

準確地說,「聲明爲類型爲int [y] [x]'**的對象是** sizeof(int)* x * y'」大小的塊。 –

10

下不是真正的「設計的」作爲語言;相反,功能是根據需要添加的,並且不會破壞較早的代碼。在C開發的這段時間,這樣的進化方法是一件好事,因爲這意味着大多數開發人員可以在語言可能需要做的所有事情完成之前從早期的語言改進中獲益。不幸的是,數組和指針處理的發展方式導致了各種各樣的規則,回想起來,這些規則是不幸的。

在今天的C語言中,有一個相當實際的類型系統,變量有明確的類型,但事情並非總是如此。聲明char arr[8];將在當前範圍內分配8個字節,並使arr指向其中的第一個字節。編譯器不會知道arr表示一個數組 - 它將代表一個字符指針,就像其他任何char*一樣。據我所知,如果一個人宣稱char arr1[8], arr2[8];,聲明arr1 = arr2;本來就是完全合法的,在概念上與char *st1 = "foo, *st2 = "bar"; st1 = st2;有些相同,但幾乎總是代表一個錯誤。

數組分解成指針的規則起源於數組和指針真的是同一件事。從那以後,數組已經被認爲是一種獨特的類型,但是語言需要基本上與它們不存在的日子保持一致。在制定規則時,如何處理二維數組並不是問題,因爲沒有這樣的事情。可以像char foo[20]; char *bar[4]; int i; for (i=0; i<4; i++) bar[i] = foo + (i*5);這樣做,然後按照與現在使用二維數組相同的方式使用bar[x][y],但是編譯器不會以這種方式查看事物 - 它只是將bar看作指向指針的指針。如果有人想把foo [1]指向與foo完全不同的地方,那麼可以完全合法地做到這一點。

當兩個二維數組添加到C時,沒有必要保持與先前聲明二維數組的代碼的兼容性,因爲沒有任何代碼。儘管可能指定char bar[4][5];將生成等效於使用foo[20]顯示的代碼的代碼,在這種情況下,char[][]可用作char**,但認爲正如分配數組變量會導致錯誤99時間的百分比,如果是合法的話,那麼也會重新分配數組行。因此,C中的數組被認爲是不同的類型,它們自己的規則有點奇怪,但它們是什麼。

+0

這很有趣,但是你知道任何這方面的參考嗎? (這是一個真正的問題,我真的有興趣瞭解這個歷史。) –

+0

不幸的是,我沒有任何單一的參考資料,儘管歷史的各個部分已經在其他SO問題中討論過了。 – supercat

+0

至少有幾個SO線程來支持該聲明?好貼。 – SparKot

相關問題