必備技能 4.9: 指針運算符
在使用指針的時候,有兩個特殊的運算符要用到:*和&。其中&是一個單目運算符,它傳回操作數的記憶體位址。(單目運算符隻需要一個操作數參與運算。)
例如:
ptr = &total;
上面的語句把變量total的位址指派給ptr。這個位址就是變量total在記憶體中的位置。它和變量total的取值沒有任何關系。取位址運算&可以看做是傳回某某變量的位址。是以上面的指派語句可以表述成“為ptr指派為total的位址“。為了更好的了解這種指派運算,我們假設變量total在記憶體中的位址為100。那麼經過這個指派語句後,ptr的值就是100。
第二個運算符是*,它是對&運算符的補充。它也是一個單目的運算符。它傳回運算數的值對應的記憶體位址的值。我們繼續使用前面的那個例子,如果ptr的值為變量total的記憶體位址,那麼
val = *ptr;
将把total變量的值指派給val。例如,如果total變量的值為3200,那麼val的值就是3200,因為3200就是存儲在位置為100的記憶體中的值,而ptr的值就是100。是以*運算符可以看做是傳回某個記憶體位址上的值。是以,上面的這個語句可以表述為“把ptr指向的記憶體位址的值指派給val”。
下面的程式示範了對上面的操作:
#include <iostream>
using namespace std;
int main()
{
int total;
int *ptr;
int val;
total = 3200; //給變量total指派為3200
ptr = &total; //給ptr指派為變量total的位址
//把指針ptr指向的變量的值指派給val,也就是把ptr的值作為一個記憶體位址
//然後把這個記憶體位址中的值指派給val
val = *ptr ;
cout << "Total is:" << val << '\n';
return 0;
}
需要注意的是,乘法的運算符和取某個記憶體位址的值的運算符是相同的。這點對于C++初學者來說可以會引起混淆。這兩者之間沒有任何的關系。請記住,&和*運算符的優先級是高于任何算術運算符的,而它們和單目運算符負的(-)的優先級是相同的。
使用指針的方法通常被稱作是間接的方式,因為我們是通過一個變量間接地通路另外一個變量。
練習:
1. 什麼是指針?
2. 寫出如何聲明一個名為valPrt的指向一個long int類型的指針?
3. 和指針相關的兩個運算符*和&的作用是什麼?
指針的基本類型很重要
在前面的讨論中,我們看到,我們可以通過間接的使用指針來把total變量的值指派給val變量。 你也許會考慮到一個很重要的問題:C++是如何知道需要從ptr所指向的位址中指派多少個位元組的值到val變量中呢?或者,更通用一點的問題:在涉及指針的指派時,C++編譯器是如何決定需要複制的位元組的多少呢?針對前面的例子,因為ptr是一個整型數指針,是以需要從ptr執行的位址開始複制4個位元組資料到val中(假設整型數的大小為32位)。然而,如果ptr被定義成double 類型的指針,則需要複制8個位元組的資料到val中。
指針變量總是指向相應的正确類型的資料,這一點至關重要。例如,當我們聲明一個int類型的指針的時候,C++編譯器會認為它所指向的任何資料都是一個int類型的資料。如果它所指向的資料不是int類型,通常很快就會出現問題。例如,下面的寫法是錯誤的:
int * p;
double f;
//…
p=&f; //這種寫法是錯誤的。
上面的代碼片段是無效的,因為我們不能把一個double類型的指針指派給一個int類型的指針。也就是說,&f産生的是一個double類型的指針,但是p是一個int類型的指針。這兩種類型是不相容的。(實際上,編譯器會在這條語句這裡報告編譯錯誤,說明是不相容指派。)
盡管在指針互相指派的時候,兩個指針的類型必須是相容的,但是我們可以使用強制類型轉換來突破這個限制。例如,下面的代碼種計數上來講是正确的:
int * p;
double f;
//…
p=(int*)&f;
強制的類型轉換把double類型的指針轉換成了一個int類型的指針。然而,這種使用強制類型的做法是有争議的。因為指針的基本類型決定了編譯器如何對待它所指向的資料。在這種情況下,盡管p實際上是指向了一個double類型的資料,但是編譯器會認為p指向的是一個int類型的資料,因為p的基本類型位int類型。為了更好地了解這點,考慮下面的程式:
//下面的程式将不能正确工作。
#include <iostream>
using namespace std;
int main()
{
double x,y;
int *p;
x = 123.33;
p = (int *)&x; //強制地把double類型的指針轉換位int類型的指針
y = *p; // 會發生什麼情況了?
cout << y;
return 0;
}
下面是程式的輸出。(你可能會看到不同的輸出結果。)
-1.20259e+09
這個數值顯然不是123.33!這是為什麼了?在程式中,p是一個int類型的指針, 它被指派為一個double類型的資料的位址。是以,當通過p給y指派的時候,y隻是被複制了4個位元組的資料(而不是double類型的8個位元組),因為p是一個int類型的指針。是以,cout語句輸出的資料不是123.33而是一個垃圾資料。
通過指針來指派
我們可以在指派語句的左邊使用指針來把一個記憶體位址指派給該指針。假設p是一個int類型的指針,下面的指派語句将把101指派給p所指向的位置。
*p=101;
上面的這個指派語句可以表述為:“把101指派給p雖指向的位置。“如果需要對p指向的位置的值進行自增或者自減我們可以使用下面的語句:
(*p)++;
其中括号是有必要的,因為*運算符的優先級是低于++運算符的。
下面的程式示範了使用指針來進行指派:
#include <iostream>
using namespace std;
int main ()
{
int *p, num;
p = #
*p = 100; //通過使用p來給num指派為100
cout << num << ' ';
(*p)++; // 通過p來實作num的自增
cout << num << ' ';
(*p)--; // 通過p來實作num的自減
cout << num << ' ';
return 0;
}
程式的輸出如下:
100 101 100