[C++ 入門] 3. 指標與參照

在開始前先看一段程式碼:

#include <iostream>

using namespace std;

int main()
{
    int a = 87, b;
    b = a;
    a = 69;
    cout << "a = " << a << ", b = " << b << '\n';
    return 0;
}

可能會有人以為是 69 69, 但實際上是 69 87, 原因是我們宣告 a, b 兩個整數變數,他們分別儲存在記憶體的不同位置,而我們強調過:第八行 b = a;b 的值設為 a 當時的值也就是 \(87\), 之後第九行將 a 的值設為 \(69\), 此時對 b 的值毫無影響。那麼假如我們真的有綁定變數的需求,要如何實做呢??

指標

指標向來被視為 C/C++ 中令人聞風喪膽的魔王 (? 其實我覺得沒那麼恐怖。簡單而言,一個變數的指標,就是其在記憶體的地址。看看下面的例子:

#include <iostream>

using namespace std;

int main()
{
    int n = 87, *ptr = &n;
    cout << "n = " << n << ", &n = " << &n << '\n'
         << "ptr = " << ptr << ", *ptr = " << *ptr << '\n';
    return 0;
}

注意到第七行 int *ptr = &n, 我們在變數名稱前加上 * 宣告一個整數的指標變數,透過在 n 前方加上 & 取得 n 的記憶體位置並使 ptr 指向 n. 接著第八行我們輸出 n 值與其記憶體位置,最後輸出 ptr 指向的位置及利用 * 取得其指向的內容。 這次我們修改 n 的值,會發現連帶地 ptr 所指向的值當然也隨之變化。所以指標第一個麻煩的地方,就是,* 既拿來做為宣告指標,又用作解析指標的內容。

const T*, T* const, const T* const

前面我們有介紹過常數變數,不知道還有迷有印象??沒有也沒關係喇 這邊就隨便帶一下,const T\* ptr0; 是常數變數的指標,它自身指向某個常數變數,但它不是常數,所以它可以被修改,指向別的常數變數;T\* const ptr1; 是變數的常數指標,它自身是常數,因此它初始化指向一個變數後,就不能再指向其他變數,但那個變數當然還是可以被修改;const T\* const ptr2; 是常數變數的常數指標,它自身是常數,因此它初始化指向一個常數後,就不能再指向其他常數,而且那個常數當然不能被修改。

到這邊暈惹嗎??舉個例子好惹: 這邊可能需要慢慢體會。

指標的指標的… & MISC

指標除惹指向整數、浮點數、字元等等基本型別,當然還可以指向另一個指標,怎麼樣,好玩吧??

注意到各種整數、浮點數、字元、指標的指標等等其實殊途同歸,本質上是一樣的,都是關於記憶體位置的變數,你可以用介紹過的 sizeof() 確認看看。非常危險地,它們彼此之間是可以互相轉換的,更概括的說,所有指標都可以被視為 void *, 需要強調型別的原因是因為不同型別記憶體儲存的內容當然不一樣,錯誤的解析會引發 Undefined Behaviors.

另外,當我們宣告一個指標,在確定它指向誰之前,不應該去嘗試存取它的值,這同樣是非常危險的。C 傳統上以 NULL 這個巨集表示空指標,通常會被展開為 0, 但還是需要小心使用;C++11 新推出的 nullptr 是個好東西,請多愛用它。

最後,指標還有許多妙用,是陣列、字串及動態配置記憶體的基礎,也是 C++ STL iterator 的精神,等到我們遇見函式在來介紹函式指標吧。

成大教授好文 推介。

參照

指標這種直接碰觸到記憶體的操作,是比較底層、低階的,雖然非常方便同時也十分不安全,因此現代高階語言多半使用參照這個技巧,或譯為參考。

#include <iostream>

using namespace std;

int main()
{
    int a = 87, &b = a;
    a = 69;
    cout << "a = " << a << ", b = " << b << '\n';
    b = 426;
    cout << "a = " << a << ", b = " << b << '\n';
    return 0;
}

int &b = a; 這行,我們用 &b 表示 b 是個參照,參考到 a 的值,因此當 a 修改時,b 也會隨之更動;同樣地,當 b 修改時,a 也會隨之更動。

實務上,參照的實作通常就是指標,雖然比較安全,但也少惹許多彈性,包括一定要立即初始化且無法改參考其他的變數。

另外 C++11 新增惹 r-value reference, 不過我們應該很久以後才會討論它。


comments powered by Disqus