在〈變數〉曾經說過,變數提供具名稱的記憶體儲存空間,一個變數關聯一個資料型態、儲存的值與儲存空間的位址值。
如果想知道變數的記憶體位址,可以使用 &
取址運算子(Address-of operator),例如:
#include <stdio.h>
int main(void) {
int n = 10;
printf("n 的值:%d\n", n);
printf("n 的位址:%p\n", &n);
return 0;
}
執行結果:
n 的值:10
n 的位址:0061FECC
這個程式中,宣告了一個 int
整數變數 n
,n
儲存的記憶體位址是 0061FECC,這是 16 進位表示法,如果 int
長度是 4 個位元組,從 0061FECC 後的 4 個位元組是 n
配置到的記憶體空間,現在這個空間中儲存值為 10。
直接存取變數會對分配到的空間作存取,指標(Pointer)是一種變數,儲存記憶體位址,要宣告指標,是使用以下的語法:
type *ptr;
ptr
可儲存位址,而 type
為該位址儲存值的型態,實際宣告的方式如下:
int *n;
float *s;
char *c;
雖然宣告指標時,C 習慣將 *
前置在變數名稱前,不過 n
的型態是 int*
,s
的型態是 float*
,而 c
的型態是 char*
,指標的型態決定了位址上的資料如何解釋,以及如何進行指標運算(Pointer arithmetic)。
可以使用 &
運算子取出變數的位址值並指定給指標,例如:
#include <stdio.h>
int main(void) {
int n = 10;
int *p = &n ;
printf("n 的位址:%p\n", &n);
printf("p 儲存的位址:%p\n", p);
return 0;
}
執行結果:
n 的位址:0061FEC8
p 儲存的位址:0061FEC8
以上的程式使用 &
來取得變數 n
的位址,然後指定給指標 p
,因此 p
儲存的位址就與 &n
取得的位址相同。
可以使用提取 (Dereference)運算子 *
來提取指標儲存的位址中之資料,例如:
#include <stdio.h>
int main(void) {
int n = 10;
int *p = &n;
printf("指標 p 儲存的值:%p\n", p);
printf("取出 p 儲存位址處之值:%d\n", *p);
return 0;
}
執行結果:
指標 p 儲存的值:0061FEC8
取出 p 儲存位址處之值:10
如果已經取得了記憶體位址,將某值指定給 *P
時,該記憶體位址的值也會改變,這相當於告訴程式,將值放到 P
儲存的位址處,例如:
#include <stdio.h>
int main(void) {
int n = 10;
int *p = &n ;
printf("n = %d\n", n);
printf("*p = %d\n", *p);
*p = 20;
printf("n = %d\n", n);
printf("*p = %d\n", *p);
return 0;
}
執行結果:
n = 10
*p = 10
n = 20
*p = 20
當指標 p
儲存的位址與變數 n
的位址相同時,對 *p
進行指定,就會將值直接存入該記憶體位置,因此透過變數 n
取出的值也就改變了。
如果宣告指標但不指定初值,則指標儲存的位址是未知的,存取未知位址的記憶體內容是危險的,例如:
int *p;
*p = 10;
這個程式片段並未初始指標就指定值給 *p
,會造成不可預知的結果,最好為指標設定初值,如果指標一開始不儲存任何位址,可設定初值為 0,例如:
int *p = 0;
在指標宣告時,可以靠在名稱旁邊,也可以靠在關鍵字旁邊,或者是置中,例如:
int *p1;
int* p2;
int * p3;
這三個宣告方式都是可允許的,C 開發者比較傾向用第一個,因為可以避免以下的錯誤:
int* p1, p2;
這樣的宣告方式,初學者可能以為 p2
也是指標,但事實上並不是,以下的宣告 p1
與 p2
才都是指標:
int *p1, *p2;
有時候,只希望儲存記憶體的位址,可以使用 void*
來宣告指標,例如:
void *p;
void*
型態的指標沒有任何型態資訊,只用來儲存位址,不可以使用 *
運算子對 void*
型態指標提取值,而必須轉型至對應的型態,例如:
#include <stdio.h>
int main(void) {
int n = 10;
void *p = &n ;
// 下面這句不可行,void 型態指標不可取值
// printf("%d\n", *p);
// 轉型為int型態指標並指定給iptr
int *iptr = (int*) p;
printf("%d\n", *iptr);
return 0;
}
執行結果:
10
順便來看一下 const
宣告的變數,被 const
宣告的變數一但被指定值,就不能再改變變數的值,雖然可以強制如下改變變數值的:
const int n = 10;
int *p = &n; // warning: initialization discards 'const' qualifier from pointer target type
*p = 20;
printf("%d\n", n);
然而,gcc
會產生警訊,執行程式會顯示 20,如果不想該位址的值被改變,可以用 const
宣告指標,例如:
const int n = 10;
const int *p = &n;
*p = 20; // error, assignment of read-only location
必須留意的是,上面的 p
並不是唯讀,若想要有唯讀的指標,必須使用指標常數,也就是一旦指定給指標值,就不能指定新的記憶體位址值給它,例如:
int x = 10;
int y = 20;
int* const p = &x;
p = &y; // error, assignment of read-only variable `p'
因此,若 x
、y
被 const
宣告,對應的指標常數宣告會是如下:
const int x = 10;
const int y = 20;
const int* const p = &x;
p = &y; // error, assignment of read-only variable `p'