一、用處
參考
為什么要有指針赚抡?
指針究竟是什么爬坑?是地址?還是類型涂臣?
指針究竟有什么用盾计?
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.圖示
(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)。
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 類型六敬。
因此碘赖,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;
}
關(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"