一、數組的指針、指針數組以及指向指針的指針 考慮數組的指針的時候我們要同時考慮類型和維數這兩個屬性。換一句話,就是說一個數組排除在其中存儲的數值,那么可以用類型和維數來位置表示他的種類。 A)一維數組 在c和c++中數組的指針就是數組的起始地址(也就第一個元素的地址),而且標准文檔規定數組名代表數組的地址(這是地址數值層面的數組表示)。例如: int a[10]; int *p; p=&a[0]//和p=a是等價的:
因為a是數組名,所以他是該數組的地址,同時因為第一個元素為a[0],那么&a[0]也代表了該數組的地址。但是我們是不是就說一個數組名和該數組的第一個元素的&運算是一回事呢?在一維的時候當時是的,但是在高維的時候,我們要考慮到維數給數組帶來的影響。 a[10]是一個數組,a是數組名,它是一個包含10個int類型的數組類型,不是一般的指針變量噢!(雖然標准文檔規定在c++中從int[]到int*直接轉換是可以的,在使用的時候似乎在函數的參數為指針的時候,我們將該數組名賦值沒有任何異樣),a代表數組的首地址,在數字層面和a[10]的地址一樣。這樣我們就可以使用指針變量以及a來操作這個數組了。 所以我們要注意以下問題: (1) p[i]和a[i]都是代表該數組的第i+1個元素; (2) p+i和a+i代表了第i+1個元素的地址,所以我們也可以使用 *(p+I)和*(a+I)來引用對象元素; (3)p+1不是對於指針數量上加一,而是表示從當前的位置跳過當前指針指向類型長度的空間,對於win32的int為4byte; B)多維數組 對於二維數組a[4][6];由於數組名代表數組的起始地址,所以a(第一層)和第一個元素a[0][0]地址的數字是相同的,但是意義卻是不同的。對於該數組我們可以理解為:a的一維數組(第一層),它有四個元素a[0]、a[1]、a[2]、a[3](第二層),而每個元素又含有6個元素a[0][0],a[0][1],a[0][2],a[0][3],a[0][4],a[0][5](第三層),…到此我們終於訪問到了每個元素了,這個過程我們經歷了:a->a[0]->a[0][0]; 整體來講:a是一個4行5列的二維數組,a表示它指向的數組的首地址(第一個元素地址&a[0]),同時a[0]指向一行,它是這個行的名字(和該行的第一個元素的首地址相同(第一個元素為地址&a[0][0]))。所以從數字角度說:a、a[0]、&a[0][0]是相同的,但是他們所處的層次是不同的。 既然a代表二維數組,那么a+i就表示它的第i+1個元素*(a+i)的地址,而在二維數組中 *(a+i)又指向一個數組,*(a+i)+j表示這個數組的第j+1個元素的地址,所以要訪問這個元素可以使用 *(*(a+i)+j)(也就是a[i][j])。 他們的示意圖為(虛線代表不是實際存在的): 對照這個圖,如下的一些說法都是正確的(對於a[4][6]): a是一個數組類型,*a指向一個數組;a+i指向一個數組;a、*a和&a[0][0]數值相同;a[i]+j和*(a+i)+j是同一個概念; 總結一下就是:我們對於二維指針a,他指向數組a[0,1,2,3],使用*,可以使他降級到第二層次,這樣*a就指向了第一個真正的數組。對於其他的情況我們也可以采用相同的方式,對於其他維數和類型的數組我們可以采用相類似的思想。
說到指向數組的指針,我們還可以聲明一個指針變量讓它指向一個數組。例如: int (*p)[5]; 這時p就是一個指針,要指向一個含有5個int類型元素的數組,指向其他的就會出現問題。 這個時候我們可以使用上面的什么東西來初始化呢? 我們可以使用*a,*(a+1),a[2]等。 原因很簡單:我們在一個二維的數組中,那么表達方式有上面的相互類似的意義呢?只有 *a,*(a+1),a[2]等, C)指針數組 一個指針數組是指一個數組中的每個元素都是一個指針,例如: int *p[10];//而不能是int (*p)[10] 或者 char *p[10]; 此時p是一個指針(數值上和&p[0]一樣); 在前面有int t[10]; int * pt=t;//使用pt指向t 那么這里我們用什么指向int *t[10]中的t呢?我們要使用一個指針的指針: int **pt=t; 這是因為:在int *t[10]中,每個元素是指針,那么同時t又指向這個數組,數組上和&t[0]相同,也就是指向t[0],指向一個指針變量,可以說是一個指針的指針了,所以自然要用 int **pt; D)指針的指針 一個指針變量內部可以存儲一個值,這個值是另外一個對象的地址,所以我們說一個指針變量可以指向一個普通變量,同樣這個指針變量也有一個地址,也就是說有一個東西可以指向這個指針變量,然后再通過這個指針變量指向這個對象。那么如何來指向這個指針變量呢?由於指針變量本身已經是一個指針了(右值),那么我們這里就不能用一般的指針了,需要在指針上體現出來這些特點,我們需要定義指針的指針(二重指針)。 int *p1=&i; int**p2=&p1; 綜合以上的所有點,下面是我們常常看到一些匹配(也是經常出錯的地方): int a[3],b[2][3],c,*d[3]; void fun1(int *p); void fun2(int (*p)[3]); void fun3(int **p); void fun4(int p[3]); void fun5(int p[]); void fun6(int p[2][3]); void fun7(int (&p)[3]); 自己加上的:int (&p)[3]:p是一個引用,引用的是一個一維數組; #include <string.h> #include <stdlib.h> #include <stdio.h> void fun7(int (&p)[3]) { p[0] = p[1] + p[2] + p[0]; } void main() { int t[3] = {1,2,3}; fun7(t); system("pause"); } 跟int *p的不同點在於 int t1[4] = {1,2,3,4}; fun7(t1);是錯誤的 而 int t1[4] = {1,2,3}; fun7(t1);是對的 最新C99標准 int f(int t[3]);參數的數組個數最少為3; 數組沒有像malloc一樣有信息頭;所以是經過在編譯時檢查的;同時說明sizeof(數組名)是編譯時生成; 函數 不會產生編譯時刻的可能值(但邏輯上不一定都對)
函數 |
不會產生編譯時刻的可能值(但邏輯上不一定都對) |
fun1 |
a, &a[i], *b ,b[i],&b[i][j] ,&c ,d[i] |
fun2 |
b,b+i, |
fun3 |
d |
fun4 |
a, &a[i], *b ,b[i],&b[i][j] ,&c ,d[i] |
fun5 |
a, &a[i], *b ,b[i],&b[i][j] ,&c ,d[i] |
fun6 |
b |
fun7 |
a |
|