1. 程式人生 > >關於指標及 * 運算子的一些使用方法

關於指標及 * 運算子的一些使用方法

    對於* 運算子,熟悉C的程式設計師一定對它不陌生,他表示的是指標, 指向記憶體中的某一個變數或者物件。因為指標本身是一個記憶體的地址, 所以指標本身的大小與計算機的記憶體有關(在編譯器位數與計算機相匹配時), 如4GB記憶體(可使用的記憶體)的計算機指標大小為4byte即32位 , 8GB記憶體的計算機指標大小為8byte即64位 (可以使用sizeof 運算子檢視某臺計算機上的指標大小)。 不管指向什麼型別(或類) , 指標本身大小是固定的(這也是為什麼一個類在聲明後可以定義一個指向它的指標而不能初始化物件)。

    對於  * 的用法 , 一種是定義(宣告)指向某個型別的指標 , 使用方法位在型別名稱後面加上 * 運算子 , 注意這個變數是一個指標 , 所以賦給它的值必須是一個地址 。 還有一種用法是解除引用 , 但我們想訪問這個指標指向的物件是 , 在指標變數前面加上 * 即(未初始化、 未賦值的指標行為是未定義的)。

    int * pi;    //宣告一個指標
    int i = 0;    //定義一個變數
    pi = &i;     //使指標指向變數
    *pi = 1;    //解除引用使i的值變為1

    因為pi指向i , 所以最後i 的值為1 ,這就是通過指標間接改變變數的值。

    可能很多C程式設計師習慣把指標當成實參傳入函式並改變它指向的物件的值 , 這裡多說一句:

void change(int * pi){    //把指向整型的指標當成形參傳入函式
     *pi = 0;    //把pi指向的物件的值改成0 , 離開該函式後該物件的值為0
     pi = 0;     //把pi設為空指標, 在該函式作用域內有效 , 離開該函式後作為實參的指標本身的值不變
}
    上述函式的例子告訴我們 , 傳入指標能改變指向物件的值是因為函式把指標複製了 , 也就是拷貝了一份與該指標一樣的地址 , 這樣就可以根據地址改變指向的值 , 但改變指標的值實際上是改變了那個複製而來的值 , 對原指標沒有任何影響。
    空指標的概念是不指向任何物件的指標 , 對於這種指標不能解除引用 。 製造空指標的方法最簡單的是直接賦予指標0這個值 , 也可以用NULL(c++11可以用nullptr)

    當把指標轉為布林值時 , 只要指標不是空指標就返回true , 否則返回false。舉個例子:

//pi是一個指向int型的指標
    if(pi);    //當pi不是空指標時結果為true
    if(*pi);    //當pi指向的值不為0時結果為true


    還有一項常見的操作是對指標加減某一個整數 , 該操作通常用於陣列轉指標 , 但從機器層面來看 , 實際上是對指標的指向進行移位 , 即下移或上移一個數據單元 , 至於移動的大小則根據指向的型別而定 。 即 type * pt ,則移動的大小為type所佔大小位元組數的整數倍。 當type 為int 時 , 每加1移動2(4)個byte , 為char時 , 移動1個byte 。但對於非陣列的指向單值的指標進行移動 , 指向的物件會是未定義的 , 所以不能解除引用。同樣基於上述原因 , 不能用某一個型別的指標指向另個型別的物件 , 這樣做編譯器會報錯 , 因為它無法判定物件到底佔用幾個byte 。

    因為指標中的值是地址 , 所以比較兩個指標就相當於比較兩個地址 , 這樣做是毫無意義的(特殊情況除外 , 比如比較相等==和不等!=) , 應該要比較指標指向的值。

//pi_1與pi_2為兩個指向int型的指標

pi_1 > pi_2;    //比較兩個地址
*pi_1 > *pi_2;    //比較兩個指標指向的值

    有一種非常特殊的指標 void* , 這種指標可以指向任何物件的地址 , 它的意義只是表示一個記憶體地址而已 , 也不能確定指向的物件是什麼。可以把其他指標賦給void* , 但不能將其賦給其它指標。該指標可以作為地址與其他指標比較 , 也可以輸出來檢視地址 。

int i = 1 , *pi = &i;
void * pv = &i;    //合法 , 可賦予任何物件給void *
pv = pi;         //合法 , 可賦予任何指標給void *
if(pv == pi);    //比較兩個地址
//pi = pv;
//非法 , 不能把void *付給其他型別的指標
cout << pv;    //合法 , 輸出一個地址

    對於指向指標的指標 , 可以把它想象成把一個變數放在一個袋子裡, 再在外面再套一層袋子 , 如果要拿到裡面的東西 , 就必須拆掉兩層袋子。

int i = 1 , *pi = &i;
int ** p_pi = &pi    //指向pi的指標
//p_pi = pi;
//錯誤 , 因為pi和p_pi級別不同 , 不能賦值
**p_pi = 0;    //解除兩層引用 , 改變i的值

    對於指標來說,如果指向的是一個類的物件 , 還可以間接訪問類成員。

string s = "Hello,World" , *ps = &s;
cout << ps->size();    //與(*ps).size()等價

    這是指標最基本的用法 , 還有一些跟其他內容組合在一起的下次更新 。(智慧指標 , 動態記憶體分配 , 函式指標以後介紹)