在C語言中,&符號大家一定很熟悉吧。
它除了可以作為按位元運算“與”之外還有更常用的功能——取變數地址。
我們首先看下面一個簡單的例子:
- #include <stdio.h>
- int main(void)
- {
- int a = 0;
- int *p = &a;
- printf("The value is: %d/n", *p);
- return 0;
- }
上面代碼中,指標p指向變數a的地址。在C/C++中,每個變數都有其相應的地址,通過在變數標識符前加&符號即可獲得變數的地址。
那麼我們這麼寫可以嗎?int *p = &0x01000;
這顯然不行。因為對於一個數值常量,它是沒有地址的。而變數之所以有地址就是因為要有一個儲存單元對變數進行標識(當然,變數也可以直接映射到某個寄存器)。
我們再看下面的代碼:
- #include "stdio.h"
- int main(void)
- {
- int a = 0; // &a = 0x0012ff60
- int *p = &*(int*)0x0012ff60;
- printf("The value is: %d/n", *p);
- return 0;
- }
上面代碼又是怎麼回事呢?
先前已經調查過變數a的地址——0x0012ff60,那麼這裡的指標p實際上也是指向變數a的地址。
首先,將0x0012ff60作為int*,這時它與&a是等價的。
然後*(int*)0x0012ff60表示取變數a的內容。
最後,&*(int*)0x0012ff60表示再脫去*(int*)0x0012ff60的解引用,相當於又變為(int*)&a。
因此,這裡的&與第一個例子中的&是不同的語義。這裡的&不是取地址,因為一個*(int*)0x0012ff60不是變數,它是沒有地址的。每一個變數標識符在編譯期間,編譯器會為它們建立一個符號表,其中存放著變數標識符相應的各種屬性,如類型、地址標識等。地址標識在串連後即可確定邏輯地址值。簡而言之,&作為取地址操作,若且唯若&後面跟著的是變數或函數標識符。所以這裡的&表示脫去解引用。
由此我們可以得出:&作為取地址操作時,其行為結果是在編譯時間就被確定的;而*,解引用操作(或取內容)操作,其行為結果只能在運行時才可被確定。
再看下面的例子,加深印象:
- #include "stdio.h"
- int main(void)
- {
- int a = 0;
- int *p = &*&*&a;
- printf("The value is: %d/n", *p);
- return 0;
- }
不過,&符號不象解引用*能用多次,它只能放在變數標識符或一次解引用前。下面舉一個例子
int main(void){ int a = 100; int *p = &a; int **pp = &p; int **qq = &*&*pp; // OK int **rr = *&*&pp; // OK int **ss = &&**pp; // ERROR}
由於經過一次&來脫去解引用之後,當前運算式就不為左值。而&必須放在一個左值前,因為只有左值才能確保引用是有效,呵呵。
在C++中,&還可以表示引用,這個就不多說了。
- #include "iostream"
- using namespace std;
- int main(void)
- {
- int a = 0;
- int &r = a;
- cout << "The value is: " << r << endl;
- return 0;
- }