08 指標
Post on 12-Jul-2015
2.885 Views
Preview:
TRANSCRIPT
第8章 指標
認識指標
指標與陣列的應用
動態配置記憶體
上機實習課程
2
認識指標(1)
指標與記憶體有著相當密切的關係。
現在請您思考一個問題,變數是用來儲存數值,
而數值究竟儲存在記憶體中的哪個位址上呢?
相當簡單,如果要了解變數所在記憶體位址,只要透過 &(取址運算子)就能求出變數所在的位址。語法格式如下:
8-1 認識指標
&變數名稱;
3
認識指標(2)
程式範例:&(取址運算子)的宣告與使用範例:
CH08_01.c
8-1 認識指標
4
執行結果
程式解說
8-1 認識指標
第6~8行分別宣告三種不同型態的變數,第10~12行則
以%p格式來表示16進位的位址,如果要取出變數的位
址只要在變數前加上&運算子即可。
5
指標變數的宣告(1)
指標的宣告方式如下:
以下是幾個指標變數的宣告方式:
8-1 認識指標
資料型態 *指標名稱;
或
資料型態* 指標名稱;
int* x;
int *x, *y;
6
在宣告指標時,我們可以將*置放於型態宣告
的關鍵字旁,或是變數名稱旁邊,通常若要宣
告兩個以上的變數,會將*靠在變數名稱旁,
增加可讀性。
當然指標變數宣告時也可設定初值為0或是
NULL來增加可讀性:
8-1 認識指標
int *x=0;
int *y=NULL;
7
以下程式是很經典的指標範例,主要是說明指
標變數address1的儲存內容是位址,
*address1則是address1所指向的變數值(也就
是num1的值),而&address1則是指標變數本
身的位址。
我們可以使用下圖來表示數值、變數、記憶體
與指標間的關係:
8-1 認識指標
8
指標變數的宣告(2)
程式範例:指標變數的宣告與使用範例:
CH08_02.c
8-1 認識指標
9
執行結果
程式解說
8-1 認識指標
第9行宣告指標變數指向num1變數的位址。
第11行則利用&運算子取出num1位址。
第13行則是輸出此指標變數的位址。
10
指標變數的宣告(3)
程式範例:指標變數與取值運算子的使用範例:
CH08_03.c
8-1 認識指標
11
執行結果
程式解說
8-1 認識指標
在第10、12行中分別指向相同資料型態的不同變
數,而在第11、13行中分別輸出指標變數的值與
指向變數的位址間的比較。
12
指標變數的宣告(4)
程式範例:指標變數重新設定範例:CH08_04.c
8-1 認識指標
13
執行結果
程式解說
8-1 認識指標
在第9行中,我們特意使用了input變數來初始化指標的值,
這邊再次要提醒的是,您不能使用未經初始化的指標。
第12,行,由於取址運算子與乘法運算子在符號使用上相同,
您可以使用空白來增加程式的可讀性,此外由於取址運算子
的優先順序大於乘法運算子,所以不必加上括號。
第13行中我們特別把求得立方值的*ptr內容輸出,各位會發
現與它同一位值的input變數值也同步改變了。
14
指標與函數傳遞(1)
先來回憶一下傳址方式的函數宣告型式如下所示:
此外,傳址呼叫的函數呼叫型式如下所示:
8-1 認識指標
回傳資料型態 函數名稱(資料型態 *參數1, 資料型態 *參數
2, ……….);
或
回傳資料型態 函數名稱(資料型態 *, 資料型態 *, ……….);
函數名稱(&引數1,&引數2, ……….);
15
指標與函數傳遞(2)
程式範例:氣泡排序法的傳址呼叫範例:
CH08_05.c
8-1 認識指標
16
8-1 認識指標
17
8-1 認識指標
18
執行結果
程式解說
8-1 認識指標
在本程式中由於swap()函數與BubbleSort( )函數是定義
在main( )函數之前,所以不用原型宣告。
第4行利用兩個指標變數傳入函數內。
第24行接收兩個指向陣列元素的位址。
第38行呼叫BubbleSort( )函數,並將num陣列以傳址呼
叫傳送。
19
函數與指標回傳值(1)
指標回傳函數宣告語法如下:
8-1 認識指標
回傳資料型態 *函數名稱(資料型態 參數1, 資料型
態 參數2, ……….)
{
….
return 指標變數;
}
20
函數與指標回傳值(2)
程式範例:函數與指標回傳值的應用範例:
CH08_06.c
8-1 認識指標
21
8-1 認識指標
22
執行結果
程式解說
8-1 認識指標
第4行是傳回指標值的函數原型宣告,第9行呼叫
get_pointer_value ()函數,並傳值給ptr指標變數。
第11行輸出ptr指標變數的內容。
第19行函數宣告為傳回指標變數。第25行輸入input變
數的值。
第28行傳回值為指標變數。
23
函數與指標回傳值(3)
程式範例:指標回傳值與函數傳遞的應用範例:
CH08_07.c
8-1 認識指標
24
8-1 認識指標
25
執行結果
程式解說
8-1 認識指標
第4行宣告傳址呼叫函數min()的原型。
以ptr指標變數接收函數的指標回傳值。
第23、24行則直接傳回指標值。
26
指標的運算
對於指標的加法或減法運算,只能針對常數值
(如+1或-1)來進行,不可以做指標變數之間的
相互運算。
因為指標變數內容只是存放位址,而位址間的
運算並沒有任何意義,而且容易讓指標變數指
向不合法位址。
以下程式範例中可以發現,對整數型態的指標
來說每進行一次加法運算,記憶體位址就會向
右移動4位元組,而對於字元型態的指標而言,
加法運算則是每次向右移動1位元組。
8-1 認識指標
27
程式範例:指標運算的宣告與應用範例:
CH08_08.c
8-1 認識指標
28
執行結果
8-1 認識指標
29
程式解說
8-1 認識指標
第6、7行宣告整數型態指標與字元型態指標變數。
第13行整數指標變數加一,則記憶體位址就會向右
移動4位元組。
第14行字元指標變數加一,則記憶體位址就會向右
移動1位元組。
30
一個宣告雙重指標的例子如下所示:
雙重指標變數所存放的就是某個指標變數在記
憶體中的位址,也就是這個ptr就是一個指向指
標的指標變數。例如我們宣告如下:
多重指標(1)8-1 認識指標
int **ptr;
int num=100,*ptr1,**ptr2;
ptr1=#
ptr2=&ptr1;
31
多重指標(2)
程式範例:多重指標的宣告與應用範例:
CH08_09.c
8-1 認識指標
32
執行結果
程式解說
8-1 認識指標
第11行中各位可以發現&num的位址和ptr是一樣的,而
*ptr的值也和num相同。
第13行中&ptr1和ptr2相同,ptr1與*ptr2一樣,*ptr1與
**ptr2相同。
33
陣列宣告:
這時陣列名稱arr就是一個指標常數,也是這個
陣列的起始位址。
可以利用指標方式與取值運算子「*」來直接
存取陣列內的元素值。使用語法如下:
指標與一維陣列(1)8-2 指標與陣列的應用
int arr[6]={312,16,35,65,52,111};
陣列名稱[索引值]= *陣列名稱(+索引值)
或
陣列名稱[索引值]= *(&陣列名稱[索引值])
34
指標與一維陣列(2)
程式範例:指標常數與一維陣列的宣告與應用
範例:CH08_10.c
8-2 指標與陣列的應用
35
執行結果
8-2 指標與陣列的應用
36
程式解說
8-2 指標與陣列的應用
第7行輸出指標常數arr的值與指標常數arr的位址相同。
第11行列印陣列與兩種指標常數的替代運算,從執行結
果中您可以看到,對於int資料型態來說,每加1則位址
位移4位元組。
第15行則以兩種指標常數方式來存取陣列內的元素值。
37
我們也可以將陣列的記憶體位址指派給一個指
標變數,並使用此指標變數來間接顯示陣列元
素內容。
有關指標變數取得一維陣列位址的方式如下:
8-2 指標與陣列的應用
資料型態 *指標變數=陣列名稱;
或
資料型態 *指標變數=&陣列名稱[0];
38
指標與一維陣列(3)
程式範例:指標變數與一維陣列的宣告與應用
範例:CH08_11.c
8-2 指標與陣列的應用
39
執行結果
8-2 指標與陣列的應用
40
程式解說
8-2 指標與陣列的應用
使用指標變數ptr指向陣列常數arr。
第15、17行輸出arr+i的值與ptr+I的值,兩者顯
示的位址是相同的。
41
二維陣列具有兩個索引值,這意味著二維陣列會有兩個值來控制指定元素相對於第一個元素的位移量,為了說明方便,我們以下面這個宣告為例:
在這個例子中,*(no+0)將表示陣列中維度1的第一個元素的記憶體位址。
也就是&no[0][0];而*(no+1)表示陣列中維度2的第一個元素的記憶體位址,也就是&no[1][0],而*(no+i)表示陣列中維i+1的第一個元素的記憶體位址。
指標與二維陣列(1)8-2 指標與陣列的應用
int no[2][4];
42
指標與二維陣列(2)
程式範例:二維陣列的指標常數表示範例:
CH08_12.c
8-2 指標與陣列的應用
43
執行結果
程式解說
8-2 指標與陣列的應用
第11行中是輸出使用「&」取址運算子取得二維陣列元素
位址與指標常數來表示二維陣列元素位址,並可發現要取
得元素no[i][j]的記憶體位址,則要使用*(no+i)+j來取得。
44
指標與二維陣列(3)
程式範例:二維陣列的指標常數與求值應用範
例:CH08_13.c
8-2 指標與陣列的應用
45
執行結果
程式解說
8-2 指標與陣列的應用
第11行使用*(*(no+i)+j)與no[i][j]來輸出二維陣列的元素值。
依照以上的說明,您也可以推論在三維陣列中,存取元素值
時的三個索引值之真正意義,其實就分別代表了三個記憶體
位移量的控制,而當中必須使用三重指標來計算,這部份的
推論方式與以上的介紹類似。
46
指標與字串(1)
在C語言中,字串是以字元陣列來實現,指標
既然可以運用於陣列,則當然也可以運用於字
串。以下都是字串宣告的合法方式:
使用指標的觀念來處理字串,會比使用陣列來
得方便許多,宣告格式如下:
8-2 指標與陣列的應用
char name[] = { 'J', 'u', 's', 't', '\0'};
char name1[] = "Just";
char *ptr = "Just";
char *指標變數="字串內容";
47
指標與字串(2)
程式範例:指標與字串的宣告與使用範例:
CH08_14.c
8-2 指標與陣列的應用
48
8-2 指標與陣列的應用
49
執行結果
8-2 指標與陣列的應用
50
程式解說
8-2 指標與陣列的應用
第7行以以指標變數宣告字串方式,第8行則以字元陣列
宣告字串方式。
在第11行輸出的ptr所佔空間大小4位元,是因為ptr所存
放的是整數位址。
至於sizeof(name)則輸出包含‘\0’共7位元。
第16行則以指標變數及指標常數輸出字串。
第21行則將指標變數及指標常數以陣列方式及逐一讀值
方式輸出字元。
第26、27行則執行指標運算,並輸出當時的位址。
51
指標與字串(3)
程式範例:字串傳遞與大小寫字母互相轉換應
用範例:CH08_15.c
8-2 指標與陣列的應用
52
8-2 指標與陣列的應用
53
8-2 指標與陣列的應用
54
執行結果
程式解說
8-2 指標與陣列的應用
第9行中是以陣列字元宣告字串,第24、40行則利用指標
變數接收參數字串。
第29、46行則將函數中的指標變數逐一計算字元總數。
第34、51行的主要技巧是利用指標變數逐一讀出字元與在
ASCII編碼中,英文小寫字母編碼為97~122,而大寫字母
為65~90,中間的編碼數值差為32,而字元在記憶體中其
實是以整數型態儲存,所以我們只要對字元加32或減32,
就可以直接轉換字母的大小寫。
55
以下程式範例是使用指標來進行Strcat()字串函
數串接實作,並將指標當作傳回值。
技巧就是首先必須找出被串接字串的尾端,然
後將要的串接字串中之字元一一指定至被串接
字串之後,此外最後串接後的字串中英文字母
的部份都改為大寫字元。
8-2 指標與陣列的應用
56
程式範例:指標傳回值Strcat()字串函數實作範
例:CH08_16.c
8-2 指標與陣列的應用
57
8-2 指標與陣列的應用
58
執行結果
8-2 指標與陣列的應用
59
程式解說
8-2 指標與陣列的應用
第4行是指標傳回值的字串串接函數。
第8、9行宣告兩個有60字元的字串陣列。
第28行尋找str1的結束字元‘\0’位置,並計算i的長度,第
31~32行將小寫字元轉為大寫。第36~37行逐一加上str2
的字元,並將小寫字元轉為大寫。
第41行加上空字元,代表字串結束。
60
一維指標陣列的宣告格式:
例如以下是宣告一個名稱為p的整數指標陣列,
每個元素(p[i])皆可指向一整數值,另外一個則
是宣告一個名稱為ptr的浮點數指標陣列:
指標陣列(1)8-2 指標與陣列的應用
資料型態 *陣列名稱[元素名稱];
int *p[3];
float *ptr[4];
61
一維指標陣列的應用特別在儲存字串上相當實用。
例如之前介紹使用二維字元陣列,當一個字串陣列
的宣告方式如下所示:
為了避免記憶體空間的浪費,我們就相當適合使用
「指標陣列」來儲存字串,我們可以將之前的宣告
更改為以下的方式:
8-2 指標與陣列的應用
char name[4][11] = { "apple", "watermelon", "Banana",
"orange" };
char *name[4] = { "apple", "watermelon", "Banana",
"orange" };
62
指標陣列(2)
程式範例:一維指標陣列與字串儲存的應用範
例:CH08_17.c
8-2 指標與陣列的應用
63
執行結果
程式解說
8-2 指標與陣列的應用
我們於第06行宣告陣列時加入了一個空字串,這是
為了方便計算指標陣列儲存字串時所佔有的空間。
在第11行列印字串內容及儲存起始位址。
第12行各位可從兩個位址相減會得到位移量來判斷
所佔用儲存空間。
64
指標陣列(3)
程式範例:一維指標陣列與氣泡排序法的應用
範例:CH08_18.c
8-2 指標與陣列的應用
65
8-2 指標與陣列的應用
66
執行結果
程式解說
8-2 指標與陣列的應用
第7行宣告並設定字串陣列內容。
第10行宣告一維指標陣列及字元指標變數。
第17行將一維指標陣列每個元素指向字串陣列的每個位址。
第31行輸出排序後一維指標陣列的內容。
67
動態配置變數(1)
動態配置一般變數的方式如下,如果n=1,即
表示一個變數:
當執行時期動態配置的一般變數不需要時,可
以將其釋放,釋放動態配置的一般變數的方式
如下:
8-3 動態配置記憶體
資料型態* 指標名稱=(資料型態*)malloc(sizeof(資料型態)*n);
free(指標名稱);
68
動態配置變數(2)
程式範例:動態配置變數的宣告與應用範例:
CH08_19.c
8-3 動態配置記憶體
69
8-3 動態配置記憶體
70
執行結果
程式解說
8-3 動態配置記憶體
第6、8行將浮點數與整數指標指向動態配置記憶空間。
第7、9行分別設定浮點數與整數指標變數的起始值。
第15、16行使用free()函數釋放piVal與 piCal指標所指
向的記憶體空間。
71
動態配置變數(3)
程式範例:動態配置字串變數的宣告與應用範
例:CH08_20.c
8-3 動態配置記憶體
72
執行結果
8-3 動態配置記憶體
73
程式解說
8-3 動態配置記憶體
第7行動態配置記憶與str1字串相同大小空間,而第
10行將str1字串複製到str2字串。
第13行輸出str2的位址與所指向的內容值。
第15行釋放str2字串記憶空間。
第13行輸出釋放後的str2位址與所指向的內容值。
74
動態配置陣列(1)
動態配置一維陣列的方式如下,n=陣列長度:
當執行時期動態配置的一維陣列不需要時,可
以將其釋放,釋放動態配置的一維陣列的方式
如下:
8-3 動態配置記憶體
資料型態* 指標名稱=(資料型態*)malloc(n*sizeof(資料型態));
free(指標名稱);
75
動態配置陣列(2)
程式範例:動態配置一維陣列的宣告與應用範
例:CH08_21.c
8-3 動態配置記憶體
76
8-3 動態配置記憶體
77
執行結果
8-3 動態配置記憶體
78
程式解說
8-3 動態配置記憶體
第10行請輸入欲產生的動態一維陣列個數,第11行
將整數指標指向動態配置一維陣列記憶空間。
第13~16行輸入max個數的陣列元素值。
第24行釋放指標指向的記憶空間。
79
上機實習課程(1)
上機實習範例:CH08_22.c
– 以下程式範例是利用指標常數方式來表示三維
陣列元素位址的方法與直接使用「&」取址運算
子取得三維陣列元素位址的比較。
– arr陣列元素內容如下:
8-4 上機實習課程
A[4][3][3]={{{1,-2,3},{4,5,-6},{8,9,2}},
{{7,-8,9},{10,11,12},{0.8,3,2}},
{{-13,14,15},{16,17,18},{3,6,7}},
{{19,20,21},{-22,23,24},(6-,9,12)}};
80
8-4 上機實習課程
81
8-4 上機實習課程
82
執行結果
8-4 上機實習課程
83
上機實習課程(2)
上機實習範例:CH08_23.c
– 延續上題,以下程式範例是利用指標常數方式
來取得以下三維陣列arr的元數值,並計算每個
陣列元素值總和。
8-4 上機實習課程
84
執行結果
8-4 上機實習課程
85
上機實習課程(3)
上機實習範例:CH08_24.c
– 以下程式範例中利用指標變數指向arr陣列,並
將此指標變數傳遞到計算次方值的cubic()函數,
再利用函數中的指標變數加法運算,來將每一
個元素值次方運算及所有新元素的總和。最後
再輸出所有新元素及總和。
8-4 上機實習課程
86
8-4 上機實習課程
87
執行結果
8-4 上機實習課程
88
上機實習課程(4)
上機實習範例:CH08_25.c
– 現在有三個整數陣列num1、num2、num3,其中分別存放二位數整數、三位數整數與四位數整數,如下所示:
– 請設計一程式,利用指標陣列的三個元素值指向這三個陣列,並透過這個指標陣列來輸出此三個整數陣列的所有元素值。
8-4 上機實習課程
int num1[]={ 15,23,31 };
int num2[]={ 114,225,336 };
int num3[]={ 1237,3358,9271 };
89
8-4 上機實習課程
90
執行結果
8-4 上機實習課程
91
上機實習課程(5)
上機實習範例:CH08_26.c
– 以下程式範例只要是說明函數名稱本身也代表
了一個記憶體位址,當您呼叫函數時,其實就
是在告訴程式,執行該函數名稱所指向的記憶
體位址中之程式,這與函數後加上括號的呼叫
方式是有不同。
8-4 上機實習課程
92
執行結果
8-4 上機實習課程
93
上機實習課程(6)
上機實習範例:CH08_27.c
– 延續上題的概念,函數名稱其實也是個指標變
數,其本身所儲存的值為函數內容所在的記憶
體起始位址,因此函數也可當成指標來宣告,
稱為「函式指標」。
– 其作用在於使用同一個函式指標名稱,主要作
用在於程式執行期間動態地決定該呼叫的函式。
宣告方式如下:
8-4 上機實習課程
資料型態 (*ptr)(參數1, 參數2, …)
94
8-4 上機實習課程
95
執行結果
8-4 上機實習課程
96
上機實習課程(7)
上機實習範例:CH08_28.c
– 請將一個整數指標變數指向二維陣列arr,並利
用指標運算將陣列中每一個元素值輸出。
– arr陣列內容如下:
8-4 上機實習課程
arr[4][3]={312,16,35,65,52,111,66,88,44,55,99,100};
97
執行結果
8-4 上機實習課程
98
上機實習課程(8)
上機實習範例:CH08_29.c
– 請將一個字元指標變數指向由使用者輸入的字串,
並利用指標運算將字串中每一個字元輸出。
8-4 上機實習課程
99
執行結果
8-4 上機實習課程
100
上機實習課程(9)
上機實習範例:CH08_30.c
– 請設計一函數replace(),可在使用者所輸入的
字串中指定位置及打算更換的字元,函數中將
使用字元指標來處理運算及置換過程。
8-4 上機實習課程
101
8-4 上機實習課程
102
執行結果
8-4 上機實習課程
103
上機實習課程(10)
上機實習範例:CH08_31.c
– 下面這個程式範例則是說明了三重指標的宣告
與使用方法,依據相同的方法也可以宣告更多
重指標。
8-4 上機實習課程
104
執行結果
8-4 上機實習課程
105
上機實習課程(11)
上機實習範例:CH08_32.c
– 我們宣告一個指標變數p1並設定它的初值為指向陣列變數array1的第五個元素的記憶體位址:
– 例如當我們將指標變數p1減1之後,就是將目前指標變數p1所指向的記憶體位址往前移動1次,也就是指向陣列變數array1的第四個元素,請看以下程式碼的實作驗證。
8-4 上機實習課程
int i,array1[5]={100,200,300,400,500};
int *p1=array1;
p1=&array1[4];
106
8-4 上機實習課程
107
執行結果
8-4 上機實習課程
108
上機實習課程(12)
上機實習範例:CH08_33.c
– 以下程式範例是利用指標常數方式來表示三維
陣列元素位址的方法與直接使用「&」取址運算
子取得三維陣列元素位址的比較。
– arr陣列元素內容如下:
8-4 上機實習課程
A[4][3][3]={{{1,-2,3},{4,5,-6},{8,9,2}},
{{7,-8,9},{10,11,12},{8,3,2}},
{{-13,14,15},{16,17,18},{3,6,7}},
{{19,20,21},{-22,23,24},(-6,9,12)}};
109
8-4 上機實習課程
110
8-4 上機實習課程
111
執行結果
8-4 上機實習課程
112
上機實習課程(13)
上機實習範例:CH08_34.c
– 以下程式範例是說明如果使用指標變數*ptr來存
取二維陣列中第i列的第j行元素可以利用如下公
式來取出該元素值:
8-4 上機實習課程
*(ptr+i*m+j);
113
執行結果
8-4 上機實習課程
114
上機實習課程(14)
上機實習範例:CH08_35.c
– 下面的範例程式以兩種不同方式建立字串,並利用
指標讀出其中的各個字元,顯示於螢幕中。
8-4 上機實習課程
115
執行結果
8-4 上機實習課程
116
上機實習課程(15)
上機實習範例:CH08_36.c
– 以下程式範例是先取得使用者輸入字串,然後
比對是否和字串指標陣列中的某個字串相同。
8-4 上機實習課程
117
8-4 上機實習課程
118
執行結果
8-4 上機實習課程
top related