C語言入門(十八)數組和指針

數組和指針 

前言:

指針和數組有着密切的關係,任何能由數組下標完成的操作也都可用指針來實現,但程序中使用指針可使代碼更緊湊、更靈活。

一、指向數組元素的指針
我們定義一個整型數組和一個指向整型的指針變量:
int a[10], *p; 
和前面介紹過的方法相同,可以使整型指針p指向數組中任何一個元素,假定給出賦值運算
p=&a[0]; 
此時,p指向數組中的第0號元素,即a[0],指針變量p中包含了數組元素a[0]的地址,由於數組元素在內存中是連續存放的,因此,我們就可以通過指針變量p及其有關運算間接訪問數組中的任何一個元素。 
Turbo C中,數組名是數組的第0號元素的地址,因此下面兩個語句是等價的
p=&a[0]; 
p=a; 

根據地址運算規則,a+1爲a[1]的地址,a+i就爲a[i]的地址。
下面我們用指針給出數組元素的地址和內容的幾種表示形式:
(1). p+i和a+i均表示a[i]的地址, 或者講,它們均指向數組第i號元素, 即指向a[i]。 
(2). *(p+i)和*(a+i)都表示p+i和a+i所指對象的內容,即爲a[i]。 
(3). 指向數組元素的指針, 也可以表示成數組的形式,也就是說,它允許指針變量帶下標, 如p[i]與*(p+i)等價。

假若: p=a+5; 
則p[2]就相當於*(p+2), 由於p指向a[5], 所以p[2]就相當於a[7]。而p[-3]就相當於*(p-3), 它表示a[2]。

二、指向二維數組的指針 
1.二維數組元素的地址 
爲了說明問題, 我們定義以下二維數組:
int a[3][4]={{0,1,2,3}, {4,5,6,7}, {8,9,10,11}}; 
a爲二維數組名,此數組有3行4列, 共12個元素。但也可這樣來理解,數組a由三個元素組成:a[0],a[1],a[2]。而每個元素又是一個一維數組, 且都含有4個元素(相當於4列),例如,a[0]所代表的一維數組所包含的 4 個元素爲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|
|______| |___|___|___|___|
但從二維數組的角度來看,a代表二維數組的首地址,當然也可看成是二維數組第0行的首地址。a+1就代表第1行的首地址,a+2就代表第2行的首地址。如果此二維數組的首地址爲1000,由於第0行有4個整型元素,所以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]看成是一維數組名,可以認爲它們分別代表它們所對應的數組的首地址,也就是講,a[0]代表第 0 行中第 0 列元素的地址,即&a[0][0], a[1]是第1行中第0列元素的地址,即&a[1][0],根據地址運算規則,a[0]+1即代表第0行第1列元素的地址,即&a[0][1],一般而言,a[i]+j即代表第i行第j列元素的地址, 即&a[i][j]。
另外,在二維數組中,我們還可用指針的形式來表示各元素的地址。如前所述,a[0]與*(a+0)等價,a[1]與*(a+1)等價,因此a[i]+j就與*(a+i)+j等價,它表示數組元素a[i][j]的地址。
因此,二維數組元素a[i][j]可表示成*(a[i]+j)或*(*(a+i)+j),它們都與a[i][j]等價,或者還可寫成(*(a+i))[j]。 

另外, 要補充說明一下, 果你編寫一個程序輸出打印a和*a,你可發現它們的值是相同的,這是爲什麼呢? 我們可這樣來理解: 
首先,爲了說明問題,我們把二維數組人爲地看成由三個數組元素a[0],a[1],a[2]組成,將a[0],a[1],a[2]看成是數組名它們又分別是由4個元素組成的一維數組。因此,a表示數組第0行的地址, 而*a即爲a[0], 它是數組名, 當然還是地址,它就是數組第0 行第0 列元素的地址。
2.指向一個由n個元素所組成的數組指針
在Turbo C中, 可定義如下的指針變量: 
int (*p)[3]; 
指針p爲指向一個由3個元素所組成的整型數組指針。在定義中,圓括號是不能少的, 否則它是指針數組, 這將在後面介紹。這種數組的指針不同於前面介紹的整型指針,當整型指針指向一個整型數組的元素時,進行指針(地址)加1運算,表示指向數組的下一個元素,此時地址值增加了2(因爲放大因子爲2),而如上所定義的指向一個由3個元素組成的數組指針,進行地址加1運算時,其地址值增加了6(放大因子爲2x3=6), 
這種數組指針在Turbo C中用得較少,但在處理二維數組時, 還是很方便的。例如: 
int a[3][4], (*p)[4]; 
p=a; 
開始時p指向二維數組第0行,當進行p+1運算時,根據地址運算規則,此時放大因子爲4x2=8,所以此時正好指向二維數組的第1行。和二維數組元素地址計算的規則一樣,*p+1指向a[0][1],*(p+i)+j則指向數組元素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指向二維數組的第1行, 此時*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的指向,每次跳過二維數組的一行 */ 
printf(%d\t,*(b[i]+1)); 
} 
printf (\n); 
}
程序運行結果如下: 
9 13 17 21 
3 11 19 

三、字符指針 
我們已經知道,字符串常量是由雙引號括起來的字符序列,例如: 
a string 
就是一個字符串常量,該字符串中因爲字符a後面還有一個空格字符,所以它由8個字符序列組成。在程序中如出現字符串常量C編譯程序就給字符串常量按排一存貯區域,這個區域是靜態的,在整個程序運行的過程中始終佔用, 平時所講的字符串常量的長度是指該字符串的字符個數, 但在按排存貯區域時, C 編譯程序還自動給該字符串序列的末尾加上一個空字符'\0',用來標誌字符串的結束,因此一個字符串常量所佔的存貯區域的字節數總比它的字符個數多一個字節。
Turbo C中操作一個字符串常量的方法有: 
(1).把字符串常量存放在一個字符數組之中, 例如:
char s[]=a string; 
數組s共有9個元素所組成,其中s[8]中的內容是'\0'。實際上,在字符數組定義的過程中,編譯程序直接把字符串複寫到數組中,即對數組s初始化。
(2).用字符指針指向字符串,然後通過字符指針來訪問字符串存貯區域。當字符串常量在表達式中出現時, 
根據數組的類型轉換規則,它被轉換成字符指針。因此,若我們定義了一字符指針cp: 
char *cp; 
於是可用: 
cp=a string; 
使cp指向字符串常量中的第0號字符a, 如圖所示。 
___________________________________
CP ----- | a | | s | t | r | i | n | g | \0| 
|___|___|___|___|___|___|___|___|___| 

以後我們可通過cp來訪問這一存貯區域, 如*cp或cp[0]就是字符a,而cp[i]或*(cp+i)就相當於字符串的第i號字符,但企圖通過指針來修改字符串常量的行爲是沒有意義的。 

四、指針數組 
因爲指針是變量,因此可設想用指向同一數據類型的指針來構成一個數組, 這就是指針數組。數組中的每個元素都是指針變量,根據數組的定義,指針數組中每個元素都爲指向同一數據類型的指針。指針數組的定義格式爲: 
類型標識 *數組名[整型常量表達式]; 
例如: 
int *a[10]; 
定義了一個指針數組,數組中的每個元素都是指向整型量的指針,該數組由10個元素組成,即a[0],a[1],a[2], ..., a[9],它們均爲指針變量。a爲該指針數組名,和數組一樣,a是常量,不能對它進行增量運算。a爲指針數組元素a[0]的地址,a+i爲a[i]的地址,*a就是a[0],*(a+i)就是a[i]。
爲什麼要定義和使用指針數組呢?主要是由於指針數組對處理字符串提供了更大的方便和靈活,使用二維數組對處理長度不等的正文效率低,而指針數組由於其中每個元素都爲指針變量,因此通過地址運算來操作正文行是十分方便的。
指針數組和一般數組一樣,允許指針數組在定義時初始化,但由於指針數組的每個元素是指針變量,它只能存放地址,所以對指向字符串的指針數組在說明賦初值時,是把存放字符串的首地址賦給指針數組的對應元素, 
例如下面是一個書寫函數month_name(n),函數返回一個指向包含第n月名字的字符指針(關於函數指針和指針函數,下一節將專門介紹)。 

例: 打印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)); 
}

對於指針這一節,一定要多練習一些題。指針是一個很重要的概念,必須多接觸實際的問題才能掌握它。
發佈了101 篇原創文章 · 獲贊 540 · 訪問量 74萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章