C 語言指針的指針和二維數組的區別?

為什麼:指針的指針不能存二維數組的指針。

如:

1 #include&
2 int main (void)
3 {
4 int a[2][3] = {{1,2,3},{4,5,6}};
5 int ** array_ptr = a;
6 printf("%d
",a[0][0]);
7 //printf("%d
",**array_ptr);
8 printf("%p
",a);
9 printf("%p
",*a);
10 printf("%p
",array_ptr);
11 printf("%p
",*array_ptr);
12 return 0;
13 }
~

編譯報警:

test.c: In function 『main』:
test.c:5:21: warning: initialization from incompatible pointer type
int ** array_ptr = a;
^

運行出錯:

1
0x7ffdce0e9b90
0x7ffdce0e9b90
0x7ffdce0e9b90
0x200000001

補充一下:

7 //printf("%d
",**array_ptr);

這個去掉注釋編譯運行後會出現段錯誤,"段錯誤是什麼意思...?"


指針的指針和二維數組完全倆東西,二維數組是 一維數組的一維數組,元素是數組,所以可以隱式轉化為int (*)[3],跟int **兩回事,擴展到更多維或其它類型也是成立的,只能隱式轉化為第一維的元素的指針

==================

補充說明下隱式轉化為指針,簡單說就是這樣(T是任意類型)

設有一個數組T a[10];

a的類型是「T [10]」,在做大多數運算的時候,都先隱式轉化為「T *」類型,即T的指針

對於多維數組,可以看做是一個一維數組,數組的元素類型就是第二維開始的數組類型,比如:

T a[10][20][30];

是一個有10個類型為「T [20][30]」元素的數組,用代碼解釋更清晰:

typedef T U[20][30];

U a[10];

跟上面的定義等價,所以a只能隱式轉化為「U *」,「U *」展開後也就是「T (*)[20][30]」這個類型了,跟多級指針沒關係,因此,多維數組不存在到多級指針的轉化規則,強轉只會導致問題


先來看看你的問題。

1.為什麼錯了?

我先來問你:a的值是什麼?a的值是一個指向a這個數組第一個元素的指針,好,那麼,a這個數組第一個元素是什麼?第一個元素它還是一個數組,所以,結論是a是一個指向數組的指針。而且指向的這個數組還是一個整形數組,當然這個不重要。

默念幾遍:a是一個指向數組的指針,a是一個指向數組的指針,a是一個指向數組的指針。

而你聲明的

int **array_ptr

是個什麼?

假設某個整形指針

int *p

,那麼

int **array_ptr=p

這個是成立的。也就是說啊,

*array_ptr

引用的是整形指針啊。而你的程序中非要用*array_ptr來引用一個數組指針,那肯定就錯了。

2.該如何改正?

簡單,如下:

int (*array_ptr)[3] = a;

注意:這裡為了保險一點,還是將array_ptr指向的數組的長度寫上,避免錯誤。

以上。

你以為你懂了?來個課後小練習吧?

  1. 聲明一個指向整形的指針p

  2. 聲明一個返回整形指針的函數f

  3. 聲明一個指向 返回整形值的函數 的函數指針ff

  4. 聲明一個指向返回整形指針的函數的函數指針fff

  5. 聲明一個指針數組

  6. 聲明一個數組指針

  7. 變態的:int (*f7[])() ,那麼f7的含義?

  8. 最後一個:int *(*f8[])(),那麼 f8的含義?

    ------------------------------------------------

    不懂我再繼續更


c語言,指針數組:首先它是一個數組,數組的元素都是指針,數組佔多少個位元組由數組本身決定。它是「儲存指針的數組」的簡稱。

數組指針:首先它是一個指針,它指向一個數組。在32 位系統下永遠是佔4 個位元組,至於它指向的數組佔多少位元組,不知道。它是「指向數組的指針」的簡稱。


課上用手機答的,簡單說一下吧。

首先題主要知道多維數組實際上在內存中的結構與一位數組是一樣的,也就是說,它們都是連續的,不管是幾維。

舉個例子:

int arr[2][2][3] = {{{1, 2, 3}, {4, 5, 6}}, {{1, 2, 3}, {4, 5, 6}}};

其實就等價於一位數組把它們按順序寫。

OK,明白這個了咱們再繼續看。

題主認為多維數組可以這麼表示:

int **p = (int **) arr;

咱們不妨先對這個指針取一層地址的值:

int *x = p[0];

這裡 x 肯定是 p 所表示的地址的值,p 的值又與 arr 相等,那麼 x 肯定是 1。對於一個地址為 0x1 的指針,你直接取數組下標允許的值,最後指向的都是一個非法地址,當然會出現段錯誤。

應該的表示方法是 int (*p)[][],這個讀作指向數組的指針,這時 p 指向的是一個二維數組。

然後你寫 p[0], p[1] 它們的結果都是地址,根據你類型中數組的大小計算的,也就是說你不光要這麼寫,還要吧大小寫上,不然編譯器不知道地址的增量是多少。

這時你可能說了,main 函數的 argv 不是 char ** 么,為啥它行?

注意,雖然 string 可以簡單表示成 char *,但是 argv 的另一個寫法是 char *argv[],它讀作指針的數組,也就是說這個數組本身裡面的值就都是一些 char 指針,你寫 argv[0],它的結果還是個指針,所以你可以再進一步取裡面的元素,或直接拿這個字元串指針該幹啥幹啥。

所以如果題主想寫成 int **,那方法就是先構造幾個 int 數組,然後分別拿到它們的地址,再放入一個新的數組中,然後你可以寫成 int **。


當p=a時,二維數組名a作為右值是一個指向數組的指針,p是一個指向指針(指向int)的指針,當p=a時,由於類型不同會警告,解引時會出現錯誤,二級指針和二維數組名有本質區別,應定義為int(*p)[3],指針和數組這裡建議樓主多看點書,自己領悟最重要。


因為int [2][3]的長度是24,offset是一個int的長度是4。而int**是的offset是一個指針的長度,在64位系統下是8。解引用以後取指針長度,也是8位元組。那麼這個結果不是對的嘛。

所以你應該用int*而不是int**

------------

回復里有人說offset是sizeof(int)*3。從語法層面來說的話確實是這樣。所以我上面提到的offset是int的長度是指這個數據塊里連續放著了2*3=6個int


我說清楚一點吧,C/C++ 賦值時類型要對應,以下用int類型舉例,方便表達。為什麼有很多人把指針與數組名看成是一樣的呢,因為一級指針可以用一維數組名去賦值,的確一維int數組名是一個地址,也就是第一個int 元素的地址,而一級int指針也是要存一個int的地址的,這裡剛好就可以賦值了,但到了二維,二維int數組也只是一個地址,但二級指針要用一個一級指針的地址去賦值,二維int數組的地址,雖然跟第一個子數組的第一個int元素的地址一樣,但它不是一個int變數的地址了,在機器看來,它是一個一維int數組的地址,剛剛說了一維數組的地址(即數組名)不是一級指針,所以不能賦給二級指針。只要你記住,數組名不是指針,這種錯誤是不會犯的。

再給個例子

int *p[3];

int **l=p;

以上是可以通過的,為什麼呢?一樣的道理,p是一個數組,存了三個int指針,數組名p是誰的地址呢?是第一個元素的地址,也就是一個一級int指針的地址,二級指針 l 正需要一個一級int指針的地址去賦值,所以p是可以直接賦給l的!

第一次的評論是手機里寫的,太麻煩就寫的不是很清楚,見諒。

數組名是地址,不是指針,你去查一下,數組名只是存了地址,不是指針,即然是地址,就只能賦給一級指針啊。


幾維數組都是int*,而不是int**之類的。就這麼簡單。

二維數組和一維數組的區別,是語法上的,本質根本沒有二維數組這種東西。

1000個士兵,排成一排,方陣(33X33+X),或者立體(10X10X10),指定第一個士兵的位置為整個兵團的位置。所以兵團位置就是士兵位置,不是位置的位置。

其實 a = a[0] = a[0][0],另外數組就是指針的另一個名稱,當然C語言有些特殊處理,但是實質上一樣的。


int**類型指向指針的數組,而不是二維數組


採用int(*p)[3] = a;即可,因為2維數組返回的類型是int(*)[3].並且這貨是隱式轉換的。。2樓說的很好。。


沒有出錯,輸出的就是正確的結果


推薦閱讀:

如何生成rest api文檔?
零基礎學習python需要直接使用linux嗎?
寫代碼的時候適合聽的音樂有哪些推薦?
函數式編程有必要學設計模式和演算法嗎?
請問用c語言怎麼寫出原始的除法運算的函數?就是在不用到"/"的情況下

TAG:編程 | C編程語言 | CC | C指針 |