一,引例子
二維數組可以使用指向數組的指針代替,而指針數組才可以用指向指針的指針代替。
- #include<iostream>??
- using?namespace?std;??
- ??
- void?main()??
- {??
- ?????char?*a[]={"Hello","the","World"};?//指針數組??
- ??
- ?????char?**pa=a;?//指向指針的指針??
- ??
- ?????pa++;???
- ??
- ?????cout<<*pa;??
- }??
?
類型確定的數組,元素的類型和元素的個數是確定的;數組退化為對應的指針,元素的類型保持一致;
所謂的二維數組只是數組的數組,因此二維數組不能退化為二級指針,而只能退化為指向一維數組的指針
例子:
#include <iostream.h>?
void main()
{?
??? int a[2][3];??? //二維數組
??? int **p=a;????? //二維數組不能退化為指向指針的指針
}?
請問為什么是錯誤的??(請不要說數組名是一個指針這個我知道,我想知道為什么不能用二級指針指向二維數組)
??
解析:首先數組和指針的概念你沒分清楚,數組的本質你沒搞清楚。這是導致問題出現的根源。
????? int x[5]; 這個定義里面,我們說定義了一個數組x,此數組有5個數組元素,元素的類型為int類型。
????? 首先要問的是,x到底為什么東西?x是數組名,x代表了數組第一個元素的首地址。沒錯,x確實是數組的名字,x的值也確實是第一個數組元素的地址值。
????? 注意這里我們說x代表的值與數組第一個元素的地址值相等,但類型不一樣。
????? 那么數組x的類型到底是什么呢? 有人說就是int * 類型。有如下語句可以做證:
???????????????????????? int *p=x; //這句話是正確的。
????? 數組x的類型真是int *嗎?我們說不是,因為下面的語句是不正確的:
???????????????????????? int a=10; x=&a; // int *類型的變量時可以接受值的。
????? 所以x不是int*? 那么我們可以猜測x的類型是不是 int *const呢?也就是說x是一個地址值不可以改變的指針。這句話貌似有點正確。但是請大家看看下面的例子:
???????????????????????? int x[5]={0}; int a=sizeof(x); // a的值到底是多少?
?????實際上這里a的值是5*4=20 這里是整個數組占用的字節數。 我們不是說x的類型是int * const類型的嗎,也就是x應該是一個指針類型,應該是4個字節的啊,為什么sizeof出來時整個數組占用的字節數呢?例如? sizeof(int *)這個的結果就是4。所以有此可以看出,x的類型并不是int*,也不是int * const。? i
?????? int x[5];中的x到底是什么呢,我們說x是數組,此數組有5個元素,并且每個元素都是int類型。 我們有一個識別數據類型的規律例如:?
?????? int x; //x類型為int
?????? int *x;//x類型為int *
?????? int **x;//x類型為int **
?????? int (*x)[10];//x類型為int(*)[10]實際上是指向數組的指針
?????? int (*x)(int ,int);//x的類型為int(*)(int,int)實際上是指向函數的指針?
??? 由此可以看出,一個符號是什么數據類型,我們只要在其定義的表達式中去掉符號本身,剩下的就是符號的類型了。
??? 照此推斷,int x[5];中x的類型應該是 int [5]這個類型,可以看出此類型并不是int *類型。?
??? 那么int x[5];中的x可以這樣賦值: int *p=x; 為什么呢?只能說這里面將x的類型隱式轉換為了int *類型。所以這里是可以賦值的,因為進行了類型轉換。
?? 再請看下面的例子:?
???????? void function(int x[5])
?????????{??
???????????? cout<<sizeof(x)<<endl; //這里輸出4
????????? }? 為什么會輸出4,而不是4*5呢,可以看出上面的函數形參實際上類型是int*,并不是數組類型,
?????? 所以我們在定義函數的時候,下面的都是與上面等價的:?
?????? void function(int x[])//元素個數是多少可以省略 {?? cout<<sizeof(x)<<endl; //這里輸出4 }
???????void function(int *x) //直接寫成指針變量也沒錯 {?? cout<<sizeof(x)<<endl; //這里輸出4 }?
??
?? 那么我們看一個類似的問題:
????????? int x[5]; int **p=&x; //為什么會報錯? 因為類型不匹配。?
??????? ?p的類型是int **,而&x的類型卻不是int **。 &x的類型實際上是int(*)[5],因為取的是x的地址,也就是說這個地址是數組的地址,并不是指向數組第一個元素的指針的指針(也就是二維指針),而是整個數組的地址。
???????? 所以我們可以改成下面的: int (*p)[5]=&x;//這就對了。
?
二,指向數組的指針,和指向數組元素的指針有什么不同??
??? 例如int *p;我們要注意的是,p的類型是int*,p占用的空間4個字節,p指向的數據類型是int。P指向的數據類型占用4個字節。
??? 所以對于指針變量,我們要明白指針變量本身是占用空間的,本身是有類型的,其次指針變量所指向的空間是有類型的,是有空間的。?
????那么int *p; char *p1; 對于指針變量來說p,p1里面都放的是地址值,說白了就是一個數值,他們都占用4個字節的空間,但是他們的類型不一樣,p里面的地址指向的是int類型的數據,p1指向的是char類型的數據,這主要體現在p++與p1++中他們在內存中移動的字節數是不一樣的,我們假設int占4個字節,char占1個字節。那么對于p來說向前移動了4個字節,p1來說移動了一個字節。這就是他們的類型不同,導致運算過程中的不同。 int x[5];
???? int (*p3)[5]; 此時p3指向數組x,那么p3++實際上向前移動了多少呢,可以算出移動了4*5個字節。也就是p3指向的是一個數組,是整個數組,所以p3移動的時候是將一個數組當做一個整體來看待的。所以向前移動了一整個數組的距離。? 再看你的問題之前,我們來看一個類似的問題:?
?????int a[2][3]; int? **p=&a; //這里我用&a來賦值行不行呢。是不行的。? 這里為什么是錯誤的,原因就是因為&a的類型不是int**類型。所以類型不兼容,導致不能賦值,同時這兩種類型是不可以相互轉換的。那么&a到底是一個什么樣的類型呢。 我們說&a取的是整個數組的地址,那么&a自然就是指向整個數組的指針了。
???? int (*p)[2][3]=&a; 此時這樣賦值才是正確的。如果我們要用a直接賦值,那該定義一個什么樣的變量來接受它呢,首先要明白,數組名代表的地址類型是指向數組的第一個元素的指針,
??? 例如:? int a[10]; int *p=a; 實際上這里與 int *p=&a[0];是等價的。因為指向a[0]的指針類型就是int*類型。? 那么&a的是取數組的地址,其類型是指向數組的指針,而不是指向數組第一個元素的指針,整個是要區別的,他們的類型就不一樣。 int(*p)[10]=&a;?
???? 所以說這里的a和&a絕對不是同一個東西,雖然本質上他們的地址值是一樣的,但是他們的類型不一樣。就決定他們代表不同的意義。?
??? ?那么剛剛說了對于下面的例子:
??????????? int a[2][3];
??????????? int (*p)[2][3]=&a;
???? 我們可以定義這樣的一個變量p來接受&a的值。?
???? 那么我們要接受a應該定義一個什么樣的變量呢。a[2][3]是一個二維數組,可以看成是這樣的a是一個數組,具有兩個元素,分別為a[0],a[1]其中這兩個元素的值a[0],a[1]他們的值又是一個具有3個元素的數組。此時我們可以將a[0],a[1]看成是數組名,那么a[0][0]就是數組a[0]的第0個元素了。對應關系如下: a[0] ---->? a[0][0],a[0][1],a[0][2] a[1] ---->? a[1][0],a[1][1],a[1][2]? 那么a到底是什么,其實a數組有兩個元素,a[0],a[1],那么a的值自然就是其第一個元素的地址了,也就是&a[0]了。這是一個什么類型?? 我們知道如果我們將a[0]看成一個整體,例如我們用A來代替a[0],那么A[0],A[1]就相當于a[0][0],a[0][1] 。 此時A就是一個int類型的數組,&A,的類型實際上就是 int(*p)[3]這個類型。? 所以下面的代碼也是正確的:???
????? int a[2][3];? int(*p)[3]=a; //所以對于你的問題,可以這樣子