我對變量產生了這些想法

最近在學習Golang的過程當中,發現一個有意思的事情,有的文章說函數調用傳參時 slice 是引用傳遞,有的說是值傳遞。爲何同一個東西你們會不一樣認識?爲了搞清楚其本質,我進行了如下內容的研究:c++

  1. 變量的變量名、變量值、變量地址在內存中是怎麼樣的?
  2. 指針的定義是什麼?引用的定義是什麼?兩者有什麼關係?
  3. 函數傳參中值傳遞、指針傳遞與引用傳遞到底有什麼不同?
  4. Go中 slice 在傳入函數時究竟是不是引用傳遞?若是不是,在函數內爲何能修改其值?

爲了不文章寫的過長,看了想瞌睡,分紅兩篇文章來解釋這個問題,本文先解決問題1跟2,下一篇說明餘下的問題。程序員

變量名程序員給地址取的外號

上學的時候,老師講變量是存在內存中的,內存就像一排排抽屜組成的,每一個抽屜上面有個編號,咱們定義一個變量,就是把想放的東西放到這個對應編號的抽屜裏。好比: int a = 10,用圖來表示下: shell

ref1

這裏:變量的名字叫 a ,變量的值是:10,變量的地址是:0x 00000001。 那麼問題來了,變量的值咱們知道是放在了抽屜裏(內存中),每一個抽屜有編號(地址),可是變量的名字 a 存放在哪裏呢?或者說它會存在於內存中嗎?函數

你們想一個問題,若是變量的名字要存放在內存中,那麼確定分配一個空間給他,保存它的空間有個地址,這個地址是否是又得有個地方存起來程序才能找到?若是真是這樣設計,那麼代碼根本沒發寫、沒法運行了。學習

其實變量名僅僅是寫給程序員看的,讓咱們寫代碼的時候知道這個變量有什麼用,可以經過名字調用變量的值。由於若是直接給你一個地址 0x 23004123,你知道這是要幹嗎嗎?代碼通過編譯後,最終都會轉換成機器碼,咱們定義的變量名就都不存在了,存在的只有地址跟值。ui

指針其實很普通

有了上面的理解,再來一個特殊的變量:指針變量。什麼叫指針變量呢?其實就是這個變量裏邊存放的是一個變量的地址,經過這個地址,機器能夠找到對應變量的值,例如:int * pa = &a,就表示變量 pa 抽屜裏放的是 a 的地址,它的類型是:int*,繼續看圖: spa

ref2

這裏須要重要說明的是:指針pa與a的關係是:a抽屜裏邊放的是變量值10,pa放的是變量的地址:0x00000001,這裏必定要記住,下面說引用的時候才更容易理解。設計

引用就是變量的另外一名字

繼續談引用,引用與指針咱們常常傻傻分不清,由於它們的行爲確實很是詭異,看起來效果很是類似,看代碼:指針

因爲引用的概念是在 c++ 中引入的,所以下面的代碼使用c++,僅僅是一些打印而已,放心看下去code

int main() {
    int a = 10;// 變量
    int * pa = &a; // 指針
    int & b = a; // 引用

    printf("a: %d\n", a);// a: 10
    printf("*pa: %d\n", *pa);// *pa: 10
    printf("b: %d\n", b);// b: 10

    *pa = 20;
    printf("a: %d\n", a);// a: 20
    printf("*pa: %d\n", *pa);// *pa: 20
    printf("b: %d\n", b);// b: 20

    b = 30;
	  printf("a: %d\n", a);// a: 30
    printf("*pa: %d\n", *pa);// *pa: 30
    printf("b: %d\n", b);// b: 30

	  a = 40;
	  printf("a: %d\n", a);// a: 40
    printf("*pa: %d\n", *pa);// *pa: 40
    printf("b: %d\n", b);// b: 40
    return 0;
}
複製代碼

經過上面的代碼咱們發現,指針與引用都能達到一個效果:都有能力修改a的值,指針前面講過了,由於它保存了a的地址,經過解引用操做後,實際上就是打開了a的抽屜,所以能夠進行修改。那麼引用又是怎麼辦到的?這裏注意一個細節:*pa = 20; c = 30;a = 40。咱們看到操做c的時候與操做a是同樣的方式:直接使用變量名,可是pa要想改變a的值,必須進行 *pa 操做(解引用),若是直接 pa=20,這僅僅是改變的pa的值,讓他指向了另一個地址。

爲何引用與變量是同樣的操做方式?先來看一下引用的定義:

引用就是某一變量的一個別名,對引用的操做與對變量直接操做徹底同樣。

那麼別名是什麼意思呢?繼續看圖,一看就懂

ref3
看到了吧?a就是b,b就是a。系統並不會爲引用額外分配空間進行存儲,甚至能夠簡單理解爲:這個別名僅僅是爲了給程序員看的,到機器碼層面的時候,他們都會變成地址:0x 00000001。

有碼爲證

經過上面的分析不知道你理解了幾分?或者你是否是對指針與引用仍是半信半疑?不要緊,寫點代碼證實一下便可,咱們要證實的是:

  • 引用是變量的別名,那麼它的地址應該與變量一致;
  • 指針保存的是變量的地址,那麼它的值是變量的地址,它自身的地址與變量不一樣。

爲了證實,程序設計以下:定義一個變量,分別賦值給指針、引用,而後檢查他們對應的值與地址。

int main() {
    int a = 10;
    printf("%d\n", a);
    printf("%p\n", &a);

    printf("~~~~~~~~~~~~~~\n");
    int * b = &a;
    printf("%p\n", b);
    printf("%p\n", &b);

    printf("~~~~~~~~~~~~~~\n");
    int & c = a;
    printf("%d\n", c);
    printf("%p\n", &c);
    return 0;
}
複製代碼

得到輸出:

10 // 變量a的值
0x7ffee3c7a768 // 變量a的地址
~~~~~~~~~~~~~~
0x7ffee3c7a768 // 指針的值,是變量a的地址
0x7ffee3c7a760 // 指針變量本身的地址
~~~~~~~~~~~~~~
10 // 變量a的值
0x7ffee3c7a768 // 引用變量c的地址,與變量a的地址徹底同樣
複製代碼

在上面若是指針想要打印變量a的值,須要解引用操做:printf(「%d\n」, *b);

小結

  • 變量由三分部分構成:變量名、變量值、變量地址;
  • 變量名實際上只是給程序員看的,編譯後的代碼中並不存在變量名;
  • 指針變量就是一個變量存儲了另一個變量的地址,系統也會爲他分配內存空間來存儲這個地址;
  • 引用實際是變量的別名,他跟變量有相同的地址。

下次預告:

  1. 函數傳參中值傳遞、指針傳遞與引用傳遞到底有什麼不同?
  2. 爲何說 slice 是引用類型?
  3. Go中 slice 在傳入函數時究竟是不是引用傳遞?若是不是,在函數內爲何能修改其值?
相關文章
相關標籤/搜索