標籤:

此處為何要使用取地址符?

int ia[3][4];

int (*p)[4] = ia;

p = ia[2]; // 這裡為什麼需要取地址符?

C++中ia[2]不就是下級數組名嗎?為什麼還要取地址?

這個代碼是C++ primer上面的,不是很清楚。初學C++,如果問題太弱智請見諒!


所以說基本概念不清有多禍害。

先解釋原因:

ia的類型是int[3][4],所以ia[2]的類型是int[4]。

因此,ia[2](等價於(ia[2]))的類型是int(*)[4],就是p的類型。

然而,C++中的數組在大多數情況(運算符是一個例外)下會轉換成指向第一個元素的指針。(下文稱為decay。這個術語暫時沒有找到出處)也就是說,T a[N];中,儘管a是一個T[N]類型的數組對象,但是你能見到的大多數地方它都會decay成指針。例如:

a + 1 // decay

*a // decay

a[0] // decay

a // NO decay

所以,使用ia初始化p是可以的,因為ia decay成int(*)[4];它等價於ia[0]。當然ia[2]也可以。而ia[2]不行,因為它decay成int*,不能隱式轉換成int(*)[4]。這就是原因所在。

本來想吐槽的,想想還是算了。另外,ia和ia的值真的是不一樣的,你們能不能消停消停。。


你們不要嘲笑題主……

這問題還是值得動動腦的

p是一個指向長度為4的一維數組的指針。(因為解引用後得到了一維數組的引用)

ia是一個數組名

但在初始化p時,ia轉換成了指針(默認數組名轉化成該數組第一個元素的地址,也就是指向ia[0]的指針,而ia[0]是一個一維數組的引用,與p相同),相當於 p = ia[0]。

初始化p這裡,是一個隱式的類型轉換。(相當於p = ia[0])。

而後面才是正常的寫法。

我換一種寫法你就會明白了

typedef int[4] arr;

arr ia[3];
arr *p = ia;

p = ia[2];

至於說ia[2] == ia[2]的。。

ia(保存了ia[0]的地址)

ia[2](保存了ia[2][0]的地址)

ia[2](是ia[2]的地址,也就是 ia + 2)

//update

在和 @周柏祥 討論以後得到了另一個角度的看法

1.在C里,多維數組是線性的。(我的理解是實際上存的都是基礎類型的地址。。)

2.ia+2 = a[2] = a[2][0]

int a[3][4] = {1};
std::cout &<&< *((int *)a) &<&< std::endl; //output: 1

//感覺學到了新姿勢。不過這和題主的問題沒多大關係。

//題主 讀一讀《C++ Primer》吧…


ia[2]的類型是是int[4],於是(ia[2])的類型就是int(*)[4]了,就這麼簡單而已(結合性不是很清楚...)


我從來沒印象說方括弧之後的整體也可以算數組名的。


類型,帶取地址符號後類型才是一致的。

p的類型是 int (*)[4] ,

ia[2]的類型是 int *的

實際上 ia[2]的數字值 和 ia[2] 是一樣的, 你試試類型轉換

p = (int(*)[4])ia[2];


#include &

using namespace std;

int _tmain(int argc, _TCHAR* argv[])
{
int ia[3][4] = { { 1, 2, 3, 4 }, { 5, 6, 7, 8 }, { 9, 10, 11, 12 } };
int(*p)[4] = ia;
p = ia[2];
cout &<&< *ia[2] &<&< endl; cout &<&< typeid(ia[2]).name() &<&< endl; cout &<&< typeid(ia[2]).name() &<&< endl; return 0; } /* 輸出: 9 int [4] int (*)[4] */

寫代碼看一下……

有一本電子書不錯 讓你不再害怕指針.pdf_免費高速下載

還有另外兩本不錯的但是講的篇幅比較大(全面)的 C缺陷和陷阱.pdfC和指針(第二版)高清pdf版.pdf


因為ia[2]是ia數組的一個元素,這個元素是int[4]類型的,需要獲得一個元素的地址當然需要取址啦。

本質上沒有多維數組,ia是個元素數量為3的數組,每個元素都是一個元素數量為4的數組。


因為你的p是一個指向具有4個元素的數組int[4]的指針,而ia[2]是一個指向單個元素int的指針。類型不一樣怎麼賦值?加了取地址,成了一個4元素數組的首地址,就可以賦值了。


ia[2] 和 ia[2] 值是一樣的。

p是指向一個含有4個int的一維數組。

ia[2] 是一維數組首元素地址,也就是第9個元素的地址 (int*)。ia[2]是取去這個數組的地址(第3個一維數組的地址,int (*)[4])。

我想作者就是這個用意了。

用的時候加和不加沒有區別了,因為p的類型就是 int (*)[4] 。ia[2] 和ia[2] 值一樣,p上的操作就一樣了。


印象中,那個取址符沒有實際意義。不過需要回去在確認一下

==== 5月2日 更新 ====

仔細想了一下,加不加取址符獲得的地址是一樣的,不過語義上有區別。

int ia[3][4];
int (*p)[4] = ia;
p = ia[2]; // 這裡為什麼需要取地址符?

加上取址符,獲取的是ia[2]指向的地址,取址符的含義本來就是獲取地址,所以這裡的語義是獲取ia[2]所指向的地址空間;不加取址符獲取到的是ia[2]這個類型,而這個類型是一個數組類型,即 int [4] 的數組類型,並實現賦值。

所以,從語義上來講,加上取址符執行的是「先取址、再賦值」;不加取址符執行的是直接的「變數賦值」。從獲得到數據類型上講,加上取址符得到的是指針類型,不加取址符得到的是數組類型

應該想個辦法把兩種數據的類型列印出來,用 typeof 沒成功,等找到辦法之後再來更新。

雖然題主提的問題很基礎,但是已經基礎到了以前根本沒有仔細考慮過的地步,細想一下還是挺有難度的。對題主提出了這個問題表示感謝!

==== 5月3日 更新 ====

一個贊都沒有收集到,不開心不開心

閑言少敘,上代碼!

我測試用的系統環境如下,編譯器用了gcc

Darwin bogon 14.0.0 Darwin Kernel Version 14.0.0: Fri Sep 19 00:26:44 PDT 2014; root:xnu-2782.1.97~2/RELEASE_X86_64 x86_64

測試代碼

#include &

int main()
{
int a[4][8];
int (*p1)[8];
int (*p2)[8];

p1 = a[2];
p2 = a[2];

printf("p1 size: %ld, address: %p
", sizeof(p1), p1);
printf("p2 size: %ld, address: %p
", sizeof(p2), p2);
printf("a[2] size: %ld, address: %p
", sizeof(a[2]), a[2]);
printf("a[2] size: %ld, address: %p
", sizeof(a[2]), a[2]);

return 0;
}

編譯:

a.c:9:5: warning: incompatible pointer types assigning to int (*)[8] from int [8]; take the address with [-Wincompatible-pointer-types]
p1 = a[2];
^ ~~~~

1 warning generated.

警告原因,a[2]類型是數組類型,應該使用地址類型,應該加上取址符。和我上文分析結果一致。

運行結果:

p1 size: 8, address: 0x7fff5abacbf0
p2 size: 8, address: 0x7fff5abacbf0
a[2] size: 32, address: 0x7fff5abacbf0
a[2] size: 8, address: 0x7fff5abacbf0

從sizeof函數上分析數據類型,除a[2]是int [8]類型的數組外,其他的都是指針類型。

收工。


沒看懂,數組名本來是a,怎麼突然變ia了?

還有賦值行數組指針這樣寫不是更好?

int a[3][4];

int (*p)[4];

p=a;


要是我的員工敢寫這樣的代碼,我發現一個開除一個。

c的自由度很高,但它所提供的自由度不是讓你用晦澀的手段實現一些簡單的功能的。寫出這樣代碼的人,要不就是腦子裡一團漿糊,沒有培養的意義; 要不就是想在項目里留定時炸彈,良心大大的壞了。可讀性,可維護性太低,一定是故意這麼做的,此人不除,項目遲早出大問題。


推薦閱讀:

新手如何閱讀《C++ Primer》?
新手自學C++ Primer(第五版)應該用什麼開發環境?
如何正確的通過 C++ Primer 學習 C++?

TAG:CC | CPrimer |