指標 (Pointer)

36
1 CSIM, PU C Language 指指 (Pointer) Introduction to the C Programming Language

description

Introduction to the C Programming Language. 指標 (Pointer). 指 標 (Pointer). 指標概述 指標的宣告與定義 指標與函數 指標與陣列 指向指標的指標 — 雙 重指標. 為什麼要用指標 ?. 可使函數在傳遞陣列或字串時更有效率。 較複雜的資料結構須要指標才能將資料串在一起。 Ex. Linked list, binary tree. 有些函數必須利用指標來傳達記憶體的訊息。 malloc( ): 記憶體配置函數 fopen( ): 檔案開啟函數. - PowerPoint PPT Presentation

Transcript of 指標 (Pointer)

Page 1: 指標  (Pointer)

1CSIM, PU C Language

指標 (Pointer)

Introduction to the C Programming Language

Page 2: 指標  (Pointer)

2CSIM, PU C Language

指 標 指 標 (Pointer)(Pointer)

指標概述 指標的宣告與定義 指標與函數 指標與陣列 指向指標的指標—雙重指標

Page 3: 指標  (Pointer)

3CSIM, PU C Language

為什麼要用指標 ?

可使函數在傳遞陣列或字串時更有效率。 較複雜的資料結構須要指標才能將資料串在一起。

Ex. Linked list, binary tree.

有些函數必須利用指標來傳達記憶體的訊息。 malloc( ): 記憶體配置函數 fopen( ): 檔案開啟函數

Page 4: 指標  (Pointer)

4CSIM, PU C Language

指標概述 C 語言提供一種存取變數的特殊方式 -- 指標 (pointer), 透

過指標 , 可以不必用到變數的名稱 , 卻可以存取到變數的內容 .

指標其實只是一種特殊的變數 , 用來存放變數在記憶體中的位址 .

利用指標變數 , 可以把變數在記憶體內的位址存入指標中, 可利用指標先找到該變數的位址 , 再由該位址取出位址內所儲存的變數值 . 這種依位址來取值的特殊方式 , 稱之為“間接定址取值法”

5

FDD4

變數內容

a

ptr

FDD2

FDD4

FDD6

FDD8

一般變數

指標變數( 圖一 : 指標與變數在記憶體中的情形 )

指標 ptr 存放了變數 a 的位址,也就是指標 ptr 指向變數 a 。

Page 5: 指標  (Pointer)

5CSIM, PU C Language

指標的宣告與定義指標的宣告與定義 指標變數宣告格式

(1) 在變數的前面加上指標符號「 * 」,即是將變數宣告 成指標變數。 (2) 指標變數之前的資料型態,則是代表指標所指向之變 數的型態。

例一 :

int *ptr ; /* 宣告指向整數的指標變數 ptr ,它所存放的

位址必須是一個整數變數的位址。 */

資料型態 * 指標變數 ;

Page 6: 指標  (Pointer)

6CSIM, PU C Language

法 1---

int *ptr ; /* 宣告指向整數的指標變數 ptr */

int num=20 ; /* 宣告整數變數 num ,並設值為 20 */

ptr = &num ; /* 把指標 ptr 設為變數 num 的位址 , 把 ptr 指向 num */

法 2---

int num=20;

int *ptr = #

指標的宣告與定義指標的宣告與定義

20

1408 1400

變數 num指標變數 ptr

指標變數 ptr 的位址 變數 num 的位址

1400

ptr 指向整數變數 num

Page 7: 指標  (Pointer)

7CSIM, PU C Language

指標變數的使用 指標變數使用方式

1.位址運算子「 & 」 : 用來取得變數的記憶體位址

2.位址取值運算子「 * 」 : 用來取得指標所指向變數的內容

20

1400

變數 num

變數 num 的位址

int num=20 ;

&num 可取出變數 num的位址,即 1400

20

1408 1400

變數 num指標變數 ptr

指標變數 ptr 的位址 變數 num 的位址

1400 *ptr 可取出 ptr 所指向之變數 num 的值 (num 的值為 20)

int num=20;

int *ptr=#

Page 8: 指標  (Pointer)

8CSIM, PU C Language

指標變數的宣告 範例一:指標變數的宣告#include<stdio.h>

#include<stdlib.h>

int main(void)

{

int *ptr,num=20; /* 宣告變數 num 與指標變數 ptr */

ptr=&num; /* 將 num 位址設給指標 ptr 存放 */

printf(“num=%d, &num=%x\n”,num,&num);

printf(“*ptr=%d,ptr=%x,&ptr=%x\n”,*ptr,ptr,&ptr);

system(“pause”);

return 0;

}

Output:num=20,&num=ffd6*ptr=20,ptr=ffd6,&ptr=ffd4

Page 9: 指標  (Pointer)

9CSIM, PU C Language

指標的宣告與定義指標的宣告與定義 範例二 : 比較輸出指標的位址與值#include<stdio.h>

void main()

{

int *ptr;

int a,b;

a=20;

ptr=&a; /* 將 ptr 指向變數 a 的位址 */

b=*ptr; /* 將 b 設定為 ptr 所指位址的內容值 */

printf(“a=%d,*ptr=%d , b=%d\n", a,*ptr , b );

printf(“ptr=%x , &a=%x\n", ptr , &a );

}

Output:

a=20, *ptr=20, b=20

ptr=ffd6 ,&a=ffd6

Page 10: 指標  (Pointer)

10CSIM, PU C Language

指標所佔的空間大小 #include<stdio.h> #include<stdlib.h> int main(void) { int *ptri; /* 宣告指向整數的變數 ptri*/ char *ptrc; /* 宣告指向字元的變數 ptrc*/ printf("size of ptri = %d\n",sizeof(ptri)); printf("size of ptrc = %d\n",sizeof(ptrc)); printf("size of *ptri = %d\n",sizeof(*ptri)); printf("size of *ptrc = %d\n",sizeof(*ptrc)); system("pause"); return 0; }

Page 11: 指標  (Pointer)

11CSIM, PU C Language

指標所佔的空間大小 ( 續 )

在 TC 中, compile 配置給指標變數的空間都是 2bytes 。 Output:

Page 12: 指標  (Pointer)

12CSIM, PU C Language

課堂練習 #include <stdio.h> #include <stdlib.h> int main(void) { int a=5,b=10; int *ptr1,*ptr2; ptr1=&a; /* 將 ptr1 設為 a 的位址 */ ptr2=&b; /* 將 ptr2 設為 b 的位址 */ *ptr1=7; /* 將 ptr1 指向的內容設為 7 */ *ptr2=32; /* 將 ptr2 指向的內容設為 32 */ a=17; /* 設定 a 為 17 */ ptr1=ptr2; /* 設定 ptr1=ptr2 */ *ptr1=9; /* 將 ptr1 指向的內容設為 9 */ ptr1=&a; /* 將 ptr1 設為 a 的位址 */ a=64; /* 設定 a 為 64 */ *ptr2=*ptr1+5; /* 將 ptr2 指向的內容設為 *ptr1+5*/ ptr2=&a; /* 將 ptr2 設為 a 的位址 */ printf("a=%2d, b=%2d, *ptr1=%2d, *ptr2=%2d\n",a,b,*ptr1,*ptr2); printf("ptr1=%p, ptr2=%p\n",ptr1,ptr2); system("pause"); return 0; }

Page 13: 指標  (Pointer)

13CSIM, PU C Language

課堂練習 Output:

請試著寫出以下四個變數記憶體空間之值的變化 : ptr1 ptr2 a b

FFDE

FFDE FFDG

FFDG 5 10

FBDEFADA

FFDG

FFDE

FFDE 7

17

64

32

9

69

Page 14: 指標  (Pointer)

14CSIM, PU C Language

指標與函數 在函數中 , 若是想傳回某個結果給原呼叫函數 , 可利用 return 敘述 ,

但當程式需要傳遞兩個以上的值時 , 就無法利用 return 敘述 , 此時可利用“指標”解決函數間傳遞多個回傳值的問題 .

宣告函數原型範例 :

int func ( int * , char * ) ; /* 將指標當成參數 , 傳入函數 */

函數定義中接收參數部分 , 假設接收的變數名稱為 ptr1 及 ptr2, 記得在變數名稱前要加指標符號 *

int func ( int * ptr1 , char * ptr2 ) ;

Page 15: 指標  (Pointer)

15CSIM, PU C Language

指標與函數之範例 1 範例六 :設計一個傳值函式 noswap(), 及一個傳址函式 swap()#include<stdio.h>void noswap(int, int);void swap(int *, int *);main(){ int x=2,y=3;printf("The original values are: x=%d , y=%d \n" , x , y );

noswap(x,y); printf(“(call by value: x=%d , y=%d \n", x , y );

swap(&x,&y); printf(“(call by address: x=%d , y=%d \n", x , y );}

傳入 pointer 值 ( 變數位址即為 pointer 的值 )

Page 16: 指標  (Pointer)

16CSIM, PU C Language

指標與函數之範例 1 接上頁 ( 範例六 )

void noswap ( int a , int b ) /* call by value */ { int t; t=a; a=b; b=t;}void swap ( int *a , int *b) /* call by address* / { int t; t=*a; *a=*b; *b=t;}

Page 17: 指標  (Pointer)

17CSIM, PU C Language

指標與函數之範例 2 #include <stdio.h> #include <stdlib.h> void rect(int,int,int *,int *); /* 函數 rect() 的原型 */ int main(void) { int a=5,b=8; /* 矩形的長與寬 */ int area=0,peri=0; /* 矩形的面積與邊長 */ rect(a, b, &area, &peri); /* 呼叫 rect(), 計算面積及周長 */ printf("area=%d,total length=%d\n",area,peri); system("pause"); return 0; } void rect(int x, int y, int *p1, int *p2) { *p1=x*y; *p2=2*(x+y); }

Page 18: 指標  (Pointer)

18CSIM, PU C Language

範例 2之說明 當函數 rect(x, y, area, length) 執行完後,變數 area 可存

放矩形面積,而變數 peri 可存放矩形的周長。 指標的好處之一在於它可以更改傳入的引數值,利用此

特性,我們便可以設計出很實用的程式。

Page 19: 指標  (Pointer)

19CSIM, PU C Language

指標與陣列 陣列事實上也是指標的一種應用 , 不同的是 , 陣列是固定長

度的記憶體區塊 ; 而指標是一個變數 , 用來記錄所指變數的位址 .

利用 index 即可取出陣列的元素值 , 除了用 index 外 , 也可另外利用指標取出陣列的元素值 .

char a[10]; /* 宣告陣列 */ char *pa; /* 宣告指標 */ pa=&a[0]; 或 pa=a; /* 指標指向陣列的開頭 (第 0個元素 )*

/ 以此為基點用索引的方式來取得陣列的每個元素 *(pa) 取到的是陣列的第 0個元素 a[0] *(pa+1) 取到的是陣列的第 1個元素 a[1] /*pa+1 是將位

址值加 2(int 佔 2個 bytes)*/ *(pa+i) 取到的是陣列的第 i個元素 a[i]

Page 20: 指標  (Pointer)

20CSIM, PU C Language

指 標指 標 -- 指標與一維陣列 指標與一維陣列

表示宣告 ptr 是一個指標變數, ptr本身存的是一個變數的位址。 ptr=a 的效果就是將 ptr 指向 a[0] 的位址。

printf(“%d”,*ptr);

for(i=1;i<5;i++)

printf(“%d”,*ptr++); /* 不能寫成 *a++ */

int *ptr;int a[5]={3,8,6,2,5};ptr=a; /* 或 ptr=&a[0]; */

*ptr 取出陣列裡的值

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

printf(“%d”,a[i]);

陣列名稱本身是一個存放位址的「指標常數」,指向陣列的位址。

因為陣列宣告後,陣列的記憶體位址配置已被固定,所以我們不能再去改變陣列名稱的指向!

Page 21: 指標  (Pointer)

21CSIM, PU C Language

3

8

6

2

5

a[1]

a[2]

a[3]

a[4]

a[0]

fdd10

a

fdd4

fdd8

fdd6

fdd2

ptrptr+0

ptr+1

ptr+2

ptr+3

ptr+4

&a[0]

&a[1]

&a[2]

&a[3]

&a[4]

*(ptr+0)

*(ptr+1)

*(ptr+2)

*(ptr+3)

*(ptr+4)

int a[5]={3,8,6,2,5};

int *ptr;

ptr=&a[0];

Page 22: 指標  (Pointer)

22CSIM, PU C Language

課堂練習 範例 4-2: #include <stdio.h> #include <stdlib.h> int main(void) { int a[3]={5,1,9}; printf(“a[0]=%d, *(a+0)=%d\n”,a[0],*(a+0)); printf(“a[1]=%d, *(a+1)=%d\n”,a[1],*(a+1)); printf(“a[2]=%d, *(a+2)=%d\n”,a[2],*(a+2)); system(“pause”); return 0; }

Page 23: 指標  (Pointer)

23CSIM, PU C Language

範例 4-2 請說明以下變數的內容

9

1

5

1408

1406

1404

*(a+2) a[2]

*(a+1) a[1]

*(a+0) a[0]

&a[2]

&a[1]

&a[0]

Page 24: 指標  (Pointer)

24CSIM, PU C Language

指標與陣列 範例四 : 用指標取出陣列元素的值與位址

#include<stdio.h>main(){ int i; char *pa; char a[4]={'A','B','C','D'}; pa=a;

for(i=0;i<4;i++) printf("a[%d] value:%c ; *(pa+%d) value:%c\n",i,a[i],i,*(pa+i));

for(i=0;i<4;i++) printf("&a[%d] address:%x ; *(pa+%d) address:%x\n",i,&a[i],i,pa+i);}

Pointer指向陣列的開頭

Page 25: 指標  (Pointer)

25CSIM, PU C Language

範例四的結果

指標的算術運算多半用在存取陣列元素的操作上。 因為 compile會配置給陣列一塊連續的記憶體空間,所以陣列元

素的位址之間有很高的關聯性。

Page 26: 指標  (Pointer)

26CSIM, PU C Language

指 標指 標 -- 指標與二維陣列 指標與二維陣列 二維陣列在人們眼中通常用於處理二維的行列表格,但電腦內部的記憶體,是

以一維陣列的方式排列,所以雖然是二維的陣列,但在電腦的記憶體中已轉為一維的方式儲存。

以指標來處理二維陣列,第一步驟設定第一元素的位址給指標變數: int *ptr;

int a[4][3];

ptr=&a[0][0];

第二步驟以 ptr 來表示二維陣列的值,必須先將二維排列轉為一為陣列的直線排列:

a(0,0)

0

a(0,1)

1

a(0,2)

2

a(1,0)

3

a(1,1)

4

a(1,2)

5

a(2,0)

6

a(2,1)

7

a(2,2)

8

a(3,0)

9

a(3,1)

10

a(3,2)

11

第 0列第 1列第 2列第 3列

第 0行

第 1行

第 2行 *(ptr+3*0+2)=5

表示將 a[0][2] 的值設為 5

第 i 列,第 j 行 的表示方式 *(ptr+3*i+j)

3 為此陣列行的個數

ptr

Page 27: 指標  (Pointer)

27CSIM, PU C Language

若要存取第 i 列第 j 行的值: *(*(a+i)+j)=5; a[i][j]=5; *(ptr + 3 *i + j)=5; /*a[i][j]=5;*/

a(0,0)

0

a(0,1)

1

a(0,2)

2

a(1,0)

3

a(1,1)

4

a(1,2)

5

a(2,0)

6

a(2,1)

7

a(2,2)

8

a(3,0)

9

a(3,1)

10

a(3,2)

11

i

j

Page 28: 指標  (Pointer)

28CSIM, PU C Language

311016

131018

191020

281022

121000

231002

421004

181006

431008

221010

161012

141014

num[0]

1000

num[1]

num[2]

1008

1016

num 1000

陣列名稱 num 是指標常數,是一個指向指標的指標

num[0]~num[2] 也是指標常數,分別儲存了每一個一維陣列裡,第一個元素的位址

3*4 的二維陣列可以看成是由 3 個一維陣列所組成

int num[3][4];

在宣告 num陣列時,編譯器會自動配置一個「指標常數」的陣列 num[0]、 num[1]、 num[2]讓他們分別指向每一列的第一個元素。

第 0 列

第 1 列

第 2 列

1000

1008

1016

1000

Page 29: 指標  (Pointer)

29CSIM, PU C Language

續上頁

311016

131018

191020

281022

121000

231002

421004

181006

431008

221010

161012

141014

num[0]

1000

num[1]

num[2]

1008

1016

num+01000

1008

1016

num+1

num+2

*(num+0) 可取出此值

*(num+2) 可取出此值

Page 30: 指標  (Pointer)

30CSIM, PU C Language

續上頁

431008

221010

161012

141014

num[1] 10081008

*(num+1) 可取出此值

num+1

*(num+1)+0

*(num+1)+1

*(num+1)+2

*(num+1)+3

*(*(num+1)+0)

*(*(num+1)+1)

*(*(num+1)+2)

*(*(num+1)+3)

第 1 列

*(num+i)+j 代表了第 i 列,第 j 行的位址*(*(num+i)+j) 代表了取出第 i 列,第 j 行的值

這裡列跟行的算法是從第 0 列第 0 行起算

Page 31: 指標  (Pointer)

31CSIM, PU C Language

指標與陣列 --- 指標陣列 指標也可以宣告成陣列:指標陣列。 宣告方式如下 :

資料型態 * 陣列名稱 [ 元素個數 ];

例如 : int *ptr[3]; /* 表示宣告了一個可存放 3 個指向整數的指標 , 分別是 pt

r[0] 、 ptr[1]、 ptr[2]*/ 二維陣列 :

char str[3][10]={“Clare”, ”Jane Wang”, ”Tom Lee”};

指標陣列 : char *ptr[3]={“Clare”, ”Jane Wang”, ”Tom Lee”};

Page 32: 指標  (Pointer)

32CSIM, PU C Language

指標陣列 指標陣列 如何建立一個儲存字串的陣列 ? 除了用二維的字元陣列處理 , 另外可用“

指標陣列”來解決 , 用指標陣列來處理 ,會較節省記憶體空間 .

C l a r e \0

FDB2

FDB4

FBD6

C l a r e \0

J a n e W a n g \0

T o m L e e \0

Name[0]

Name[1]

Name[2]

( 圖 三 : 儲存字串的二維字元陣列儲存方式 )

Name[0]

Name[1]

Name[2]

FDB2

FDB4

FBD6

( 圖 四 : 指標陣列儲存方式 )

J a n e W a n g \0

T o m L e e \0

char Name[3][10]={“Clare”,”Jane Wang”,”Tom Lee”};

char *Name[3]={“Clare","Jane Wang","Tom Lee"};

Page 33: 指標  (Pointer)

33CSIM, PU C Language

指標陣列 範例五 : 指標陣列

#include <stdio.h>

int main(void)

{

int i;

char *name[3]={"David","Jane Wang","Tom Lee"};

for(i=0;i<3;i++) /* 印出指標陣列的內容 */

printf("name[%d]=%s\n",i,name[i]);

/*puts(name[i];)*/

return 0;

}

Page 34: 指標  (Pointer)

34CSIM, PU C Language

指標陣列 範例五 : 指標陣列另一種寫法 #include <stdio.h> int main(void) { int i; char *ptr[3]; ptr[0]="David"; /* 設定 ptr[0] 指向字串” David” */ ptr[1]="Jane Wang"; /* 設定 ptr[1] 指向字串” Jane Wang” */ ptr[2]="Tom Lee"; for(i=0;i<3;i++) /* 印出指標陣列的內容 */ /*printf("ptr[%d]=%s\n",i,ptr[i]);*/ puts(ptr[i]); return 0; }

Page 35: 指標  (Pointer)

35CSIM, PU C Language

指向指標的指標—雙重指標 雙重指標宣告方式

例 :

int **ptri; /* 宣告一個指向整數的雙重指標 ptri */

資料型態 ** 指標變數;

雙重指標

位址

位址 整數值

指向整數的指標 變數

1200 1460 5

雙重指標 指標變數 一般變數

1000 1200 1460

Page 36: 指標  (Pointer)

36CSIM, PU C Language

指向指標的指標—雙重指標 範例三 : 雙重指標 , 輸出其內容及位址

#include<stdio.h>void main(){ int *ptr1,**ptr2; int a=10;

ptr1=&a; /* 指標 ptr1 指向 a 的位址 */ ptr2=&ptr1; /* 雙重指標 ptr2 指向 ptr1 的位址 */

printf("a=%d , &a=%x\n", a , &a ); printf("*ptr1=%d , ptr1=%x\n", *ptr1 , ptr1 ); printf("**ptr2=%d , *ptr2=%x\n", **ptr2 , *ptr2); printf("ptr2=%x, &ptr1=%x\n", ptr2 , &ptr1);}

a

ffd4 ffd6 10

ptr2 ptr1

ffd2 ffd4 ffd6

Output:

a=10, &a=ffd6

*ptr1=10, ptr1=ffd6

**ptr2=10 ,*ptr2=ffd6

ptr2=ffd4 , &ptr1=ffd4