「學C語言如果不會用指標,那千萬別說學過」我非常讚同這句話..
對於一般人,指標往往是進入 C 語言後難以跨過的障礙,我也不例外..
這篇主要是在說明指標變數的基本觀念..
1. 指標的宣告與基本使用
假設我今天有一變數宣告成: int a = 10;
而我現在又要再宣告一個指標 ptr 想要儲存變數 a 的位址,有二種宣告方式可以達成。
請記住二點,(a) 我現在要指向的是 "一個整數變數 a" (b) 我的指標是來 存位址值 的
(1.1) 不賦初值的宣告方式
int *ptr;
ptr = &a;
(1.2) 賦予初值的宣告方式
int *ptr = &a;
由於我指向的變數是 int ,所以宣告時是宣告成 int *ptr,如果今天換成 double b; 那麼宣告方式也要換成 double *ptr; 這是指標的宣告。先以(1.1) 為例,其實第一行並不是好的習慣,當你一開始宣告還不知道要指向哪個變數的時候,請記得這麼寫法:
int *ptr = NULL;
先將指標指向 NULL ,之後要用到的時候再抓出來用就可以了。先說一下 &a 代表什麼意思, &a 就是指我程式中, a 所在記憶體的位置。這下總算清楚了吧.. (1.1) 第二行為例:
ptr = &a;
等於是說,我把a 的位址(也就是&a)存到 ptr 中。現在,馬上用圖解說明 (1.1) 的指令..
2. 圖解說明
(2.1) int a = 10;
現在第一步是宣告一個整數變數 a,此時 OS 會為程式配罝一個記憶體位置給變數a,我們假設記憶體配置到的位置是12FF80,同時由於我在宣告時,也設了 a 的初值為 10,所以變數 a 的儲存值即為 10,示意圖如下所示。
儲存值 |
…….. |
…….. |
…….. |
…….. |
10 |
….. |
記憶體位址 |
…….. |
…….. |
…….. |
…….. |
12FF80 |
12FF81 |
|
|
|
|
|
變數a |
|
(2.2) int *ptr;
接著我宣告了一個指向整數的 整數指標變數 ptr,OS會配置一個記憶體位置給 ptr,在這裡也假設他的位址是12FF54。記得,ptr 是用來儲存記憶體位置的。但由於我現在 ptr 並還沒給初值(也就是說我還沒讓 ptr 指向某個變數),所以 ptr 的值現在都還不知道。示意圖如下所示:
儲存值 |
?????? |
…….. |
…….. |
…….. |
10 |
….. |
記憶體位址 |
12FF54 |
12FF55 |
12FF56 |
…….. |
12FF80 |
12FF81 |
|
指標ptr |
|
|
|
變數a |
|
(2.3) ptr = &a;
再來,我將 a 變數的位址值(也就是&a) 給 ptr,所以指標變數 ptr 就存變數 a 的位址。
示意圖如下所示:
儲存值 |
12FF80 |
…….. |
…….. |
…….. |
10 |
….. |
記憶體位址 |
12FF54 |
12FF55 |
12FF56 |
…….. |
12FF80 |
12FF81 |
|
指標ptr |
|
|
|
變數a |
|
當然,如果像是
int a = 10;
int *ptr = &a;
這種宣告直接賦予初值的話,(2.2) 的步驟將會跳過。
3. 如何使用指標取得變數值
請先記得下列二種運算子
(3.1) & : 取址運算子,如我所說的, &a 就是取得 a 的位址,
(3.2) * : (依址取值運算子), 以上面的 (2.3) ptr = &a; 為例,現在 ptr 已經存放了 a 變數的位址,所以
printf("%0X\n", ptr); 是顯示 ptr 的內容值,也就是 12FF80
printf("%0X\n", *ptr); 由於 ptr 是 12FF80,所以跑到位址 12FF80 的地方去抓它的值,故顯示出來就是10
4. 使用指標注意事項
(4.1) 儲存位址的變數一定要是指標:當然有人會想到,既然我只是要存放一個數值,只是這個數值是位址,那為什麼不使用這種方式存放呢?
int a = 0;
int ptr = &a; // complier error
原因很簡單,在 C/C++ 裡面要存變數的位址值,就是要用指標的型態去存
(4.2) 在指標變數還沒確定指向某一變數時,不能取用:指標變數主要的功能是 "依址取值",如果你還沒給他一個明確的位址值(像是 ptr = &a ,這就有給一個明確的位址值了)就進行調用,到時程式執行時,會從怪怪的記憶體位址去抓值出來,(因為你不知道指標裡面放的是什麼),假設指標一開始沒有初始化,所存的值是 0XFFFFFF,又假設 0XFFFFFF 是 OS 裡不可存取的記憶體位址,那麼將會發生不可預期的後果(run time error)
(4.3) 指標不能指向一個常數:如果你已經 #define N 100,那麼指標就不能指向 N。
在了解上述的原理之後,接下來附上一段 source code ,建議最好是準備紙筆畫位址 - 儲存址 的大概對應圖,如果下面的 code 有些部份為什麼 comment 掉不懂的話,請將上面再詳細看幾遍,有好處沒壞處的。程式執行結果不了解了,也歡迎留言與我討論。
5. 程式碼
// ====================================
// FileName: BasicPtr.cpp
// Author : Edison.Shih.
// Complier: VC 2008
#include <stdio.h>
// ====================================
int main(int argc, char **argv)
{
/*
int *ptr1;
printf(" ptr1 = %0X\n", ptr1); // runtime error
printf("&ptr1 = %0X\n", &ptr1); // runtime error
printf("*ptr1 = %0X\n", *ptr1); // runtime error
*/
printf("========================================== \n");
int a = 0X10;
printf(" a = %0X\n", a); // 10
printf(" &a = %0X\n", &a); // the address of a
// printf(" *a = %0X\n", *a); // complier error.
printf("============ int *ptr2 = NULL ============ \n");
int *ptr2 = NULL;
printf(" ptr2 = %0X\n", ptr2); // 0,
printf("&ptr2 = %0X\n", &ptr2); // ptr2 of address, ex:12FF60
// printf("*ptr2 = %0X\n", *ptr2); // logic error, runtime error
printf("================ ptr2 = &a =============== \n");
ptr2 = &a;
printf(" ptr2 = %0X\n", ptr2); // the address of a, =&a
printf("&ptr2 = %0X\n", &ptr2); // ptr2 of address, ex:12FF60
printf("*ptr2 = %0X\n", *ptr2); // the value of a, =10
return 0;
}
// ====================================
6. 執行結果
==========================================
a = 10
&a = 12FF60
============ int *ptr2 = NULL ============
ptr2 = 0
&ptr2 = 12FF54
================ ptr2 = &a ===============
ptr2 = 12FF60
&ptr2 = 12FF54
*ptr2 = 10