C/C++ 基礎程式設計 指標 (Pointer) 講師 : 張傑帆 CSIE, NTU 瘋到自以為能改變世界的人, 就能改變世界 The people who are crazy enough to think they can change the world are the ones who do.-steve Jobs
課程大綱 指標簡介 陣列與指標 動態記憶體配置 指標宣告進階
指標 用途 : 紀錄某資料的記憶體位置, 透過位置間接使用該資料 使用時機 : 當你需要用記憶體中的某一個資料, 但這個資料沒有名稱可直接使用, 可以使用指標這種特別的單位, 透過存放某資料的記憶體位置, 來間接操作這些資料 使用指標的注意事項 : 使用前要先宣告 ( 要幫它取個名字 ) 只能用來儲存記憶體位置 兩個重要的應用 : 動態記憶體配置 函式的設計
指標的概念 儲存另一個資料的位置, 然後間接操作它 int *p; int * *p 間接找到資料並使用 int a = 70; int 0x1000 70 資料內容 名稱 :p 想辦法取得 0x1000 位置! 名稱 :a 位置 :0x1000 沒有名稱可用 : 透過指標! 有名稱可用 : 直接用 a 我要使用 70 那個整數
指標簡介 指標 ( pointer ) 與地址 ( address ) 我們可宣告一變數為一指標, 該變數的值為一記憶体的地址 宣告語法 : 資料型態 * 指標名稱 ; 例如 : 宣告一個整數指標名稱為 p: int *p; p 為一指標, 如果 p 的值為 0x1000, 那麼我們可以說 p 指向記憶体其地址為 0x1000 int *p; int a = 70; p=0x1000 x=70 某個位置 ex:0x800 0x1000
指標簡介 取址運算子 & & 是可當做取址運算子, 用來表示 ( 或取得 ) 一變數的記憶体地址 #include <stdio.h> int main() { int *p; int a = 70; p } p = &a; return 0; p 某個位置 ex:0x800 &a 0x00F4 70 a 0x00F4
指標簡介 間接取值運算子 * 與宣告指標變數之 * 意義不同! 我們可利用指標變數來間接取值, 例如 : #include <stdio.h> int main() { int *p; /* p 為一指標變數 */ int a = 70, b; p = &a; b = *p + 1; /* 與 b = a + 1; 相同 */ *p = 80; return 0; } p 某個位置 ex:0x800 &a *p (=a=70) a 0x00F4 p=&a 70 0x00F4
小練習 命名三變數 int i = 10; int k; int *ptr1; 令 ptr1 取得 i 的記憶體位址 並令 k 透過 ptr1 取得 i 的值 且印出所有變數的記憶體位址與值
課程大綱 指標簡介 陣列與指標 動態記憶體配置 指標宣告進階
關於陣列名稱的機制 陣列名稱可以表示陣列最前面元素的位址 printf("test[0] 的位址為 %p \n", &test[0]); printf("test 的值為 %p \n", test);
陣列與指標 假如一個指標 P 指向陣列 A, 則 P 在語法上也可當陣列使用 如下圖所示 : int A[3]; int *P; P = A; *P ( 或 P[0]) *(P+1) ( 或 P[1]) *(P+2) ( 或 P[2]) P P=A ( 或 P=&A) 某個位置 A A[0] A[1] A[2] &A[0] 位置 : &A[1] &A[2] P+0 P+1 P+2
陣列與指標 範例 #include <stdio.h> int main() { int A[10]={1,2,3,4,5,6,7,8,9,10}; int *P, i; } P=A; // 或 P=&A: 陣列名稱為記憶體位置 for(i=0; i<10; i++) { printf("%d ", P[i]); } printf("\n"); return 0;
陣列與指標 上頁範例也可以這樣寫 : #include <stdio.h> int main() { int A[10]={1,2,3,4,5,6,7,8,9,10}; int *P, i; } P=A; // 或 P=&A: 陣列名稱為記憶體位置 for(i=0; i<10; i++) { printf( %d, *P); // 或 printf("%d ", *(P+i)); P++; } printf("\n"); return 0;
指標的運算 指標運算 是指對目前指標 ( 變數 ) 內所存放的記憶體位址做運算, 由於指標中所存放的是某個資料的記憶體位址, 因此更改指標的內容相當於變更資料的位址, 更改後指標會指到新的記憶體位址, 這是指標如何在記憶體中連續存取資料的方法 指標運算時, 指標一次移動的單位是以目前指標所指向的資料型別所佔用 Bytes 數來計算
陣列與指標 所以 P+i 指的是 P 指到的這個位址後第 i 個整數的記憶體所在位置 *(P+i) 指的就是在 *P 之後第 i 個整數的值 *(P+i) 可以寫成 P[i] 所以, 指向一大塊空間的指標, 可以把它當成陣列語法來用
陣列與指標 陣列與指標相同處 : 陣列名稱代表某資料的記憶體位置 指標名稱代表某資料的記憶體位置 陣列與指標不同處 : 使用陣列名稱不能存放資料, 索引值用來直接存取該內容 使用指標名稱可以存放資料, 索引值用來間接存取該內容
小練習 宣告一陣列 A, 長度為 5 使用一指標 P 存取此陣列 ex: *(P+i) 讓使用者輸入 5 個數字, 並找到最大值
課程大綱 指標簡介 陣列與指標 動態記憶體配置 指標宣告進階
動態記憶體配置 動態記憶體配置則是 : 當程式執行到一半, 發現它需要一塊記憶體空間來存放資料, 才向系統索取一塊記憶體空間 當此記憶體空間用不到時, 也可隨時將之釋放供其它程式使用 動態記憶體配置的特色 : 由於寫程式時無法預知使用者需要多少資料, 因此可設計成在使用者輸入數字個數後, 再動態配置所需的記憶體空間來存放數值
動態記憶體配置 動態記憶體配置 : 先準備好一個指標 跟系統要求空間, 用指標間接操作 使用指標做記憶體配置 : p = (int*)malloc(sizeof(int)*3); int * 不能直接用, 只好間接用 0x3000 名稱 :p 我突然需要使用三個整數當陣列用! 系統 :0x3000 這位置有你要的! int int int [0] [1] [2] 名稱 :( 無 ) 位置 :0x3000
動態記憶體配置的語法 向系統索取記憶體區塊主要是 透過 malloc ( ) 函式來做 此函式的原型宣告放在 stdlib.h 呼叫的語法如下 : ( 資料型態 *)malloc(sizeof( 資料型態 ) * 個數 ); 使用完畢後, 用 free ( 指標名稱 ) 的語法將配置的記憶體釋放 EX: int *ptr = (int*) malloc(sizeof(int) * 3); 00FF012 00FF012 ptr malloc ( 資料沒有名稱 )
動態配置記憶體 int *ptr, n=4; /* 動態配置記憶體 */ ptr=(int*)malloc(sizeof(int)*n); /* 釋放記憶體 */ free(ptr);
動態記憶體配置 範例 : 使用者先輸入一班有幾個學生, 再一一輸入學生的考試成績 #include <stdio.h> #include <stdlib.h> int main() { int students; int *score; int i; } printf("how many students?"); scanf("%d",&students); score=(int *)malloc( sizeof(int)*students ); for ( i=0; i < students; i++ ) { printf("student %d=",i); scanf("%d",&score[i]); } free(score); return 0;
回家作業一 修改上述範例, 算出全班平均
課程大綱 指標簡介 陣列與指標 動態記憶體配置 指標宣告進階
指標陣列 將指標宣告成陣列 ( 很多個指標 ) 用指標陣列配置多個指標. 應用 : 配置列數固定, 行數不固定的二維陣列 範例 : int *a[3]; a[0], a[1], a[2] 也是指標 sizeof(int)*1 int * int * int * a malloc malloc malloc [0] [1] [2] sizeof(int)*2 sizeof(int)*3 int a[0][0] int int a[1][0] a[1][1] int int int a[2][0] a[2][1] a[2][2]
指標陣列 用多個指標配置出不同行數之二維陣列 常見應用 : 固定三個班級, 每班人數不同, 輸入成績 #include <stdio.h> #include <stdlib.h> int main() { int *a[3]; // 宣告 3 個可以指向整數的指標變數 a[0] = (int *)malloc( sizeof(int) ); a[1] = (int *)malloc( sizeof(int)*2 ); a[2] = (int *)malloc( sizeof(int)*3 ); a[0][0] = 10; a[1][0] = 20; a[1][1] = 30; a[2][0] = 40; a[2][1] = 50; a[2][2] = 60; [0] [1] [2] 10 20 30 40 50 60 [0] [1] [2] } return 0;
使用多重指標 指標也可以再指向另一個指標, 這種指標的指標稱之為 多重指標, 此種架構就構成資料結構中的鏈結串列 (Linked list)
上圖中透過指標中的指標來存取資料有下面兩種寫法 :
指標的指標 用指標的指標配置多個指標 ( 為了產生多個指標 ) 用指標的指標配置多個指標. 應用 : 配置列數不固定, 行數也不固定的二維陣列 範例 : int **a; malloc int int int a[0][0] a[0][1] a[0][2] malloc int * int * int * a a[0] a[1] a[2] 某個位置 malloc malloc int int int a[1][0] a[1][1] a[1][2] int int int a[2][0] a[2][1] a[2][2]
指標的指標 動態配置 m 班, 每班 n 人, 輸入成績後計算平均分數 #include <stdio.h> #include <stdlib.h> int main() { int i, j; double sum=0, aver; int **student; int m, n; printf(" 請輸入班級數目 : "); scanf("%d", &m); printf(" 請輸入每班人數 : "); scanf("%d", &n); // 動態配置 m 班各 n 人之記憶體 student = (int **)malloc(sizeof(int *) * m); for ( j=0; j < m; j++ ) student[j] = (int *)malloc(sizeof(int) * n); // 分別讀入 m 班, 各 n 個同學成績 for ( j=0; j < m; j++ ) { printf(" 班級 %d:\n", j+1); for ( i=0; i < n; i++ ) { printf(" 學生 %d: ", i+1); scanf("%d", &student[j][i]); } } // 計算總和 for ( j=0; j < m; j++ ) for ( i=0; i < n; i++ ) sum+=student[j][i]; // 求平均值 aver=sum/(m*n); printf( 全校平均為 :%lf\n",aver); return 0; }
回家作業二 修改上述例子, 增加輸出各班平均, 與回收記憶體