指針和數(shù)組有著密切的關(guān)系,任何能由數(shù)組下標(biāo)完成的操作也都可用指針來實(shí)現(xiàn),但程序中使用指針可使代碼更緊湊、更靈活。
一、指向數(shù)組元素的指針
我們定義一個(gè)整型數(shù)組和一個(gè)指向整型的指針變量:
int a[10], *p;
和前面介紹過的方法相同,可以使整型指針p指向數(shù)組中任何一個(gè)元素,假定給出賦值運(yùn)算
p=&a[0];
此時(shí),p指向數(shù)組中的第0號元素,即a[0],指針變量p中包含了數(shù)組元素a[0]的地址,由于數(shù)組元素在內(nèi)存中是連續(xù)存放的,因此,我們就可以通過指針變量p及其有關(guān)運(yùn)算間接訪問數(shù)組中的任何一個(gè)元素。
Turbo C中,數(shù)組名是數(shù)組的第0號元素的地址,因此下面兩個(gè)語句是等價(jià)的
p=&a[0];
p=a;
根據(jù)地址運(yùn)算規(guī)則,a+1為a[1]的地址,a+i就為a[i]的地址。
下面我們用指針給出數(shù)組元素的地址和內(nèi)容的幾種表示形式:
(1). p+i和a+i均表示a[i]的地址, 或者講,它們均指向數(shù)組第i號元素, 即指向a[i]。
(2). *(p+i)和*(a+i)都表示p+i和a+i所指對象的內(nèi)容,即為a[i]。
(3). 指向數(shù)組元素的指針, 也可以表示成數(shù)組的形式,也就是說,它允許指針變量帶下標(biāo), 如p[i]與*(p+i)等價(jià)。
假若: p=a+5;
則p[2]就相當(dāng)于*(p+2), 由于p指向a[5], 所以p[2]就相當(dāng)于a[7]。而p[-3]就相當(dāng)于*(p-3), 它表示a[2]。
二、指向二維數(shù)組的指針
1.二維數(shù)組元素的地址
為了說明問題, 我們定義以下二維數(shù)組:
int a[3][4]={{0,1,2,3}, {4,5,6,7}, {8,9,10,11}};
a為二維數(shù)組名,此數(shù)組有3行4列, 共12個(gè)元素。但也可這樣來理解,數(shù)組a由三個(gè)元素組成:a[0],a[1],a[2]。而每個(gè)元素又是一個(gè)一維數(shù)組, 且都含有4個(gè)元素(相當(dāng)于4列),例如,a[0]所代表的一維數(shù)組所包含的 4 個(gè)元素為a[0][0], a[0][1], a[0][2], a[0][3]。如圖所示:
______ _______________
a---| a[0] | ____ | 0 | 1 | 2 | 3 |
|______| |___|___|___|___|
| a[1] | ____ | 4 | 5 | 6 | 7 |
|______| |___|___|___|___|
| a[2] | ____ | 8 | 9 | 10| 11|
|______| |___|___|___|___|
但從二維數(shù)組的角度來看,a代表二維數(shù)組的首地址,當(dāng)然也可看成是二維數(shù)組第0行的首地址。a+1就代表第1行的首地址,a+2就代表第2行的首地址。如果此二維數(shù)組的首地址為1000,由于第0行有4個(gè)整型元素,所以a+1為1008,a+2也就為1016。如圖所示
_______________
(1000) ____ | 0 | 1 | 2 | 3 |
|___|___|___|___|
(1008) ____ | 4 | 5 | 6 | 7 |
|___|___|___|___|
(1016) ____ | 8 | 9 | 10| 11|
|___|___|___|___|
既然我們把a[0],a[1],a[2]看成是一維數(shù)組名,可以認(rèn)為它們分別代表它們所對應(yīng)的數(shù)組的首地址,也就是講,a[0]代表第 0 行中第 0 列元素的地址,即&a[0][0], a[1]是第1行中第0列元素的地址,即&a[1][0],根據(jù)地址運(yùn)算規(guī)則,a[0]+1即代表第0行第1列元素的地址,即&a[0][1],一般而言,a[i]+j即代表第i行第j列元素的地址, 即&a[i][j]。
另外,在二維數(shù)組中,我們還可用指針的形式來表示各元素的地址。如前所述,a[0]與*(a+0)等價(jià),a[1]與*(a+1)等價(jià),因此a[i]+j就與*(a+i)+j等價(jià),它表示數(shù)組元素a[i][j]的地址。
因此,二維數(shù)組元素a[i][j]可表示成*(a[i]+j)或*(*(a+i)+j),它們都與a[i][j]等價(jià),或者還可寫成(*(a+i))[j]。
另外, 要補(bǔ)充說明一下, 果你編寫一個(gè)程序輸出打印a和*a,你可發(fā)現(xiàn)它們的值是相同的,這是為什么呢? 我們可這樣來理解:
首先,為了說明問題,我們把二維數(shù)組人為地看成由三個(gè)數(shù)組元素a[0],a[1],a[2]組成,將a[0],a[1],a[2]看成是數(shù)組名它們又分別是由4個(gè)元素組成的一維數(shù)組。因此,a表示數(shù)組第0行的地址, 而*a即為a[0], 它是數(shù)組名, 當(dāng)然還是地址,它就是數(shù)組第0 行第0 列元素的地址。
2.指向一個(gè)由n個(gè)元素所組成的數(shù)組指針
在Turbo C中, 可定義如下的指針變量:
int (*p)[3];
指針p為指向一個(gè)由3個(gè)元素所組成的整型數(shù)組指針。在定義中,圓括號是不能少的, 否則它是指針數(shù)組, 這將在后面介紹。這種數(shù)組的指針不同于前面介紹的整型指針,當(dāng)整型指針指向一個(gè)整型數(shù)組的元素時(shí),進(jìn)行指針(地址)加1運(yùn)算,表示指向數(shù)組的下一個(gè)元素,此時(shí)地址值增加了2(因?yàn)榉糯笠蜃訛?SPAN lang=EN-US>2),而如上所定義的指向一個(gè)由3個(gè)元素組成的數(shù)組指針,進(jìn)行地址加1運(yùn)算時(shí),其地址值增加了6(放大因子為2x3=6),
這種數(shù)組指針在Turbo C中用得較少,但在處理二維數(shù)組時(shí), 還是很方便的。例如:
int a[3][4], (*p)[4];
p=a;
開始時(shí)p指向二維數(shù)組第0行,當(dāng)進(jìn)行p+1運(yùn)算時(shí),根據(jù)地址運(yùn)算規(guī)則,此時(shí)放大因子為4x2=8,所以此時(shí)正好指向二維數(shù)組的第1行。和二維數(shù)組元素地址計(jì)算的規(guī)則一樣,*p+1指向a[0][1],*(p+i)+j則指向數(shù)組元素a[i][j]。
例:
int a[3][4]={
{1,3,5,7},
{9,11,13,15},
{17,19,21,23}
};
main()
{
int i,(*b)[4];
b=a+1; /* b指向二維數(shù)組的第1行, 此時(shí)*b[0]是a[1][0] */
for(i=1;i<=4;b=b[0]+2,i++) /* 修改b的指向, 每次增加2 */
printf("%d\t",*b[0]);
printf("\n");
for(i=0; i<3; i++)
{
b=a+i; /* 修改b的指向,每次跳過二維數(shù)組的一行 */
printf("%d\t",*(b[i]+1));
}
printf ("\n");
}
程序運(yùn)行結(jié)果如下:
9 13 17 21
3 11 19
三、字符指針
我們已經(jīng)知道,字符串常量是由雙引號括起來的字符序列,例如:
"a string"
就是一個(gè)字符串常量,該字符串中因?yàn)樽址?SPAN lang=EN-US>a后面還有一個(gè)空格字符,所以它由8個(gè)字符序列組成。在程序中如出現(xiàn)字符串常量C編譯程序就給字符串常量按排一存貯區(qū)域,這個(gè)區(qū)域是靜態(tài)的,在整個(gè)程序運(yùn)行的過程中始終占用, 平時(shí)所講的字符串常量的長度是指該字符串的字符個(gè)數(shù), 但在按排存貯區(qū)域時(shí), C 編譯程序還自動(dòng)給該字符串序列的末尾加上一個(gè)空字符'\0',用來標(biāo)志字符串的結(jié)束,因此一個(gè)字符串常量所占的存貯區(qū)域的字節(jié)數(shù)總比它的字符個(gè)數(shù)多一個(gè)字節(jié)。
Turbo C中操作一個(gè)字符串常量的方法有:
(1).把字符串常量存放在一個(gè)字符數(shù)組之中, 例如:
char s[]="a string";
數(shù)組s共有9個(gè)元素所組成,其中s[8]中的內(nèi)容是'\0'。實(shí)際上,在字符數(shù)組定義的過程中,編譯程序直接把字符串復(fù)寫到數(shù)組中,即對數(shù)組s初始化。
(2).用字符指針指向字符串,然后通過字符指針來訪問字符串存貯區(qū)域。當(dāng)字符串常量在表達(dá)式中出現(xiàn)時(shí),
根據(jù)數(shù)組的類型轉(zhuǎn)換規(guī)則,它被轉(zhuǎn)換成字符指針。因此,若我們定義了一字符指針cp:
char *cp;
于是可用:
cp="a string";
使cp指向字符串常量中的第0號字符a, 如圖所示。
___________________________________
CP ----- | a | | s | t | r | i | n | g | \0|
|___|___|___|___|___|___|___|___|___|
以后我們可通過cp來訪問這一存貯區(qū)域, 如*cp或cp[0]就是字符a,而cp[i]或*(cp+i)就相當(dāng)于字符串的第i號字符,但企圖通過指針來修改字符串常量的行為是沒有意義的。
四、指針數(shù)組
因?yàn)橹羔樖亲兞?SPAN lang=EN-US>,因此可設(shè)想用指向同一數(shù)據(jù)類型的指針來構(gòu)成一個(gè)數(shù)組, 這就是指針數(shù)組。數(shù)組中的每個(gè)元素都是指針變量,根據(jù)數(shù)組的定義,指針數(shù)組中每個(gè)元素都為指向同一數(shù)據(jù)類型的指針。指針數(shù)組的定義格式為:
類型標(biāo)識 *數(shù)組名[整型常量表達(dá)式];
例如:
int *a[10];
定義了一個(gè)指針數(shù)組,數(shù)組中的每個(gè)元素都是指向整型量的指針,該數(shù)組由10個(gè)元素組成,即a[0],a[1],a[2], ..., a[9],它們均為指針變量。a為該指針數(shù)組名,和數(shù)組一樣,a是常量,不能對它進(jìn)行增量運(yùn)算。a為指針數(shù)組元素a[0]的地址,a+i為a[i]的地址,*a就是a[0],*(a+i)就是a[i]。
為什么要定義和使用指針數(shù)組呢?主要是由于指針數(shù)組對處理字符串提供了更大的方便和靈活,使用二維數(shù)組對處理長度不等的正文效率低,而指針數(shù)組由于其中每個(gè)元素都為指針變量,因此通過地址運(yùn)算來操作正文行是十分方便的。
指針數(shù)組和一般數(shù)組一樣,允許指針數(shù)組在定義時(shí)初始化,但由于指針數(shù)組的每個(gè)元素是指針變量,它只能存放地址,所以對指向字符串的指針數(shù)組在說明賦初值時(shí),是把存放字符串的首地址賦給指針數(shù)組的對應(yīng)元素,
例如下面是一個(gè)書寫函數(shù)month_name(n),函數(shù)返回一個(gè)指向包含第n月名字的字符指針(關(guān)于函數(shù)指針和指針函數(shù),下一節(jié)將專門介紹)。
例: 打印1月至12月的月名:
char *month_name(int n)
{
static char *name[]={
"Illegal month",
"January",
"February",
"March",
"April",
"May",
"June",
"July",
"August",
"September",
"October",
"November",
"December"
};
return((n<1||n>12)?name[0]:name[n]);
}
main()
{
int i;
for(i=0; i<13; i++)
printf("%s\n", month_name(i));
}
對于指針這一節(jié),一定要多練習(xí)一些題。指針是一個(gè)很重要的概念,必須多接觸實(shí)際的問題才能掌握它。