# Ch10 指標 > 上一章: [Ch10 指標](https://hackmd.io/@sunfrancis12/BykpM0iza) > 回目錄: [NTCU程式戰鬥營C講義](https://hackmd.io/@sunfrancis12/ByfdXdjG6) ## 指標的概念 在電腦科學中,指標(英語:Pointer),是程式語言中的一類資料類型及其物件或變數_(程式設計),用來表示或儲存一個記憶體位址,這個位址的值直接指向(points to)存在該位址的對象的值。 > [維基百科 -指標 (電腦科學)](https://zh.wikipedia.org/zh-tw/%E6%8C%87%E6%A8%99_(%E9%9B%BB%E8%85%A6%E7%A7%91%E5%AD%B8)) 上述的解釋看起來很難懂對吧? 我們慢慢來看吧 大家可以把指標想像成<a style="color:red">姓名</a>跟<a style="color:red">綽號</a>之間的關係,比如`王安明`的綽號叫`小明` ![image](https://hackmd.io/_uploads/BkGV5-amT.png) 如果我跟A說"我給`小明`10塊錢",那A就會知道我給`王安明`10塊錢 ![image](https://hackmd.io/_uploads/H1jaobaQa.png) 代表即便我沒有直接跟A說"我給`王安明`10塊",A還是可以透過<a style="color:red">綽號</a>跟<a style="color:red">姓名</a>的關係跟知道這件事,這就是**指標Pointer**的基本概念 從上述的例子來看,<a style="color:red">綽號</a>就是我們的**Pointer指標**,而且<a style="color:red">綽號</a>**指向(points to)**<a style="color:red">姓名</a> ![image](https://hackmd.io/_uploads/S107JzTm6.png) ## 指標的宣告 在宣告指標時,我們也必須告訴程式指標指向的變數其變數型態為何 ```c= int *p; //宣告一個指標P,其指向的變數其變數型態為int {指向變數其變數型態} *{指標名稱} ``` ### 指標的初始化 我們會使用`&`符號,告訴程式指標是指向哪個變數 ```c= *p = &num; //指標P指向變數num *{指標名稱} = &要指向的變數名稱 ``` 輸出指標的指向的變數內容: ```c= #include<stdio.h> int main(){ int num,*p = &num; printf("輸入num的值: "); scanf("%d",&num); printf("num的值為: %d\n",num); printf("指向num的指標(*p)其值為: %d",*p); } ``` 輸入內容: ```c= 12 ``` 輸出結果: ```c= 輸入num的值: 12 num的值為: 12 指向num的指標(*p)其值為: 12 ``` ## 指標的現實應用 那為什麼我們要這麼麻煩搞一個指標出來呢?這就要說到指標在現實中的應用了 在現實中,我們程式裡面的每一個變數都會存在記憶體(memory)的某一個處,而記錄這一位置的數字又稱作**記憶體位址(memory address)** ![image](https://hackmd.io/_uploads/BJeucG67p.png) 我們在使用`scanf()`輸入以及初始化指標所使用的`&`符號,就是代表變數在記憶體中的**記憶體位址(memory address)**,此過程又被稱作**reference**或者**取址** 印出變數num的記憶體位址: ```c= #include<stdio.h> int main(){ int num,*p = &num; printf("輸入num的值: "); scanf("%d",&num); printf("num的值為: %d\n",num); printf("變數num的記憶體位址: %p\n",&num); } ``` 輸入內容: ```c= 12 ``` 輸出結果: ```c= 輸入num的值: 12 num的值為: 12 變數num的記憶體位址: 0061FF18 ``` 而**指標Pointer**存取的值正是該變數的記憶體位址: ![image](https://hackmd.io/_uploads/rJOe0M676.png) 印出num和p的值以及其記憶體位址: ```c= #include<stdio.h> int main(){ int num,*p = &num; printf("輸入num的值: "); scanf("%d",&num); printf("num的值為: %d\n",num); printf("變數num的記憶體位址: %p\n",&num); printf("\np的值為: %p\n",p); printf("變數p的記憶體位址: %p\n",&p); printf("\n變數p的值所指向的記憶體位址的值 %d",*p); } ``` 輸入內容: ```c= 12 ``` 輸出結果: ```c= 輸入num的值: 12 num的值為: 12 變數num的記憶體位址: 0061FF1C p的值為: 0061FF1C 變數p的記憶體位址: 0061FF18 變數p的值所指向的記憶體位址的值 12 ``` 將上述輸出結果畫成圖: ![image](https://hackmd.io/_uploads/rJHkl7a7a.png) ## 為什麼要用指標 **指標的用途** * 指標直接指向記憶體中的位址 - 由於指標是直接指向記憶體中的位址,所以執行的效率會比一般呼叫變數來的**更快** * 不會受限於區域變數或者全域變數 - 由於指標是直接指向記憶體中的位址,因此我們可以直接透過記憶體位址更改變數的內容 有了指標後,我們就可以在`function`函式內使用其他函式的區域變數了: ```c= #include<stdio.h> void plus_ten(int *p){ *p += 10; } int main(){ int num,*p = &num; //num為區域變數 printf("輸入num的值: "); scanf("%d",&num); plus_ten(p); printf("輸入function後num的值: %d",num); } ``` 輸入內容: ```c= 12 ``` 輸出結果: ```c= 輸入num的值: 12 輸入function後num的值: 22 ``` ## 陣列與指標 其實我們熟知的陣列,就是由記憶體上面連續的位址組成: ![image](https://hackmd.io/_uploads/r1lt9QaX6.png) 印出陣列所有元素的指標: ```c= #include <stdio.h> int main(void) { int arr[10] = {0}; int *p = arr; for(int i = 0; i < 10; i++) { printf("&arr[%d]: %p", i ,&arr[i]); printf("\t\tptr + %d: %p\n", i, p + i); } return 0; } ``` > [程式碼取自: 指標與陣列 -openhome.cc](https://openhome.cc/Gossip/CGossip/PointerAndArray.html) 輸出內容: ```c= &arr[0]: 0061FEF0 ptr + 0: 0061FEF0 &arr[1]: 0061FEF4 ptr + 1: 0061FEF4 &arr[2]: 0061FEF8 ptr + 2: 0061FEF8 &arr[3]: 0061FEFC ptr + 3: 0061FEFC &arr[4]: 0061FF00 ptr + 4: 0061FF00 &arr[5]: 0061FF04 ptr + 5: 0061FF04 &arr[6]: 0061FF08 ptr + 6: 0061FF08 &arr[7]: 0061FF0C ptr + 7: 0061FF0C &arr[8]: 0061FF10 ptr + 8: 0061FF10 &arr[9]: 0061FF14 ptr + 9: 0061FF14 ``` 使用指標運算來取出陣列的元素值: ```c= #include <stdio.h> int main(){ int num[10] = {9,8,7,6,5,4,3,2,1,0}; //num為區域變數 int *p = num; for(int i=0;i<10;i++){ printf("%d ",*(p + i)); } } ``` 輸出結果: ```c= 9 8 7 6 5 4 3 2 1 0 ``` ## 進階的陣列用法 除了標準的for(i=0;...)的方法,我們也可以使用指標的方式印出迴圈,又稱**迭代**: 透過迭代地方式印出陣列(大家可以去[來源網站](https://openhome.cc/Gossip/CGossip/PointerAndArray.html)查看程式的運作原理): ```c= #include <stdio.h> int main(void) { int arr[] = {10, 20, 30, 40, 50}; int *begin = arr; int *end = *(&arr + 1); for(int *it = begin; it < end; it++) { printf("%d ", *it); } return 0; } ``` > [程式碼取自: 指標與陣列 -openhome.cc](https://openhome.cc/Gossip/CGossip/PointerAndArray.html) 輸出結果: ```c= 10 20 30 40 50 ``` 如果指標中都是非`0`的元素,可以使用以下方式印出: ```c= #include <stdio.h> int main(){ int num[10] = {9,8,7,6,5,4,3,2,1,1}; //num為區域變數 int *p = num; int count = 0; for(;*p;*p++){ //如果*p不存在或等於0,則跳出迴圈 printf("(p + %d): %d \n",count,*p); count++; } } ``` 輸出結果: ```c= (p + 0): 9 (p + 1): 8 (p + 2): 7 (p + 3): 6 (p + 4): 5 (p + 5): 4 (p + 6): 3 (p + 7): 2 (p + 8): 1 (p + 9): 1 ``` 迭代的終極用法: ```c= #include <stdio.h> int main(){ int num[10] = {9,8,7,6,5,4,3,2,1,0}; //num為區域變數 for(int i : num){ printf("%d ",i); } } ``` 輸出結果: ```c= 9 8 7 6 5 4 3 2 1 0 ``` :::warning 誠心建議在沒有了解這個迭代的運作原理之前,先不要用此方法 ::: ### 使用其他區域的陣列 我們也可以透過上述我們學到關於陣列的性質,透過指標的方式在使用位於其他function的陣列: ```c= #include<stdio.h> int* plus_ten(int arr[],int length){ for(int i=0;i<length;i++){ arr[i] +=10; } return arr; } int main(){ int num[10] = {9,8,7,6,5,4,3,2,1,0}; //num為區域變數 int *p = num; printf("陣列num的值: \n"); for(int i=0;i<10;i++) printf("%d ",num[i]); p = plus_ten(num,10); printf("\n\n輸入function後陣列num的值: \n"); for(int i=0;i<10;i++) printf("%d ",num[i]); } ``` 輸出結果: ```c= 陣列num的值: 9 8 7 6 5 4 3 2 1 0 輸入function後陣列num的值: 19 18 17 16 15 14 13 12 11 10 ``` --- > 上一章: [Ch10 指標](https://hackmd.io/@sunfrancis12/BykpM0iza) > 回目錄: [NTCU程式戰鬥營C講義](https://hackmd.io/@sunfrancis12/ByfdXdjG6)