當咱們談C++中的變量、指針和引用時,咱們到底在談什麼?

問題

先看一段代碼:shell

int a=1;
    cout<<"普通變量:"<<"值:"<<a<<" 址:"<<&a<<endl;
    return 0;
}
複製代碼

上述代碼,首先定義了一個整型變量a,並賦值爲1,而後打印出變量a的值和地址。程序的運行結果以下:函數

普通變量:值:1 址:0x6dfef8
複製代碼

衆所周知,變量a存放於主存中,當CPU執行到以a爲操做數的指令時,指令的地址碼字段即爲a在主存中的地址,這被稱爲是直接尋址。根據上述運行結果,能夠分析出變量a在主存中的佈局狀況,以下圖所示:佈局

從上圖中,能夠看到,變量a存放於主存中地址爲0x6dfef8的存儲單元中,值爲1。那麼,當變量的類型是指針類型時,它在內存中的佈局是什麼情形呢?再看一段代碼:ui

int a=1;
    cout<<"普通變量:"<<"值:"<<a<<" 址:"<<&a<<endl;
    int *b=&a;
    cout<<"指針變量:"<<"值:"<<b<<" 址:"<<&b<<endl;
    return 0;
}
複製代碼

上述代碼在以前的基礎上,又定義了一個指針變量b,它指向變量a,而後打印出變量b的值和地址。程序的運行結果以下:spa

普通變量:值:1 址:0x6dfef8
指針變量:值:0x6dfef8 址:0x6dfef4
複製代碼

能夠看到,變量b的值爲0x6dfef8,地址爲0x6dfef4。據此繪製出此時的內存分佈以下圖所示:指針

咱們發現,變量b的內容剛好是變量a的地址,由於b是指向a的指針。此外,變量b的地址比a小4,這說明局部變量表所對應的棧是向下增加的。注意到,0x6dfef8-0x6dfef4=4,也就是說,a和b的地址之差爲4,而一般存儲器是以字節做爲最小的尋址單位,所以能夠認爲b佔據4個字節。而b的值,也就是0x6dfef8卻只佔用3個字節,那剩下的字節是幹什麼了呢?這可能有兩種緣由,其一是,CPU對主存按照內存對齊的方式進行訪問,從而用4個字節存儲b;其二是,0x6dfef8不是物理地址,而是邏輯地址,最多見的一種邏輯地址方式是基址尋址,此時的邏輯地址等於相對於基址的偏移量。更重要的是,指針定義了「*」運算符,在咱們的例子中,在咱們的例子中,*b=a,從彙編語言的角度看,*運算就是間接尋址,也就是說,b保存着操做數地址的地址,在指令執行前,要將b的值取出來,再送入指令寄存器的地址碼字段中。最後,咱們再看一下引用變量的狀況,代碼、結果和內存佈局分別以下所示:code

int main(){
    int a=1;
    cout<<"普通變量:"<<"值:"<<a<<" 址:"<<&a<<endl;
    int *b=&a;
    cout<<"指針變量:"<<"值:"<<b<<" 址:"<<&b<<endl;
    int &c=a;
    cout<<"引用變量:"<<"值:"<<a<<" 址:"<<&c<<endl;
    return 0;
}
複製代碼
普通變量:值:1 址:0x6dfef8
指針變量:值:0x6dfef8 址:0x6dfef4
引用變量:值:1 址:0x6dfef8
複製代碼

能夠認爲,變量a和變量c就是同一個變量,由於兩者的值、址,以及在程序中的做用方式徹底一致,所以在C++ Primer中說「引用變量就是變量的別名」。但不一樣之處在於,引用變量要有初始化過程。cdn

總結

上述的分析相對細緻,卻十分囉嗦,看到C++中隨處可見的變量時,是不可能想這麼多的。這裏作一下總結:變量對應着某個存儲單元,具備地址和值。對普通變量的訪問,訪問的是它的值;而對指針變量的訪問(*操做),訪問的是它所指向的變量的值;引用變量就是變量別名。此外,咱們說普通變量和指針做爲函數參數時,是傳值,而引用變量纔是傳址。所謂傳值,是指改變形參變量的內容,而傳址,是指改變形參變量的地址,也就是改變它所對應的存儲單元。地址尋址方式,對於計算器來很容易,但對於人理解卻不太容易。這多是Java中不適用指針的緣由之一吧,但實際上Java中的引用就是C++中的指針,遺憾的是,Java中沒有C++中的引用,在一些狀況下會使得代碼比較複雜。blog

相關文章
相關標籤/搜索