2017-04-25 89 views
2

爲什麼這樣工作?指向固定大小陣列行爲的指針

uint8_t array[4] = {1,2,3,4}; 
uint8_t* parray = array; 
uint8_t (*p1)[4] = (uint8_t (*)[4])&array; 
uint8_t (*p2)[4] = (uint8_t (*)[4])&parray; 
uint8_t (*p3)[4] = (uint8_t (*)[4])parray; 
uint8_t test1 = **p1; // test1 = 1 
uint8_t test2 = **p2; // test2 = something random 
uint8_t test3 = **p3; // test3 = 1 

粒子陣列顯然是幾乎相同陣列。例如,array [0] == parray [0]。但是當我想要指向數組作爲指向固定大小數組的指針時,我必須使用&符號。當我想要獲得指向parray的指針時,我不能。

實例。

有接受一個指針到固定大小的數組

void foo(uint8_t (*param)[4]) 
{ 
    ... 
} 

當我在另一個函數的PARAM作爲指針的功能,我可以將它傳遞給FOO這樣?

void bar(uint8_t param*) 
{ 
    uint8_t (*p)[4] = (uint8_t (*)[4])param; 
    foo(p); 
} 

有沒有更好的方法?

+2

可能您需要從頭腦中清除的第一件事是謊言,在許多介紹性文本中持續存在,即指針和數組「幾乎相同」。他們不是。在某些情況下,使用它們的表達式會產生相同的結果,這就是爲什麼謊言有時似乎是真的。但是,你正在以謊言是謊言的方式使用它們。 – Peter

+0

爲什麼'foo'需要'uint8_t(*)[4]'類型的參數才能開始? –

+0

@DavidBowling如果'bar'要調用'foo',爲什麼'bar'會帶'uint_8 *',那麼期待別的東西。 – user2079303

回答

7

這是一個稱爲陣列衰減的功能。當數組變量在值上下文中使用時,數組變量被稱爲衰變變成指向第一個元素的指針。

這裏的數組用於值上下文:parray = array,所以它衰變。你可以明確地寫下衰變:parray = &(array[0])。前者(隱式衰變)只是後者的語法糖。

addressof運算符的操作數不是值上下文。因此,數組名稱不會衰減。 &array&(array[0])不同。首先獲取數組類型的地址,後者獲取元素類型的地址。另一方面,parray是完全不同的變量,並且&parray返回存儲指針的地址,而不是數組存儲的地址。

uint8_t (*p1)[4] = (uint8_t (*)[4])&array; 

這是正確的,雖然轉化率是多餘的,因爲&array已經uint8_t (*)[4]類型。

uint8_t (*p2)[4] = (uint8_t (*)[4])&parray; 

這是不對的。 parray類型爲uint8_t*,其存儲的地址不包含類型爲uint8_t[4]的對象。相反,它包含指針。

uint8_t (*p3)[4] = (uint8_t (*)[4])parray; 

這有點可疑。 parray是指向uint8_t的指針,而不是指向uint8_t[4]的指針。但是,恰好指向一個地址,該地址也包含一個uint8_t[4]對象,所以這可以工作。


粒子陣列顯然是幾乎一樣的陣列

但顯然不完全一樣的,你的程序的行爲證明。

array是四個uint8_t元件的陣列,並且parray是指向uint8_t,它指向的array第一個元素。這個區別對於理解很重要。


結論:瞭解什麼陣列衰減是很重要的,而這也正是數組和指針,以及最重要的區別是:顯式轉換可以隱藏編譯器錯誤 - 避免他們時,你可以。


對於編輯:

當我在另一個函數帕拉姆爲指針,可我將它傳遞給foo這樣?

只有當你能證明param指向 uint8_t[4]的第一要素。這基本上是bar的先決條件。

但是,最好不要依靠口頭先決條件,當你可以使用類型系統進行通信的要求:

有沒有更好的辦法?

更改參數類型的bar,讓用戶知道通過正確類型的指針:

void bar(uint8_t (*param)[4]) { 
    foo(param); 
} 

當然,這使得在bar這個簡單的例子是多餘的。

+0

我編輯了我的問題。你能解釋一下我可以使用這個「可疑的」演員嗎? – valentin

+0

@valentin請參閱編輯。 – user2079303

2

賦值語句,

uint8_t (*p2)[4] = (uint8_t (*)[4])&parray; 

,後來做

uint8_t test2 = **p2; 

違反嚴格別名。

詳細說明,&parray的類型爲uint8_t**,您將其轉換爲(uint8_t (*)[4])類型(它們都不是兼容類型),並嘗試解引用目標指針。這導致undefined behavior

相關,C11,章§6.5/ P7

一個目的應具有其存儲的值僅由具有 一個以下類型的左值表達式獲得:88)

- 與對象的有效類型兼容的類型,

- 與對象的有效類型兼容的類型的限定版本,

- 一個類型是有符號或對應於有效類型的 對象的無符號類型,

- 一個類型是有符號或對應於 有效類型的對象的一限定的版本無符號類型,

- 聚合或聯合類型包括其 成員之間的上述類型的一個(包括遞歸地,一個子聚集或含有聯合的成員),或

- 字符類型。

2

粒子陣列顯然是幾乎相同陣列」 < - 這部分是不正確

有從array類型的parray類型,例如,一個隱式轉換初始化(或者分配),例如uint8_t* parray = array;parray等於&array[0]。轉換不存在相反的方向。

在你的p1 p2 p3初始化,你與你的強制類型轉換

uint8_t (*p1)[4] = (uint8_t (*)[4])&array; 

這裏投是多餘的,&array屏蔽類型的表達式已經是一個(uint8_t (*)[4])

uint8_t (*p2)[4] = (uint8_t (*)[4])&parray; 

這裏的演員陣容a lie&parray is a uint8_t**

uint8_t (*p3)[4] = (uint8_t (*)[4])parray; 

這裏投是安全的parray

0

的價值不僅是因爲但當我想要得到的指針數組的指針 固定大小的數組,我必須使用&符號。

在這裏,您的陳述在特定情況下是完全錯誤的,而且總體上是錯誤的。你已經獲得它,並分配給指針變量parray,然後...試圖將該變量的地址作爲指向數組的指針?特別是數組的名稱衰減到指向數組的指針。一般來說指向數組的指針與指向數組的第一個元素的指針相同,因此您可以使用&array[0]或者僅使用array