張龍波
(江蘇師范大學科文學院 江蘇 徐州 221116)
在討論C中普通指針與一維、二維數組的關系之前,我們的預備知識如下:
(1)一維數組a[i]中的元素a[m](m≤i-1)的地址的2種表示方法:&a[m],a+m;
(2)一維數組a[i]中的元素a[m](m≤i-1)的值的2種表示方法:a[m],*(a+m);
(3)二維數組a[i][j]的第m行第n列(m≤i-1,n≤j-1)元素a[m][n]的地址的3種表示方法:*(a+m)+n,a[m]+n,&a[m][n];
(4)二維數組a[i][j]的第m行第n列(m≤i-1,n≤j-1)元素a[m][n]的值的3種表示方法:*(*(a+m)+n),*(a[m]+n),a[m][n]。
我們已知:普通的指針變量只能指向相同數據類型的普通變量,a[0]是整型變量,p是整型指針變量,故二者不可以使用“p=a[0]”進行直接指向。可以采取以下兩種方法進行指向:
(1)p=a;(數組名a代表一個指針常量,它指向第一個元素對象a[0])
(2)p=&a[0];(在整型變量a[0]的左側加“取地址運算符”即可獲取其內存單元格地址,故可以直接讓指針變量p指向普通變量a[0]對應的內存單元)
初學者容易寫出這樣的代碼指向:p=a;(這樣寫是錯誤的,因為盡管a是一個常指針(即指針常量),但是a是指向“第一個包含4個元素對象的、所謂的一維數組元素a[0]的”)。因此建議采取以下三種方法進行指向:
(1)p=*a;(這樣寫才正確;因為*a是對a這個常指針“取內容”的,*a的值是a[0];因為這個a[0]它也是一個常指針,但是它是指向其內部的4個元素對象的第一個元素對象a[0][0]的,即a[0]的值與&a[0][0]的值等同;所以不難得出下面的兩種方法)
(2)p=a[0];
(3)p=&a[0][0];(直接讓指針變量p去指向普通變量a[0][0])
(1)二維數組a[i][j]中,“a+m”和“&a[m]”(m≤i-1)代表的含義又是什么呢?我們發現它們的作用是相同的,都是用來指向二維數組a的第m行所有元素對象的;
(2)二維數組a[i][j]中,“*(a+m)+n”和“a[m]+n”(m≤i-1,n≤j-1)指向的又是什么呢?我們發現它們的作用也是相同的,即指向了二維數組對應矩陣中的第m行第n列的元素對象a[m][n];
基于以上分析得出結論:使用普通指針變量指向二維數組元素a[m][n]的通用公式是:p=a[m]+n或p=*(a+m)+n或p=&a[m][n]。
(1)問題的提出:如何定義一個指針p,將其指向具有6個元素的一維數組a呢?它們之間是如何指向及相互聯系的呢?具體探討的實例代碼行如下:
第1行:inta[6]={11,13,15,17,19,21};(定義一個包含有6個元素的一維整型數組a)
第2行:int(*p)[6];(必須定義一個數組指針,這樣才可以指向一維數組;p的類型是:int*[6],所以p是一個專門指向含有6個元素的一維數組的指針變量)
第3行:p=&a;(這是可以采取的方法,作用是將數組名a所在的內存地址賦值給數組指針p,即p指向含有6個元素的一維數組a)
第4行:printf("%p ",&a[0]);(&a[0]是用來指向a[0]這個元素對象的地址)
第5行:printf("%p ",&a);(這個結果和上一行結果一樣,但含義不同;&a是用來指向整個一維數組a的)
第6行:printf("%d ",*((*p)+3));(*p完全等價于數組名a;輸出表列“*((*p)+3)”即變成“*(a+i)”;“*(a+i)”完全等價于a[i],故打印出第4個數組元素的值17)
第7行:printf("%d ",(*p)[3]);(*p完全等價于數組名a;所以輸出表列完全等價于a[3],故打印出17)
第8行:p=p+1;(因為p是一個“只能指向具有6個元素的一維數組的數組指針”,此行代碼的作用就是:數組指針變量p一下子完美跳過了整個的一維數組)
第9行:printf("%p ",p);(p一下子移動了24個內存單元格,所以打印出的內存地址比初始地址大了18H;此行代碼等價于printf("%p ",&a+1))
(2)問題的提出:若想定義一個指針p,需要將其指向二維數組a[3][2]的某行時(如a的第2行),需要將該指針定義成什么類型的指針?如何指向?
a的第2行地址代表的本質是:一個包含2個元素對象的一維數組。因此我們可以采取如下的方法:
第1行:inta[3][2]={11,13,15,17,19,21};
第2行:int(*p)[2];(定義一個數組指針變量p,這個p只能指向包含2個元素對象的一維數組)
第3行:p=a+2;(代表了p指向了二維數組a的第2行)
第4行:printf("%d ",*(*p));(打印出a[2][0]這個元素,結果為19)
第5行:printf("%d ",*(*p+1));(打印出a[2][1]這個元素,結果為21)
(3)指針變量在什么情況下可以做相加或相減的運算?
一般情況下,相同類型的指針變量之間只能做相減運算,相加運算沒有意義;同時要求這幾個指針變量必須同時指向同一數組中的元素對象的時候!如:p1和p2是完全相同類型的指針變量,則“p2-p1”的本質含義及結果是:“(p2-p1)/(指向的元素對象的字節數)”。(注意:上方所說的元素對象有可能是一個數據,也有可能是一行數據,請務必理解)。
(4)一維數組a的元素a[i]可以表示成*(a+i)嗎?二維數組a的元素a[i]可以表示成*(a+i)嗎?都可以。前者的*(a+i)表示a[i]這個一維數組元素的值;而后者的*(a+i)等價于*(a+i)+0;代表的是a[i][0]這個數組元素的地址。(即第i行數組元素的地址)。
(5)函數調用過程中的實參數組名和形參數組名均可以被賦值嗎?前者不可以,因為C編譯器會把實參數組名認識是一個常量指針,常量指針是不可以再次被賦值的;后者卻可以,因為C編譯器會把形參數組名認識是一個指針變量,指針變量當然可以再次被賦值。這兩點請讀者務必注意。
(6)某二維數組a[m][n]的數組元素a[i][j]在數組中的相對位置(相對于首元素a[0][0]的地址)的計算公式是?通過對二維數組對應矩陣數據的分析,我們不能得出a[i][j]的地址是:&a[0][0]+(i*n+j)。(n為二維數組的列數)。
(1)對于一維數組a[6],&a、a和*a的區別是?
&a:這是個指針常量,它指向了整個的包含6個元素的一維數組a;
a:這也是個指針常量,它指向了一維數組a中的第1個元素a[0];(即a等價于&a[0])
*a:這是一個普通數據,代表的是數組a的第一個元素a[0]的值。
(2)對于一維數組a[6],&a+1、a+1和*a+1的含義分別是?
&a+1:依然是一個指針常量,它指向了整個的一維數組a所有元素之后的下一個內存單元格;
a+1:依然是個指針常量,它指向了一維數組a中的第2個元素a[1];
*a+1:依然是一個普通數據,代表的是數組第一個元素a[0]的值再加上一個普通十進制數字1。
(3)對于二維數組a[3][4],&a、a和*a的區別是?
&a:這是一個指針常量,它指向了整個的包含了12個元素的二維數組a;
a:這也是一個指針常量,它指向了第一個元素對象(即二維數組的第1個元素對象是一維數組a[0]),所以a是一個行指針常量,指向了二維數組的第一行(包含4個元素);
*a:依然是一個指針常量,它等價于*(a+0)+0;它指向的是“第0行第0列這個元素對象”。
(4)對于二維數組a[3][4],&a+1、a+1和*a+1的含義分別是?
&a+1:依然是一個指針常量,它指向了整個的二維數組a(包含12個元素)后邊的一個內存單元格;
a+1:依然是一個指針常量,它指向了第2個元素對象(二維數組的第2個元素對象是一維數組a[1]),所以a+1也是一個行指針常量,指向了第2行;
*a+1:依然是一個指針常量,它等價于*(a+0)+1;它又等價于a[0]+1;所以它指向的是“第0行第1列這個元素對象”。