嵌入式C語言基礎編程—5年程式設計師給你講指針,精品講解

it百科大學堂 發佈 2020-05-17T01:48:04+00:00

本文主要是對C基礎編程關於指針的初步講解,後續會深入講解C高級相關的概念(C大神可先略過)。 本人近期會陸續上傳IT編程相關的資料和視頻教程,可以關注一下互相交流:C C++ Java python linux ARM 嵌入式 物聯網等。

本文主要是對C基礎編程關於指針的初步講解,後續會深入講解C高級相關的概念(C大神可先略過)。 本人近期會陸續上傳IT編程相關的資料和視頻教程,可以關注一下互相交流:C C++ Java python linux ARM 嵌入式 物聯網等。想學編程的朋友進入主頁即可看到相關教程和資料。

本文的主要內容:

1地址指針的基本概念

1.2變量的指針和指向變量的指針變量

1.3定義一個指針變量

1.4指針變量的引用

2指針變量作為函數參數

3數組指針和指向數組的指針變量

3.1指向數組元素的指針

3.2通過指針引用數組元素

4指向多維數組的指針和指針變量

5指針數組和指向指針的指針

5.1指針數組的概念

5.2指向指針的指針

6.1有關指針的數據類型的小結

6.2指針運算的小結

指針是C語言中廣泛使用的一種數據類型。運用指針編程是C語言最主要的風格之一。 利用指針變量可以表示各種數據結構;能很方便地使用數組和字符串;並能象彙編語言一樣 處理內存地址,從而編出精練而高效的程序。指針極大地豐富了C語言的功能。學習指針是 學習C語言中最重要的一環,能否正確理解和使用指針是我們是否掌握C語言的一個標誌。 同時,指針也是C語言中最為困難的一部分,在學習中除了要正確理解基本概念,還必須要 多編程,上機調試。只要做到這些,指針也是不難掌握的。

8.1 地址指針的基本概念


在計算機中,所有的數據都是存放在存儲器中的。一般把存儲器中的一個字節稱為一個內存單元,不同的數據類型所占用的內存單元數不等,如整型量占 2 個單元,字符量占 1 個單元等,在前面已有詳細的介紹。為了正確地訪問這些內存單元,必須為每個內存單元編上號。根據一個內存單元的編號即可準確地找到該內存單元。內存單元的編號也叫做地址。 既然根據內存單元的編號或地址就可以找到所需的內存單元,所以通常也把這個地址稱為指針。 內存單元的指針和內存單元的內容是兩個不同的概念。可以用一個通俗的例子來說明它們之間的關係。我們到銀行去存取款時, 銀行工作人員將根據我們的帳號去找我們的存款單, 找到之後在存單上寫入存款、取款的金額。在這裡,帳號就是存單的指針, 存款數是存單的內容。對於一個內存單元來說,單元的地址即為指針,其中存放的數據才是該單元的內容。在C語言中,允許用一個變量來存放指針,這種變量稱為指針變量。因此,一個指針變量的值 就是某個內存單元的地址或稱為某內存單元的指針。

圖中,設有字符變量 C,其內容為"K"(ASCII 碼為十進位數 75),C 占用了 011A 號單元(地址用十六進數表示)。設有指針變量 P,內容為 011A,這種情況我們稱為 P 指向變量 C, 或說 P 是指向變量 C 的指針。

嚴格地說,一個指針是一個地址,是一個常量。而一個指針變量卻可以被賦予不同的指針值,是變量。但常把指針變量簡稱為指針。為了避免混淆,我們中約定:"指針"是指地址,是常量,"指針變量"是指取值為地址的變量。定義指針的目的是為了通過指針去訪問內存單元。

既然指針變量的值是一個地址,那麼這個地址不僅可以是變量的地址,也可以是其它數 據結構的地址。在一個指針變量中存放一個數組或一個函數的首地址有何意義呢? 因為數組或函數都是連續存放的。通過訪問指針變量取得了數組或函數的首地址,也就找到了該數組 或函數。這樣一來,凡是出現數組,函數的地方都可以用一個指針變量來表示,只要該指針 變量中賦予數組或函數的首地址即可。這樣做,將會使程序的概念十分清楚,程序本身也精 練,高效。在C語言中,一種數據類型或數據結構往往都占有一組連續的內存單元。 用"地址"這個概念並不能很好地描述一種數據類型或數據結構,而"指針"雖然實際上也是一個地址,但它卻是一個數據結構的首地址,它是"指向"一個數據結構的,因而概念更為清楚, 表示更為明確。 這也是引入"指針"概念的一個重要原因。

8.1.2 變量的指針和指向變量的指針變量


變量的指針就是變量的地址。存放變量地址的變量是指針變量。即在C語言中,允許用 一個變量來存放指針,這種變量稱為指針變量。因此,一個指針變量的值就是某個變量的地 址或稱為某變量的指針。

為了表示指針變量和它所指向的變量之間的關係,在程序中用"*"符號表示"指向",例如,i_pointer 代表指針變量,而*i_pointer 是 i_pointer 所指向的變量。

因此,下面兩個語句作用相同: i=3;

*i_pointer=3;

第二個語句的含義是將 3 賦給指針變量 i_pointer 所指向的變量。

8.1.3 定義一個指針變量


對指針變量的定義包括三個內容:

(1) 指針類型說明,即定義變量為一個指針變量;

(2) 指針變量名;

(3) 變量值(指針)所指向的變量的數據類型。其一般形式為:

類型說明符 *變量名; 其中,*表示這是一個指針變量,變量名即為定義的指針變量名,類型說明符表示本指針變量所指向的變量的數據類型。例如: int *p1;

表示 p1 是一個指針變量,它的值是某個整型變量的地址。或者說 p1 指向一個整型變量。至於 p1 究竟指向哪一個整型變量,應由向 p1 賦予的地址來決定。

再如:

int *p2; /*p2 是指向整型變量的指針變量*/ float *p3; /*p3 是指向浮點變量的指針變量*/ char *p4; /*p4 是指向字符變量的指針變量*/

應該注意的是,一個指針變量只能指向同類型的變量,如 P3 只能指向浮點變量,不能時而指向一個浮點變量,時而又指向一個字符變量。

8.1.4 指針變量的引用


指針變量同普通變量一樣,使用之前不僅要定義說明,而且必須賦予具體的值。未經賦 值的指針變量不能使用,否則將造成系統混亂,甚至死機。指針變量的賦值只能賦予地址, 決不能賦予任何其它數據,否則將引起錯誤。在C語言中,變量的地址是由編譯系統分配的, 對用戶完全透明,用戶不知道變量的具體地址。

兩個有關的運算符:

1) &:取地址運算符。

2) *:指針運算符(或稱"間接訪問" 運算符)。

C語言中提供了地址運算符&來表示變量的地址。其一般形式為:

&變量名;

如&a 表示變量 a 的地址,&b 表示變量 b 的地址。變量本身必須預先說明。

設有指向整型變量的指針變量 p,如要把整型變量 a 的地址賦予 p 可以有以下兩種方式:

(1) 指針變量初始化的方法

int a;

int *p=&a;

(2) 賦值語句的方法int a;

int *p; p=&a;

不允許把一個數賦予指針變量,故下面的賦值是錯誤的:

int *p;

p=1000;

被賦值的指針變量前不能再加"*"說明符,如寫為*p=&a 也是錯誤的。假設:

int i=200, x; int *ip;

我們定義了兩個整型變量 i,x,還定義了一個指向整型數的指針變量 ip。i,x 中可存放整數,而 ip 中只能存放整型變量的地址。我們可以把 i 的地址賦給 ip:

ip=&i;

此時指針變量ip 指向整型變量i,假設變量i 的地址為1800,這個賦值可形象理解為下圖所示的聯繫。

以後我們便可以通過指針變量 ip 間接訪問變量 i,例如: x=*ip;

運算符*訪問以 ip 為地址的存貯區域,而 ip 中存放的是變量 i 的地址,因此,*ip 訪問的是地址為 1800 的存貯區域(因為是整數,實際上是從 1800 開始的兩個字節),它就是 i 所占用的存貯區域, 所以上面的賦值表達式等價於

x=i;

另外,指針變量和一般變量一樣,存放在它們之中的值是可以改變的,也就是說可以改變它們的指向,假設

int i,j,*p1,*p2; i='a';

j='b'; p1=&i; p2=&j;

則建立如下圖所示的聯繫:


這時賦值表達式: p2=p1

就使 p2 與 p1 指向同一對象 i,此時*p2 就等價於 i,而不是 j,圖所示:


如果執行如下表達式:

*p2=*p1;

則表示把 p1 指向的內容賦給 p2 所指的區域, 此時就變成圖所示

通過指針訪問它所指向的一個變量是以間接訪問的形式進行的,所以比直接訪問一個變量要費時間,而且不直觀,因為通過指針要訪問哪一個變量,取決於指針的值(即指向),例如

"*p2=*p1;"實際上就是"j=i;",前者不僅速度慢而且目的不明。但由於指針是變量,我們可以 通過改變它們的指向,以間接訪問不同的變量,這給程式設計師帶來靈活性,也使程序代碼編寫得更為簡潔和有效。

指針變量可出現在表達式中, 設int x,y,*px=&x;

指針變量 px 指向整數 x,則*px 可出現在 x 能出現的任何地方。例如: y=*px+5; /*表示把 x 的內容加 5 並賦給 y*/

y=++*px; /*px 的內容加上 1 之後賦給 y,++*px 相當於++(*px)*/ y=*px++; /*相當於 y=*px; px++*/

【例 8.1】

main()

{ int a,b;

int *pointer_1, *pointer_2; a=100;b=10;

pointer_1=&a; pointer_2=&b; printf("%d,%d\n",a,b);

printf("%d,%d\n",*pointer_1, *pointer_2);

}

對程序的說明:

1) 在開頭處雖然定義了兩個指針變量 pointer_1 和 pointer_2,但它們並未指向任何一個整型變量。只是提供兩個指針變量,規定它們可以指向整型變量。程序第 5、6 行的作用就是使 pointer_1 指向 a,pointer_2 指向 b。

2) 最後一行的*pointer_1 和*pointer_2 就是變量 a 和 b。最後兩個 printf 函數作用是相同的。

3) 程序中有兩處出現*pointer_1 和*pointer_2,請區分它們的不同含義。

4) 程序第 5、6 行的"pointer_1=&a"和 "pointer_2=&b"不能寫成"*pointer_1=&a"和

"*pointer_2=&b"。

請對下面再的關於"&"和"*"的問題進行考慮:

1) 如果已經執行了"pointer_1=&a;"語句,則&*pointer_1 是什麼含義?

2) *&a 含義是什麼?

3) (pointer_1)++和 pointer_1++的區別?

8.2 指針變量作為函數參數


函數的參數不僅可以是整型、實型、字符型等數據,還可以是指針類型。它的作用是將 一個變量的地址傳送到另一個函數中。

【例 8.2】輸入的兩個整數按大小順序輸出。今用函數處理,而且用指針類型的數據作函數參數。

swap(int *p1,int *p2)

{int temp; temp=*p1;

*p1=*p2;

*p2=temp;

}

main()

{

int a,b;

int *pointer_1,*pointer_2; scanf("%d,%d",&a,&b); pointer_1=&a;pointer_2=&b; if(a<b) swap(pointer_1,pointer_2); printf("\n%d,%d\n",a,b);

}

對程序的說明:

swap 是用戶定義的函數,它的作用是交換兩個變量(a 和 b)的值。swap 函數的形參 p1、

p2 是指針變量。程序運行時,先執行 main 函數,輸入 a 和 b 的值。然後將 a 和 b 的地址分別賦給指針變量 pointer_1 和 pointer_2,使 pointer_1 指向 a,pointer_2 指向 b。

接著執行 if 語句,由於 a〈b,因此執行 swap 函數。注意實參 pointer_1 和 pointer_2 是指針變量,在函數調用時,將實參變量的值傳遞給形參變量。採取的依然是"值傳遞"方式。因此虛實結合後形參 p1 的值為&a,p2 的值為&b。這時 p1 和 pointer_1 指向變量 a,p2 和pointer_2 指向變量 b。

接著執行執行 swap 函數的函數體使*p1 和*p2 的值互換,也就是使 a 和 b 的值互換。

函數調用結束後,p1 和 p2 不復存在(已釋放)如圖。

最後在 main 函數中輸出的 a 和 b 的值是已經過交換的值。

請注意交換*p1 和*p2 的值是如何實現的。請找出下列程序段的錯誤: swap(int *p1,int *p2)

{int *temp;

*temp=*p1; /*此語句有問題*/

*p1=*p2;

*p2=temp;

}

請考慮下面的函數能否實現實現 a 和 b 互換。swap(int x,int y)

{int temp; temp=x; x=y; y=temp;

}

如果在 main 函數中用"swap(a,b);"調用 swap 函數,會有什麼結果呢?請看下圖所示。

7.2.1 指針變量的運算

1) 賦值運算:指針變量的賦值運算有以下幾種形式。

1 指針變量初始化賦值,前面已作介紹。

2 把一個變量的地址賦予指向相同數據類型的指針變量。 例如:

int a,*pa;

pa=&a; /*把整型變量 a 的地址賦予整型指針變量 pa*/

3 把一個指針變量的值賦予指向相同類型變量的另一個指針變量。 如:

int a,*pa=&a,*pb;

pb=pa; /*把 a 的地址賦予指針變量 pb*/

由於 pa,pb 均為指向整型變量的指針變量,因此可以相互賦值。

4 把數組的首地址賦予指向數組的指針變量。 例如:

int a[5],*pa; pa=a;

(數組名表示數組的首地址,故可賦予指向數組的指針變量 pa) 也可寫為:

pa=&a[0]; /* 數 組 第 一 個 元 素 的 地 址 也 是 整 個 數 組 的 首 地 址 ,也可賦予 pa*/

當然也可採取初始化賦值的方法: int a[5],*pa=a;

5把字符串的首地址賦予指向字符類型的指針變量。 例如:

char *pc;

pc="C Language";

或用初始化賦值的方法寫為: char *pc="C Language";

這裡應說明的是並不是把整個字符串裝入指針變量,而是把存放該字符串的字符數 組的首地址裝入指針變量。在後面還將詳細介紹。

6把函數的入口地址賦予指向函數的指針變量。 例如:

int (*pf)();

pf=f; /*f 為函數名*/

2) 加減算術運算

對於指向數組的指針變量,可以加上或減去一個整數 n。設 pa 是指向數組 a 的指針變量, 則 pa+n,pa-n,pa++,++pa,pa--,--pa 運算都是合法的。指針變量加或減一個整數 n 的意義是把指針指向的當前位置(指向某數組元素)向前或向後移動 n 個位置。應該注意,數組指針變量向前或向後移動一個位置和地址加 1 或減 1 在概念上是不同的。因為數組可以有不同的類型,各種類型的數組元素所占的字節長度是不同的。如指針變量加 1,即向後移動 1 個位置表示指針變量指向下一個數據元素的首地址。而不是在原地址基礎上加 1。例如:

int a[5],*pa;

pa=a; /*pa 指向數組 a,也是指向 a[0]*/ pa=pa+2; /*pa 指向 a[2],即 pa 的值為&pa[2]*/

指針變量的加減運算只能對數組指針變量進行,對指向其它類型變量的指針變量作加減 運算是毫無意義的。

3) 兩個指針變量之間的運算:只有指向同一數組的兩個指針變量之間才能進行運算,否則 運算毫無意義。

【例 8.3】

main(){

int a=10,b=20,s,t,*pa,*pb; /*說明 pa,pb 為整型指針變量*/ pa=&a; /*給指針變量 pa 賦值,pa 指向變量 a*/

pb=&b; /*給指針變量 pb 賦值,pb 指向變量 b*/

s=*pa+*pb; /*求 a+b 之和,(*pa 就是 a,*pb 就是 b)*/

t=*pa**pb; /*本行是求 a*b 之積*/ printf("a=%d\nb=%d\na+b=%d\na*b=%d\n",a,b,a+b,a*b); printf("s=%d\nt=%d\n",s,t);

}

8.3 數組指針和指向數組的指針變量


一個變量有一個地址,一個數組包含若干元素,每個數組元素都在內存中占用存儲單元, 它們都有相應的地址。所謂數組的指針是指數組的起始地址,數組元素的指針是數組元素的地址。

8.3.1 指向數組元素的指針


一個數組是由連續的一塊內存單元組成的。數組名就是這塊連續內存單元的首地址。一 個數組也是由各個數組元素(下標變量)組成的。每個數組元素按其類型不同占有幾個連續的 內存單元。一個數組元素的首地址也是指它所占有的幾個內存單元的首地址。

定義一個指向數組元素的指針變量的方法,與以前介紹的指針變量相同。 例如:

int a[10]; /*定義 a 為包含 10 個整型數據的數組*/ int *p; /*定義 p 為指向整型變量的指針*/

應當注意,因為數組為 int 型,所以指針變量也應為指向 int 型的指針變量。下面是對指針變量賦值:

p=&a[0];

把 a[0]元素的地址賦給指針變量 p。也就是說,p 指向 a 數組的第 0 號元素。


C 語言規定,數組名代表數組的首地址,也就是第 0 號元素的地址。因此,下面兩個語句等價:

p=&a[0]; p=a;

在定義指針變量時可以賦給初值: int *p=&a[0];

它等效於:

int *p; p=&a[0];

當然定義時也可以寫成: int *p=a;

從圖中我們可以看出有以下關係:

p,a,&a[0]均指向同一單元,它們是數組 a 的首地址,也是 0 號元素 a[0]的首地址。應該說明的是 p 是變量,而 a,&a[0]都是常量。在編程時應予以注意。

數組指針變量說明的一般形式為: 類型說明符 *指針變量名;

其中類型說明符表示所指數組的類型。從一般形式可以看出指向數組的指針變量和指向普通 變量的指針變量的說明是相同的。

8.3.2 通過指針引用數組元素


C 語言規定:如果指針變量 p 已指向數組中的一個元素,則 p+1 指向同一數組中的下一個元素。

引入指針變量後,就可以用兩種方法來訪問數組元素了。 如果 p 的初值為&a[0],則:

1) p+i 和 a+i 就是 a[i]的地址,或者說它們指向 a 數組的第 i 個元素。

2) *(p+i)或*(a+i)就是 p+i 或 a+i 所指向的數組元素,即 a[i]。例如,*(p+5)或*(a+5)就是 a[5]。

3) 指向數組的指針變量也可以帶下標,如 p[i]與*(p+i)等價。根據以上敘述,引用一個數組元素可以用:

1) 下標法,即用 a[i]形式訪問數組元素。在前面介紹數組時都是採用這種方法。

2) 指針法,即採用*(a+i)或*(p+i)形式,用間接訪問的方法來訪問數組元素,其中 a 是數組名,p 是指向數組的指針變量,其處值 p=a。

【例 8.4】輸出數組中的全部元素。(通過數組名計算元素的地址,找出元素的值) main(){

int a[10],i; for(i=0;i<10;i++)

*(a+i)=i; for(i=0;i<10;i++)

printf("a[%d]=%d\n",i,*(a+i));

}

8.4 指向多維數組的指針和指針變量


本小節以二維數組為例介紹多維數組的指針變量。

1. 多維數組的地址

設有整型二維數組 a[3][4]如下:

它的定義為:

int a[3][4]={{0,1,2,3},{4,5,6,7},{8,9,10,11}}

設數組 a 的首地址為 1000,各下標變量的首地址及其值如圖所示。

前面介紹過,C語言允許把一個二維數組分解為多個一維數組來處理。因此數組 a 可分解為三個一維數組,即 a[0],a[1],a[2]。每一個一維數組又含有四個元素。


例如 a[0]數組,含有 a[0][0],a[0][1],a[0][2],a[0][3]四個元素。數組及數組元素的地址表示如下:

從二維數組的角度來看,a 是二維數組名,a 代表整個二維數組的首地址,也是二維數組

0 行的首地址,等於 1000。a+1 代表第一行的首地址,等於 1008。如圖:


a[0]是第一個一維數組的數組名和首地址,因此也為 1000。*(a+0)或*a 是與 a[0]等效的, 它表示一維數組 a[0]0 號元素的首地址,也為 1000。&a[0][0]是二維數組 a 的 0 行 0 列元素首地址,同樣是 1000。因此,a,a[0],*(a+0),*a,&a[0][0]是相等的。

同理,a+1 是二維數組 1 行的首地址,等於 1008。a[1]是第二個一維數組的數組名和首地址, 因此也為 1008。&a[1][0]是二維數組 a 的 1 行 0 列元素地址, 也是 1008。因此

a+1,a[1],*(a+1),&a[1][0]是等同的。

由此可得出:a+i,a[i],*(a+i),&a[i][0]是等同的。

此外,&a[i]和 a[i]也是等同的。因為在二維數組中不能把&a[i]理解為元素 a[i]的地址,不存在元素 a[i]。C語言規定,它是一種地址計算方法,表示數組 a 第 i 行首地址。由此, 我們得出:a[i],&a[i],*(a+i)和 a+i 也都是等同的。

另外,a[0]也可以看成是 a[0]+0,是一維數組 a[0]的 0 號元素的首地址,而 a[0]+1 則是 a[0]的 1 號元素首地址,由此可得出 a[i]+j 則是一維數組 a[i]的 j 號元素首地址,它等於&a[i][j]。

由 a[i]=*(a+i)得 a[i]+j=*(a+i)+j。由於*(a+i)+j 是二維數組 a 的 i 行 j 列元素的首地址,所以,該元素的值等於*(*(a+i)+j)。

【例 8.5】

main(){

int a[3][4]={0,1,2,3,4,5,6,7,8,9,10,11};

printf("%d,",a);

printf("%d,",*a);

printf("%d,",a[0]);

printf("%d,",&a[0]);

printf("%d\n",&a[0][0]); printf("%d,",a+1);

printf("%d,",*(a+1));

printf("%d,",a[1]);

printf("%d,",&a[1]);

printf("%d\n",&a[1][0]); printf("%d,",a+2);

printf("%d,",*(a+2));

printf("%d,",a[2]);

printf("%d,",&a[2]);

printf("%d\n",&a[2][0]);

printf("%d,",a[1]+1);

printf("%d\n",*(a+1)+1); printf("%d,%d\n",*(a[1]+1),*(*(a+1)+1));

}

2. 指向多維數組的指針變量

把二維數組 a 分解為一維數組 a[0],a[1],a[2]之後,設 p 為指向二維數組的指針變量。可定義為:

int (*p)[4]

它表示 p 是一個指針變量,它指向包含 4 個元素的一維數組。若指向第一個一維數組

a[0],其值等於 a,a[0],或&a[0][0]等。而 p+i 則指向一維數組 a[i]。從前面的分析可得出

*(p+i)+j 是二維數組 i 行 j 列的元素的地址,而*(*(p+i)+j)則是 i 行 j 列元素的值。二維數組指針變量說明的一般形式為:

類型說明符 (*指針變量名)[長度]

其中"類型說明符"為所指數組的數據類型。"*"表示其後的變量是指針類型。"長度"表 示二維數組分解為多個一維數組時,一維數組的長度,也就是二維數組的列數。應注意"(* 指針變量名)"兩邊的括號不可少,如缺少括號則表示是指針數組(本章後面介紹),意義就完 全不同了。

【例 8.6】

main(){

int a[3][4]={0,1,2,3,4,5,6,7,8,9,10,11};

int(*p)[4]; int i,j; p=a;

for(i=0;i<3;i++)

{for(j=0;j<4;j++) printf("%2d ",*(*(p+i)+j)); printf("\n");}

}

8.5 指針數組和指向指針的指針


8.5.1 指針數組的概念


一個數組的元素值為指針則是指針數組。 指針數組是一組有序的指針的集合。 指針數組的所有元素都必須是具有相同存儲類型和指向相同數據類型的指針變量。

指針數組說明的一般形式為:

類型說明符 *數組名[數組長度]

其中類型說明符為指針值所指向的變量的類型。 例如:

int *pa[3]

表示 pa 是一個指針數組,它有三個數組元素,每個元素值都是一個指針,指向整型變量。

【例 8.7】通常可用一個指針數組來指向一個二維數組。指針數組中的每個元素被賦予二維數組每一行的首地址,因此也可理解為指向一個一維數組。

main(){

int a[3][3]={1,2,3,4,5,6,7,8,9};

int *pa[3]={a[0],a[1],a[2]}; int *p=a[0];

int i; for(i=0;i<3;i++)

printf("%d,%d,%d\n",a[i][2-i],*a[i],*(*(a+i)+i)); for(i=0;i<3;i++)

printf("%d,%d,%d\n",*pa[i],p[i],*(p+i));

}

本例程序中,pa 是一個指針數組,三個元素分別指向二維數組 a 的各行。然後用循環語句輸出指定的數組元素。其中*a[i]表示 i 行 0 列元素值;*(*(a+i)+i)表示 i 行 i 列的元素值;*pa[i]表示 i 行 0 列元素值;由於 p 與 a[0]相同,故 p[i]表示 0 行 i 列的值;*(p+i)

表示 0 行 i 列的值。讀者可仔細領會元素值的各種不同的表示方法。

應該注意指針數組和二維數組指針變量的區別。這兩者雖然都可用來表示二維數組,但 是其表示方法和意義是不同的。

二維數組指針變量是單個的變量,其一般形式中"(*指針變量名)"兩邊的括號不可少。而 指針數組類型表示的是多個指針(一組有序指針)在一般形式中"*指針數組名"兩邊不能有括號。

例如:

int (*p)[3];

表示一個指向二維數組的指針變量。該二維數組的列數為 3 或分解為一維數組的長度為3。

int *p[3]

表示 p 是一個指針數組,有三個下標變量 p[0],p[1],p[2]均為指針變量。

指針數組也常用來表示一組字符串,這時指針數組的每個元素被賦予一個字符串的首地

址。指向字符串的指針數組的初始化更為簡單。例如在例 10.32 中即採用指針數組來表示一組字符串。其初始化賦值為:

char *name[]={"Illagal day", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"};

完成這個初始化賦值之後, name[0] 即指向字符串"Illegal day" , name[1] 指向

"Monday"......。

8.5.2 指向指針的指針


如果一個指針變量存放的又是另一個指針變量的地址,則稱這個指針變量為指向指針的 指針變量。

在前面已經介紹過,通過指針訪問變量稱為間接訪問。由於指針變量直接指向變量,所 以稱為"單級間址"。而如果通過指向指針的指針變量來訪問變量則構成"二級間址"。

從下圖可以看到,name 是一個指針數組,它的每一個元素是一個指針型數據,其值為地址。Name 是一個數據,它的每一個元素都有相應的地址。數組名 name 代表該指針數組的首地址。name+1 是 mane[i]的地址。name+1 就是指向指針型數據的指針(地址)。還可以設置一個指針變量 p,使它指向指針數組元素。P 就是指向指針型數據的指針變量。

怎樣定義一個指向指針型數據的指針變量呢?如下: char **p;

p 前面有兩個*號,相當於*(*p)。顯然*p 是指針變量的定義形式,如果沒有最前面的*,那就是定義了一個指向字符數據的指針變量。現在它前面又有一個*號,表示指針變量 p 是指向一個字符指針型變量的。*p 就是 p 所指向的另一個指針變量。

從下圖可以看到,name 是一個指針數組,它的每一個元素是一個指針型數據,其值為地址。name 是一個數組,它的每一個元素都有相應的地址。數組名 name 代表該指針數組的首地址。name+1 是 mane[i]的地址。name+1 就是指向指針型數據的指針(地址)。還可以設置一個指針變量 p,使它指向指針數組元素。P 就是指向指針型數據的指針變量。


如果有:

p=name+2; printf("%o\n",*p);

printf("%s\n",*p);

則,第一個 printf 函數語句輸出 name[2]的值(它是一個地址),第二個 printf 函數語句以字符串形式(%s)輸出字符串"Great Wall"。

【例 8.8】使用指向指針的指針。

main()

{char *name[]={"Follow me","BASIC","Great Wall","FORTRAN","Computer desighn"}; char **p;

int i; for(i=0;i<5;i++)

{p=name+i; printf("%s\n",*p);

}

}

說明:

p 是指向指針的指針變量。

有關指針的數據類型的小結


指針運算的小結

現把全部指針運算列出如下:

1) 指針變量加(減)一個整數:

例如:p++、p--、p+i、p-i、p+=i、p-=i

一個指針變量加(減)一個整數並不是簡單地將原值加(減)一個整數,而是將該指針 變量的原值(是一個地址)和它指向的變量所占用的內存單元字節數加(減)。

2) 指針變量賦值:將一個變量的地址賦給一個指針變量。p=&a; (將變量 a 的地址賦給 p)

p=array; (將數組 array 的首地址賦給 p) p=&array[i]; (將數組 array 第 i 個元素的地址賦給 p)

p=max; (max 為已定義的函數,將 max 的入口地址賦給 p) p1=p2; (p1 和 p2 都是指針變量,將 p2 的值賦給 p1)

注意:不能如下: p=1000;

3) 指針變量可以有空值,即該指針變量不指向任何變量: p=NULL;

4) 兩個指針變量可以相減:如果兩個指針變量指向同一個數組的元素,則兩個指針變量值 之差是兩個指針之間的元素個數。

兩個指針變量比較:如果兩個指針變量指向同一個數組的元素,則兩個指針變量可以進行比 較。指向前面的元素的指針變量"小於" 指向後面的元素的指針變量。

關鍵字: