Golang 學(xué)習(xí)筆記五 指針

一、用處

參考
為什么要有指針赚抡?
指針究竟是什么爬坑?是地址?還是類型涂臣?
指針究竟有什么用盾计?

1.變量
一個(gè)東西在內(nèi)存里面,而你想用語(yǔ)言去表示那個(gè)東西,就必須找到一個(gè)表示它署辉。于是我們用常量或變量去表示內(nèi)存里的值灼舍。比如, int a = 2;就是把2這個(gè)值涨薪,放在了內(nèi)存中。(但是你不知道它的位置炫乓,如果你有看到整個(gè)內(nèi)存的能力刚夺,你有可能發(fā)現(xiàn)有一個(gè)2在No.300處)并且你想要在程序中調(diào)用它,就必須有一個(gè)東西代表它末捣,于是用變量a代表了這塊內(nèi)存中的內(nèi)容.有了變量侠姑,你就可以用他表示一個(gè)值。從而你可以使用這個(gè)變量箩做。

2.指針
如果你只有這一行程序的話莽红,那指針就沒(méi)有太大的存在必要了。但是如果你有好幾個(gè)函數(shù)需要讀寫(xiě)這個(gè)值邦邦,那么這時(shí)候問(wèn)題就來(lái)了安吁。
int myMoney = 1000;如果你的賬上有1000元,有好幾個(gè)函數(shù)要操作這個(gè)值燃辖,這時(shí)候就會(huì)產(chǎn)生兩種需求鬼店。在函數(shù)里修改這個(gè)值的時(shí)候是應(yīng)該修改原值呢? 還是不修改原值?如果使用foo(myMoney)這種形式的話,就會(huì)把myMoney代表的內(nèi)存中的內(nèi)容“復(fù)制”一份到函數(shù)棧里黔龟,這樣你在函數(shù)里修改這個(gè)值不會(huì)對(duì)外界有任何影響妇智。但是,如果你想在函數(shù)中對(duì)原值進(jìn)行操作氏身,這時(shí)候就不能只傳進(jìn)來(lái)內(nèi)容巍棱,而需要傳進(jìn)來(lái)一個(gè)myMoney的地址,這樣蛋欣,在函數(shù)里面就能再程序找到那塊地址航徙,把內(nèi)容修改掉。

所以有了傳遞地址的需求陷虎。為了方便傳遞地址捉偏,所以有了指針,指針也是一個(gè)變量泻红,只不過(guò)里面存的內(nèi)容是一個(gè)地址夭禽。總結(jié)地來(lái)說(shuō)谊路, 變量為了表示數(shù)據(jù)而生讹躯, 指針為了傳遞數(shù)據(jù)為生。

3.圖示


(1)C語(yǔ)言聲明一個(gè)變量時(shí),編譯器在內(nèi)存中留出一個(gè)唯一的地址單元來(lái)存儲(chǔ)變量,如下圖潮梯,變量var初始化為100骗灶,編譯器將地址為1004的內(nèi)存單元留給變量,并將地址1004和該變量的名稱關(guān)聯(lián)起來(lái)秉馏。

(2)創(chuàng)建指針:變量var的地址是1004耙旦,是一個(gè)數(shù)字,地址的這個(gè)數(shù)字可以用另一個(gè)變量來(lái)保存它萝究,假設(shè)這個(gè)變量為p免都,此時(shí)變量p未被初始化,系統(tǒng)為它分配了空間帆竹,但值還不確定绕娘,如圖

(3)初始化指針,將變量var的地址存儲(chǔ)到變量p中栽连,初始化后(p=&var)险领,p指向var,稱為一個(gè)指向var的指針秒紧。指針是一個(gè)變量绢陌,它存儲(chǔ)了另一個(gè)變量的地址。

(4)聲明指針:typename *p 其中typename指的是var的變量類型熔恢,可以是 short ,char ,float,因?yàn)槊總€(gè)類型占用的內(nèi)存字節(jié)不同下面,short占2個(gè)字節(jié),char占1個(gè)字節(jié)绩聘,float占4個(gè)字節(jié)沥割,指針的值等于它指向變量的第一個(gè)字節(jié)的地址 。*是間接運(yùn)算符凿菩,說(shuō)明p是指針變量机杜,與非指針變量區(qū)別開(kāi)來(lái)。

(5)*p和var指的是var的內(nèi)容衅谷;p和&var指的是var的地址

4.既然指針是用來(lái)保存地址的椒拗,也就是保存一串?dāng)?shù)字而已,為什么還要有類型获黔?
以 int *a = &b; 為例蚀苛,并假設(shè)這行代碼的運(yùn)行環(huán)境下,int 為 4 字節(jié)長(zhǎng)玷氏。等價(jià)于int *a;a=&b;
類型系統(tǒng)有兩個(gè)重要作用堵未,一個(gè)是用作編譯時(shí)類型檢查,讓編譯器幫助我們避免犯錯(cuò)盏触,比如上面的例子渗蟹,我聲明的是一個(gè) int 型指針块饺,但如果賦值時(shí)使用的 b 不是 int 型(可能酒后寫(xiě)代碼不小心寫(xiě)錯(cuò)了),如果沒(méi)有指針類型檢查的話雌芽,這個(gè)錯(cuò)誤可能只有在程序運(yùn)行的時(shí)候才能發(fā)現(xiàn)授艰。

類型的另外一個(gè)作用是,指明一個(gè)內(nèi)存地址所保存的二進(jìn)制數(shù)據(jù)世落,應(yīng)該怎樣被解釋淮腾。想想看在沒(méi)有指針類型的情況下,雖然我知道了 a 中保存的地址屉佳,但當(dāng)我想解引用指針(*a)谷朝,讀出這塊地址中的一個(gè) int 型數(shù)據(jù)時(shí)就有問(wèn)題了,編譯器怎樣知道你從這個(gè)位置開(kāi)始要讀出一個(gè)字節(jié)忘古,還是 4 個(gè)字節(jié)?而在有 int* 這個(gè)類型的指導(dǎo)下诅诱,編譯器知道在解引用的時(shí)候讀出 4 個(gè)字節(jié)(int* 對(duì)應(yīng)的指針偏移量是 4 字節(jié))髓堪,甚至,你也可以通過(guò)強(qiáng)制類型轉(zhuǎn)換讀出一個(gè) char 類型數(shù)據(jù)來(lái)玩娘荡。

比如我們的調(diào)用過(guò)程希望給子過(guò)程傳遞一個(gè)數(shù)組干旁,假如這個(gè)數(shù)組有5000個(gè)數(shù)吧。那么在如果我們將這個(gè)數(shù)組作為參數(shù)傳遞會(huì)怎樣炮沐?
在傳值的方式下争群,調(diào)用過(guò)程會(huì)將這個(gè)數(shù)組在棧上全部復(fù)制一遍。棧當(dāng)然不可能是無(wú)限大的大年,總是有一個(gè)到頭的地方换薄,如果到頭了還放不下這5000個(gè)數(shù),那么翔试,恩轻要,棧就直接爆掉。然后操作系統(tǒng)提示你棧溢出垦缅,程序崩潰了冲泥。這時(shí)候我們就可以在其它地方開(kāi)辟一塊連續(xù)的內(nèi)存,然后使用指針指向這段內(nèi)存的起始位置壁涎,將指針傳遞給子過(guò)程凡恍。這樣傳遞過(guò)程中,只需要在棧上傳遞一個(gè)數(shù)怔球,不僅省空間嚼酝,而且速度快。

二竟坛、C/C++ 里指針聲明為什么通常不寫(xiě)成 int* ptr 而通常寫(xiě)成 int *ptr ?

C 語(yǔ)言設(shè)計(jì)的本意并不是把「int*」作為類型聲明革半。它的設(shè)計(jì)本意是解一個(gè)方程碑定,int ....,讓 .... 的類型是 int。也就是 *ptr 的類型是 int又官。從而反推出 ptr 是 int 指針延刘。比如int *var0, var1;這種聲明下 var0 是 int 型指針,var1 是 int 類型六敬。

《C++ primer》(第五版)

《C程序設(shè)計(jì)語(yǔ)言》

《C程序設(shè)計(jì)語(yǔ)言》

因此碘赖,int表示的是【*ip】這個(gè)表達(dá)式的類型。從表達(dá)式【*ip】反向推出未使用*操作的ip是個(gè)地址外构。

三辩诞、C++ 引用

參考C++中 引用&與取地址&的區(qū)別
引用是給已定義的變量起別名
引用:在聲明的時(shí)候一定要初始化

#include <iostream>
 
using namespace std;
 
int main()
{
    int a = 88;
    //聲明變量a的一個(gè)引用c,c是變量a的一個(gè)別名
    //引用精刷,聲明的時(shí)候一定要初始化
    int &c = a;  
    //一個(gè)變量可以有多個(gè)引用
    int &d = a;  
    
    cout<<"a="<<a<<endl;
    cout<<"c="<<c<<endl;
    cout<<"====================="<<endl;
    c=99;
    cout<<"a="<<a<<endl;
 
    return 0;
}

指針和引用的區(qū)別

1.首先次员,引用不可以為空,但指針可以為空垒酬。前面也說(shuō)過(guò)了引用是對(duì)象的別名砰嘁,引用為空——對(duì)象都不存在,怎么可能有別名勘究!故定義一個(gè)引用的時(shí)候矮湘,必須初始化。因此如果你有一個(gè)變量是用于指向另一個(gè)對(duì)象口糕,但是它可能為空缅阳,這時(shí)你應(yīng)該使用指針;如果變量總是指向一個(gè)對(duì)象景描,i.e.十办,你的設(shè)計(jì)不允許變量為空,這時(shí)你應(yīng)該使用引用超棺。而聲明指針是可以不指向任何對(duì)象橘洞,也正是因?yàn)檫@個(gè)原因,使用指針之前必須做判空操作说搅,而引用就不必炸枣。

2.其次,引用不可以改變指向弄唧,對(duì)一個(gè)對(duì)象"至死不渝"适肠;但是指針可以改變指向,而指向其它對(duì)象候引。說(shuō)明:雖然引用不可以改變指向侯养,但是可以改變初始化對(duì)象的內(nèi)容。例如就++操作而言澄干,對(duì)引用的操作直接反應(yīng)到所指向的對(duì)象逛揩,而不是改變指向柠傍;而對(duì)指針的操作,會(huì)使指針指向下一個(gè)對(duì)象辩稽,而不是改變所指對(duì)象的內(nèi)容惧笛。

3.再次,引用的大小是所指向的變量的大小逞泄,因?yàn)橐弥皇且粋€(gè)別名而已患整;指針是指針本身的大小,4個(gè)字節(jié)

4.最后喷众,引用比指針更安全各谚。由于不存在空引用,并且引用一旦被初始化為指向一個(gè)對(duì)象到千,它就不能被改變?yōu)榱硪粋€(gè)對(duì)象的引用昌渤,因此引用很安全。對(duì)于指針來(lái)說(shuō)憔四,它可以隨時(shí)指向別的對(duì)象膀息,并且可以不被初始化,或?yàn)镹ULL加矛,所以不安全履婉。const 指針雖然不能改變指向煤篙,但仍然存在空指針斟览,并且有可能產(chǎn)生野指針(即多個(gè)指針指向一塊內(nèi)存,free掉一個(gè)指針之后辑奈,別的指針就成了野指針)

總之苛茂,用一句話歸納為就是:指針指向一塊內(nèi)存,它的內(nèi)容是所指內(nèi)存的地址鸠窗;而引用則是某塊內(nèi)存的別名妓羊,引用不改變指向。

節(jié)選自為什么 C++ 有指針了還要引用稍计?
一般來(lái)說(shuō)指針和引用基本可以互換使用躁绸,這也是為什么 java 只有引用就夠了。c++ 指針的概念完全是因?yàn)?c 語(yǔ)言的歷史遺留臣嚣,為了兼容 c 而設(shè)計(jì)的净刮。好的,那你會(huì)問(wèn)硅则,那為什么不直接用指針淹父,還要搞個(gè)引用這種玩意?這是因?yàn)?c++ 里有個(gè)叫運(yùn)算符重載的東西怎虫,所以 pointer 這個(gè)表達(dá)式很有可能并不是直接取值的意思暑认,因?yàn)?/em>被重載了困介。

四、C++ 值傳遞蘸际、指針傳遞座哩、引用傳遞詳解
#include<iostream>
using namespace std;
//值傳遞
 void change1(int n){
    //顯示的是拷貝的地址而不是源地址 
    cout<<"值傳遞--函數(shù)操作地址"<<&n<<endl;
    n++;
}

//引用傳遞
void change2(int & n){
    cout<<"引用傳遞--函數(shù)操作地址"<<&n<<endl; 
    n++;
}
 //指針傳遞
void change3(int *n){
     cout<<"指針傳遞--函數(shù)操作地址 "<<n<<endl; 
    *n=*n+1;
 } 
int     main(){
    int n=10;
    cout<<"實(shí)參的地址"<<&n<<endl;
    change1(n);
    cout<<"after change1() n="<<n<<endl;
    change2(n);
    cout<<"after change2() n="<<n<<endl;
    change3(&n);
    cout<<"after change3() n="<<n<<endl;
    return true;
}

image.png

關(guān)于指針傳遞和引用傳遞,參考C++ 中的引用傳值和用指針傳值有什么區(qū)別捡鱼,哪種方式更優(yōu)八回?

五、JAVA中值傳遞驾诈、引用傳遞

參考
為什么 Java 只有值傳遞缠诅,但 C# 既有值傳遞,又有引用傳遞乍迄,這種語(yǔ)言設(shè)計(jì)有哪些好處管引?
Java 到底是值傳遞還是引用傳遞?
1.基本類型和引用類型

int num = 10;
String str = "hello";

如圖所示闯两,num是基本類型褥伴,值就直接保存在變量中。而str是引用類型漾狼,變量中保存的只是實(shí)際對(duì)象的地址重慢。一般稱這種變量為"引用",引用指向?qū)嶋H對(duì)象逊躁,實(shí)際對(duì)象中保存著內(nèi)容似踱。
2.賦值運(yùn)算符=

num = 20;
str = "java";

對(duì)于基本類型 num ,賦值運(yùn)算符會(huì)直接改變變量的值稽煤,原來(lái)的值被覆蓋掉核芽。對(duì)于引用類型 str,賦值運(yùn)算符會(huì)改變引用中所保存的地址酵熙,原來(lái)的地址被覆蓋掉轧简。但是原來(lái)的對(duì)象不會(huì)被改變(重要)。如上圖所示匾二,"hello" 字符串對(duì)象沒(méi)有被改變哮独。(沒(méi)有被任何引用所指向的對(duì)象是垃圾,會(huì)被垃圾回收器回收)

3.簡(jiǎn)要結(jié)論:
java中方法參數(shù)傳遞方式是按值傳遞察藐。
如果參數(shù)是基本類型皮璧,傳遞的是基本類型的字面量值的拷貝。
如果參數(shù)是引用類型转培,傳遞的是該參量所引用的對(duì)象在堆中地址值的拷貝恶导。

六、JavaScript中函數(shù)都是值傳遞嗎浸须?
function setName(obj) {
// 這里 obj 和 person 指向內(nèi)存中的同一塊地址惨寿,a 地址
obj.name = "Nicholas";
// 這里 obj 指向了新對(duì)象所在的地址( b 地址)邦泄,切斷了和 a 地址的聯(lián)系
obj = new Object();
obj.name = "Greg";
}
var person = new Object();
setName(person);
alert(person.name); //output: "Nicholas"
七、GO中的指針

參考Go語(yǔ)言參數(shù)傳遞是傳值還是傳引用
1.指針
變量是一種使用方便的占位符裂垦,用于引用計(jì)算機(jī)內(nèi)存地址顺囊。如果用“var x int”聲明語(yǔ)句聲明一個(gè)x變量,那么&x表達(dá)式(取x變量的內(nèi)存地址)將產(chǎn)生一個(gè)指向該整數(shù)變量的指針蕉拢,指針對(duì)應(yīng)的數(shù)據(jù)類型是 *int 特碳,指針被稱之為“指向int類型的指針”。如果指針名字為p晕换,那么可以說(shuō)“p指針指向變量x”午乓,或者說(shuō)“p指針保存了x變量的內(nèi)存地址”。同時(shí) *p 表達(dá)式對(duì)應(yīng)p指針指向的變量的值闸准。一般 *p 表達(dá)式讀取指針指向的變量的值益愈,這里為int類型的值,同時(shí)因?yàn)?*p 對(duì)應(yīng)一個(gè)變量夷家,所以該表達(dá)式也可以出現(xiàn)在賦值語(yǔ)句的左邊蒸其,表示更新指針?biāo)赶虻淖兞康闹怠?/p>

x := 1
p := &x // p, of type *int, points to x
fmt.Println(*p) // "1"
*p = 2 // equivalent to x = 2
fmt.Println(x) // "2"

var a int= 20/* 聲明實(shí)際變量 */
var ip *int  /* 聲明指針變量 */

ip = &a  /* 指針變量的存儲(chǔ)地址 */

fmt.Printf("a 變量的地址是: %x\n", &a  )

/* 指針變量的存儲(chǔ)地址 */
fmt.Printf("ip 變量?jī)?chǔ)存的指針地址: %x\n", ip )

/* 使用指針訪問(wèn)值 */
fmt.Printf("*ip 變量的值: %d\n", *ip )

a 變量的地址是: 20818a220
ip 變量?jī)?chǔ)存的指針地址: 20818a220
*ip 變量的值: 20

在其它語(yǔ)言中,比如C語(yǔ)言库快,指針操作是完全不受約束的摸袁。在另外一些語(yǔ)言中,指針一般被處理為“引用”义屏,除了到處傳遞這些指針之外靠汁,并不能對(duì)這些指針做太多事情。Go語(yǔ)言在這兩種范圍中取了一種平衡湿蛔。指針是可見(jiàn)的內(nèi)存地址膀曾,&操作符可以返回一個(gè)變量的內(nèi)存地址县爬,并且*操作符可以獲取指針指向的變量?jī)?nèi)容阳啥,但是在Go語(yǔ)言里沒(méi)有指針運(yùn)算,也就是不能像c語(yǔ)言里可以對(duì)指針進(jìn)行加或減操作财喳。

任何類型的指針的零值都是nil察迟。如果 p != nil 測(cè)試為真,那么p是指向某個(gè)有效變量耳高。指針之間也是可以進(jìn)行相等測(cè)試的扎瓶,只有當(dāng)它們指向同一個(gè)變量或全部是nil時(shí)才相等。

var x, y int
fmt.Println(&x == &x, &x == &y, &x == nil) 
// "true false false"

下面是多級(jí)指針使用例子

func main() {
    var value int = 42
    var p1 *int = &value
    var p2 **int = &p1
    var p3 ***int = &p2
    fmt.Println(p1, p2, p3)
    fmt.Println(*p1, **p2, ***p3)
}

----------
0xc4200160a0 0xc42000c028 0xc42000c030
42 42 42

2.值傳遞
函數(shù)傳遞的總是原來(lái)這個(gè)東西的一個(gè)副本泌枪,一副拷貝概荷。比如我們傳遞一個(gè)int類型的參數(shù),傳遞的其實(shí)是這個(gè)參數(shù)的一個(gè)副本碌燕;傳遞一個(gè)指針類型的參數(shù)误证,其實(shí)傳遞的是這個(gè)該指針的一份拷貝继薛,而不是這個(gè)指針指向的值。

func main() {
    i:=10
    ip:=&i
    fmt.Printf("原始指針的內(nèi)存地址是:%p\n",&ip)
    modify(ip)
    fmt.Println("int值被修改了愈捅,新值為:",i)
}

 func modify(ip *int){
     fmt.Printf("函數(shù)里接收到的指針的內(nèi)存地址是:%p\n",&ip)
    *ip=1
 }
------------------
原始指針的內(nèi)存地址是:0xc42000c028
函數(shù)里接收到的指針的內(nèi)存地址是:0xc42000c038
int值被修改了遏考,新值為: 1

首先我們要知道,任何存放在內(nèi)存里的東西都有自己的地址蓝谨,指針也不例外灌具,它雖然指向別的數(shù)據(jù),但是也有存放該指針的內(nèi)存譬巫。

所以通過(guò)輸出我們可以看到咖楣,這是一個(gè)指針的拷貝,因?yàn)榇娣胚@兩個(gè)指針的內(nèi)存地址是不同的芦昔,雖然指針的值相同截歉,但是是兩個(gè)不同的指針。

首先我們看到烟零,我們聲明了一個(gè)變量i,值為10,它的內(nèi)存存放地址是0xc420018070,通過(guò)這個(gè)內(nèi)存地址瘪松,我們可以找到變量i,這個(gè)內(nèi)存地址也就是變量i的指針ip。

指針ip也是一個(gè)指針類型的變量锨阿,它也需要內(nèi)存存放它宵睦,它的內(nèi)存地址是多少呢?是0xc42000c028墅诡。 在我們傳遞指針變量ip給modify函數(shù)的時(shí)候壳嚎,是該指針變量的拷貝,所以新拷貝的指針變量ip,它的內(nèi)存地址已經(jīng)變了末早,是新的0xc42000c038烟馅。

不管是0xc42000c028還是0xc42000c038,我們都可以稱之為指針的指針然磷,他們指向同一個(gè)指針0xc420018070郑趁,這個(gè)0xc420018070又指向變量i,這也就是為什么我們可以修改變量i的值。

3.傳結(jié)構(gòu)體

func main() {
    p:=Person{"張三"}
    fmt.Printf("原始Person的內(nèi)存地址是:%p\n",&p)
    modify(p)
    fmt.Println(p)
}

type Person struct {
    Name string
}

 func modify(p Person) {
     fmt.Printf("函數(shù)里接收到Person的內(nèi)存地址是:%p\n",&p)
     p.Name = "李四"
 }
----------
原始Person的內(nèi)存地址是:0xc4200721b0
函數(shù)里接收到Person的內(nèi)存地址是:0xc4200721c0
{張三}

我們發(fā)現(xiàn)姿搜,我們自己定義的Person類型寡润,在函數(shù)傳參的時(shí)候也是值傳遞,但是它的值(Name字段)并沒(méi)有被修改舅柜,我們想改成李四梭纹,發(fā)現(xiàn)最后的結(jié)果還是張三。

我們嘗試把modify函數(shù)的接收參數(shù)改為Person的指針致份。

func main() {
    p:=Person{"張三"}
    modify(&p)
    fmt.Println(p)
}

type Person struct {
    Name string
}

 func modify(p *Person) {
     p.Name = "李四"
 }

在運(yùn)行查看輸出变抽,我們發(fā)現(xiàn),這次被修改了。

4.迷惑Map

func main() {
    persons:=make(map[string]int)
    persons["張三"]=19

    mp:=&persons

    fmt.Printf("原始map的內(nèi)存地址是:%p\n",mp)
    modify(persons)
    fmt.Println("map值被修改了绍载,新值為:",persons)
}

 func modify(p map[string]int){
     fmt.Printf("函數(shù)里接收到map的內(nèi)存地址是:%p\n",&p)
     p["張三"]=20
 }
------------
原始map的內(nèi)存地址是:0xc42000c028
函數(shù)里接收到map的內(nèi)存地址是:0xc42000c038
map值被修改了太伊,新值為: map[張三:20]

兩個(gè)內(nèi)存地址是不一樣的,所以這又是一個(gè)值傳遞(值的拷貝)逛钻,那么為什么我們可以修改Map的內(nèi)容呢僚焦?我們可以大膽的猜測(cè),我們使用make函數(shù)創(chuàng)建的map是不是一個(gè)指針類型呢曙痘?看一下源代碼:

// makemap implements a Go map creation make(map[k]v, hint)
// If the compiler has determined that the map or the first bucket
// can be created on the stack, h and/or bucket may be non-nil.
// If h != nil, the map can be created directly in h.
// If bucket != nil, bucket can be used as the first bucket.
func makemap(t *maptype, hint int64, h *hmap, bucket unsafe.Pointer) *hmap {
    //省略無(wú)關(guān)代碼
}

現(xiàn)在看func modify(p map)這樣的函數(shù)芳悲,其實(shí)就等于func modify(p *hmap)。所以在這里边坤,Go語(yǔ)言通過(guò)make函數(shù)名扛,字面量的包裝,為我們省去了指針的操作茧痒,讓我們可以更容易的使用map肮韧。類似map的,還有chan類型:

func makechan(t *chantype, size int64) *hchan {
    //省略無(wú)關(guān)代碼
}

5.不太一樣的slice

func main() {
    ages:=[]int{6,6,6}
    fmt.Printf("原始slice的內(nèi)存地址是%p\n",ages)
    modify(ages)
    fmt.Println(ages)
}

func modify(ages []int){
    fmt.Printf("函數(shù)里接收到slice的內(nèi)存地址是%p\n",ages)
    ages[0]=1
}

運(yùn)行打印結(jié)果旺订,發(fā)現(xiàn)的確是被修改了弄企,而且我們這里打印slice的內(nèi)存地址是可以直接通過(guò)%p打印的,不用使用&取地址符轉(zhuǎn)換。這就可以證明make的slice也是一個(gè)指針了嗎区拳?不一定拘领,也可能fmt.Printf把slice特殊處理了。

6.小結(jié)
最終我們可以確認(rèn)的是Go語(yǔ)言中所有的傳參都是值傳遞(傳值)樱调,都是一個(gè)副本约素,一個(gè)拷貝。因?yàn)榭截惖膬?nèi)容有時(shí)候是非引用類型(int笆凌、string圣猎、struct等這些),這樣就在函數(shù)中就無(wú)法修改原內(nèi)容數(shù)據(jù)乞而;有的是引用類型(指針送悔、map、slice晦闰、chan等這些)放祟,這樣就可以修改原內(nèi)容數(shù)據(jù)鳍怨。

是否可以修改原內(nèi)容數(shù)據(jù)呻右,和傳值、傳引用沒(méi)有必然的關(guān)系鞋喇。在C++中声滥,傳引用肯定是可以修改原內(nèi)容數(shù)據(jù)的,在Go語(yǔ)言里,雖然只有傳值落塑,但是我們也可以修改原內(nèi)容數(shù)據(jù)纽疟,因?yàn)閰?shù)是引用類型。

這里也要記住憾赁,引用類型和傳引用是兩個(gè)概念污朽。

再記住,Go里只有傳值(值傳遞)龙考。

7.關(guān)于字符串的傳遞蟆肆,可以參考golang的官方字符串包里為什么都用string類型傳入?yún)?shù)而不是*string?
string的底層是一個(gè)結(jié)構(gòu)體晦款,包括一個(gè)指針和一個(gè)長(zhǎng)度炎功。傳參的時(shí)候是值傳遞,把string的描述結(jié)構(gòu)體復(fù)制了一次缓溅,所以兩個(gè)結(jié)構(gòu)體的指針不一樣蛇损,同時(shí)把底層字節(jié)數(shù)組的指針也復(fù)制了,兩個(gè)字節(jié)數(shù)組的指針指向同一段區(qū)域坛怪,也就是字符串字節(jié)數(shù)組存放的區(qū)域淤齐,沒(méi)有發(fā)生字符串的整體復(fù)制

8.golang語(yǔ)法自動(dòng)處理
參考<<go語(yǔ)言圣經(jīng)>>P143

type Employee struct {
ID int
Name string
Address string
DoB time.Time
Position string
Salary int
ManagerID int
}
var dilbert Employee
var employeeOfTheMonth *Employee = &dilbert
employeeOfTheMonth.Position += " (proactive team player)"
//相當(dāng)于下面語(yǔ)句
(*employeeOfTheMonth).Position += " (proactive team player)"

9.new函數(shù)
參考<<go語(yǔ)言圣經(jīng)>>P62
表達(dá)式new(T)將創(chuàng)建一個(gè)T類型的匿名變量,初始化為T類型的零值袜匿,然后返回變量地址床玻,返回的指針類型為 *T 。

p := new(int) // p, *int 類型, 指向匿名的 int 變量
fmt.Println(*p) // "0"
*p = 2 // 設(shè)置 int 匿名變量的值為 2
fmt.Println(*p) // "2"

用new創(chuàng)建變量和普通變量聲明語(yǔ)句方式創(chuàng)建變量沒(méi)有什么區(qū)別沉帮,除了不需要聲明一個(gè)臨時(shí)變量的名字外锈死,我們還可以在表達(dá)式中使用new(T)。換言之穆壕,new類似是一種語(yǔ)法糖待牵,而不是一個(gè)新的基礎(chǔ)概念。下面的兩個(gè)newInt函數(shù)有著相同的行為:

func newInt() *int {
return new(int)
}
func newInt() *int {
var dummy int
return &dummy
}

每次調(diào)用new函數(shù)都是返回一個(gè)新的變量的地址喇勋,因此下面兩個(gè)地址是不同的:

p := new(int)
q := new(int)
fmt.Println(p == q) // "false"
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末缨该,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子川背,更是在濱河造成了極大的恐慌贰拿,老刑警劉巖,帶你破解...
    沈念sama閱讀 211,948評(píng)論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件熄云,死亡現(xiàn)場(chǎng)離奇詭異膨更,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)缴允,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,371評(píng)論 3 385
  • 文/潘曉璐 我一進(jìn)店門荚守,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人,你說(shuō)我怎么就攤上這事矗漾⌒夂颍” “怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 157,490評(píng)論 0 348
  • 文/不壞的土叔 我叫張陵敞贡,是天一觀的道長(zhǎng)泵琳。 經(jīng)常有香客問(wèn)我,道長(zhǎng)誊役,這世上最難降的妖魔是什么虑稼? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 56,521評(píng)論 1 284
  • 正文 為了忘掉前任,我火速辦了婚禮势木,結(jié)果婚禮上蛛倦,老公的妹妹穿的比我還像新娘。我一直安慰自己啦桌,他們只是感情好溯壶,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,627評(píng)論 6 386
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著甫男,像睡著了一般且改。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上板驳,一...
    開(kāi)封第一講書(shū)人閱讀 49,842評(píng)論 1 290
  • 那天又跛,我揣著相機(jī)與錄音,去河邊找鬼若治。 笑死慨蓝,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的端幼。 我是一名探鬼主播礼烈,決...
    沈念sama閱讀 38,997評(píng)論 3 408
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼婆跑!你這毒婦竟也來(lái)了此熬?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書(shū)人閱讀 37,741評(píng)論 0 268
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤滑进,失蹤者是張志新(化名)和其女友劉穎犀忱,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體扶关,經(jīng)...
    沈念sama閱讀 44,203評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡阴汇,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,534評(píng)論 2 327
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了驮审。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片鲫寄。...
    茶點(diǎn)故事閱讀 38,673評(píng)論 1 341
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡吉执,死狀恐怖疯淫,靈堂內(nèi)的尸體忽然破棺而出地来,到底是詐尸還是另有隱情,我是刑警寧澤熙掺,帶...
    沈念sama閱讀 34,339評(píng)論 4 330
  • 正文 年R本政府宣布未斑,位于F島的核電站,受9級(jí)特大地震影響币绩,放射性物質(zhì)發(fā)生泄漏蜡秽。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,955評(píng)論 3 313
  • 文/蒙蒙 一缆镣、第九天 我趴在偏房一處隱蔽的房頂上張望芽突。 院中可真熱鬧,春花似錦董瞻、人聲如沸寞蚌。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 30,770評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)挟秤。三九已至,卻和暖如春抄伍,著一層夾襖步出監(jiān)牢的瞬間艘刚,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 32,000評(píng)論 1 266
  • 我被黑心中介騙來(lái)泰國(guó)打工截珍, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留攀甚,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 46,394評(píng)論 2 360
  • 正文 我出身青樓岗喉,卻偏偏與公主長(zhǎng)得像云稚,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子沈堡,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,562評(píng)論 2 349

推薦閱讀更多精彩內(nèi)容