




版權(quán)說(shuō)明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請(qǐng)進(jìn)行舉報(bào)或認(rèn)領(lǐng)
文檔簡(jiǎn)介
第golang內(nèi)置函數(shù)len的小技巧len是很常用的內(nèi)置函數(shù),可以測(cè)量字符串、slice、array、channel以及map的長(zhǎng)度/元素個(gè)數(shù)。
不過(guò)你真的了解len嗎?也許還有一些你不知道的小知識(shí)。
我們來(lái)看一道GO101的題目,這題也被GO語(yǔ)言愛(ài)好者周刊轉(zhuǎn)載:
packagemain
import"fmt"
funcmain(){
varx*struct{
s[][32]byte
fmt.Println(len(x.s[99]))
題目問(wèn)你這段代碼的運(yùn)行結(jié)果,選項(xiàng)有編譯錯(cuò)誤、panic、32和0。
我們分析一下,別看x的聲明定義一大長(zhǎng)串,實(shí)際上就是定義了一個(gè)有個(gè)[][32]byte的結(jié)構(gòu)體,然后x是這個(gè)結(jié)構(gòu)體的指針。
然后我們沒(méi)有初始化x,所以x是一個(gè)值為nil的指針??吹竭@里你也許以及有答案了,對(duì)nil指針解引用訪問(wèn)它的成員s,那不就是panic嘛。即使引用x的成員合法,我們的s也沒(méi)有初始化,訪問(wèn)沒(méi)有初始化的slice也會(huì)panic。
然而這么想你就錯(cuò)了,代碼的實(shí)際運(yùn)行結(jié)果是32!
為什么呢?我們看看len的幫助文檔:
Forsomearguments,suchasastringliteralorasimplearrayexpression,theresultcanbeaconstant.SeetheGolanguagespecification's"Lengthandcapacity"sectionfordetails.
這句話很重要,對(duì)于結(jié)果是數(shù)組的表達(dá)式,len可能會(huì)是一個(gè)編譯期常量,而且數(shù)組類型的長(zhǎng)度在編譯期是可知的,所以熟悉c++的朋友大概會(huì)立刻想到這樣的常量是不需要進(jìn)行實(shí)際求值的,簡(jiǎn)單類型推導(dǎo)即可獲得。不過(guò)口說(shuō)無(wú)憑,我們看看spec里的描述:
Theexpressionlen(s)isconstantifsisastringconstant.Theexpressionslen(s)andcap(s)areconstantsifthetypeofsisanarrayorpointertoanarrayandtheexpressionsdoesnotcontainchannelreceivesor(non-constant)functioncalls;inthiscasesisnotevaluated.Otherwise,invocationsoflenandcaparenotconstantandsisevaluated.
如果表達(dá)式是字符串常量那么len(s)也是常量。如果表達(dá)式s的類型是array或者array的指針,且表達(dá)式不是channel的接收操作或是函數(shù)調(diào)用,那么len(s)是常量,且表達(dá)式s不會(huì)被求值;否則len和cap會(huì)對(duì)s進(jìn)行求值,其計(jì)算結(jié)果也不是一個(gè)常量。
其實(shí)說(shuō)的很清楚了,但還有三點(diǎn)需要說(shuō)明。
第一個(gè)是視為常量的表達(dá)式里為什么不能含有chan的接收操作和函數(shù)調(diào)用?
這個(gè)答案很簡(jiǎn)單,因?yàn)檫@兩個(gè)操作都是使用這明確希望發(fā)生“副作用”的。特別是從chan里接收數(shù)據(jù),還會(huì)導(dǎo)致goroutine阻塞,而我們的常量len表達(dá)式不會(huì)進(jìn)行求值,這些你期望會(huì)發(fā)生的副作用便不會(huì)產(chǎn)生,會(huì)引發(fā)一些隱蔽的bug。
第二個(gè)是我們注意到了函數(shù)調(diào)用前用non-constant修飾了,這是什么意思?
按字面意思,一部分函數(shù)調(diào)用其實(shí)是可以在編譯期完成計(jì)算被當(dāng)成常量處理的,而另一些不可以。
在進(jìn)一步深入之前我們先要看看golang里哪些東西是常量/常量表達(dá)式。
首先是各種字面量以及對(duì)字面量的類型轉(zhuǎn)換產(chǎn)生的值了,無(wú)需多說(shuō)。
一部分內(nèi)置函數(shù):len、cap、imag、real、complex,它們?cè)趨?shù)是常量的時(shí)候本身也是常量表達(dá)式。
unsafe.Sizeof,因?yàn)轭愋偷拇笮∫彩蔷幾g期就能確定的,所以它是常量表達(dá)式也很好理解。
所有的常量之間的運(yùn)算(加減乘除位運(yùn)算等,除了非常量表達(dá)式函數(shù)的調(diào)用)都是常量表達(dá)式。
從上面的描述里可以看出兩點(diǎn),內(nèi)置函數(shù)和unsafe.Sizeof的調(diào)用我們可以看成是constantfunctioncalls,所有常量表達(dá)式除了浮點(diǎn)數(shù)和復(fù)數(shù)表達(dá)式都可以在編譯期完成計(jì)算。而其他函數(shù)比如用戶自定義函數(shù)的調(diào)用,雖然仍然有可能在編譯期被求值優(yōu)化,但本身不屬于常量表達(dá)式。所以語(yǔ)言標(biāo)準(zhǔn)會(huì)加以區(qū)分。比如下面這個(gè):
funcadd(x,yint)int{
returnx+y
funcmain(){
fmt.Println(add(512,513))//1025
如果我們看生成的匯編,會(huì)發(fā)現(xiàn)求值已經(jīng)完成,不需要調(diào)用add:
MOVQ$1025,(SP)
PCDATA$1,$0
CALLruntime.convT64(SB)
MOVQ8(SP),AX
XORPSX0,X0
MOVUPSX0,""..autotmp_16+64(SP)
LEAQ(SB),CX
MOVQCX,""..autotmp_16+64(SP)
MOVQAX,""..autotmp_16+72(SP)
NOP
MOVQos.Stdout(SB),AX
LEAQgo.itab.*os.File,io.Writer(SB),CX
MOVQCX,(SP)
MOVQAX,8(SP)
LEAQ""..autotmp_16+64(SP),AX
MOVQAX,16(SP)
MOVQ$1,24(SP)
MOVQ$1,32(SP)
NOP
CALLfmt.Fprintln(SB)
MOVQ80(SP),BP
ADDQ$88,SP
RET
很明顯的,1025已經(jīng)在編譯期求值了,然而add的調(diào)用不是常量表達(dá)式,所以下面的代碼會(huì)報(bào)錯(cuò):
constnumber=add(512,513)//error!!!
//example.go:11:7:constinitializeradd(512,513)isnotaconstant
spec給出的實(shí)例是調(diào)用的內(nèi)置函數(shù),內(nèi)置函數(shù)也只有在參數(shù)是常量的情況下被調(diào)用才算做常量表達(dá)式:
const(
c4=len([10]float64{imag(2i)})//imag(2i)isaconstantandnofunctioncallisissued
c5=len([10]float64{imag(z)})//invalid:imag(z)isa(non-constant)functioncall
varzcomplex128
所以len的表達(dá)式里如果用了non-constant的函數(shù)調(diào)用,那么就len本身不能算是常量表達(dá)式了。
這就有了最后一個(gè)疑問(wèn),題目中的x不是常量,為什么len的結(jié)果是常量呢?
標(biāo)準(zhǔn)只說(shuō)表達(dá)式里不能有chan的接收和非常量表達(dá)式的函數(shù)調(diào)用,沒(méi)說(shuō)其他的不可以。你也可以這么理解,表達(dá)式都有結(jié)果值,任何值除了無(wú)類型常量(這里顯然不是)都是要有一個(gè)確定的類型的,只要這個(gè)類型是數(shù)組或者數(shù)組的指針,那len就能獲得數(shù)組的長(zhǎng)度,而這一切不需要s一定是常量表達(dá)式,編譯器可以簡(jiǎn)單推導(dǎo)出表達(dá)式的值的類型。不能包含non-constantfunctioncalls和chan接收是我在第一點(diǎn)里解釋的,杜絕所有可能的副作用發(fā)生從而保證即使不對(duì)表達(dá)式求值程序也是正確的,不包含這兩個(gè)操作的表達(dá)式既可以是常量的也可以不是,所以這里我們能用x.s[99]作為len的參數(shù)。
說(shuō)了這么多,只要len的參數(shù)類型為array或者array的指針并且符合要求,就不會(huì)進(jìn)行求值,而題目里的表達(dá)式正好滿足這點(diǎn),所以雖然我們看起來(lái)是會(huì)導(dǎo)致panic的代碼,但是本身并未進(jìn)行實(shí)際求值,因此程序可以正常運(yùn)行。另外cap也遵循同樣的規(guī)則。
最后,還有個(gè)小測(cè)驗(yàn),檢驗(yàn)一下自己的學(xué)習(xí)吧:
//以下哪些語(yǔ)句是正確的,哪些是錯(cuò)誤的
varslice[][]*[10]int
const(
a=len(slice[10000000000000][4])//1
b=len(slice[1])//2
c=len(slice)//3
d=len([1]int{1024})//
溫馨提示
- 1. 本站所有資源如無(wú)特殊說(shuō)明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請(qǐng)下載最新的WinRAR軟件解壓。
- 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請(qǐng)聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
- 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁(yè)內(nèi)容里面會(huì)有圖紙預(yù)覽,若沒(méi)有圖紙預(yù)覽就沒(méi)有圖紙。
- 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
- 5. 人人文庫(kù)網(wǎng)僅提供信息存儲(chǔ)空間,僅對(duì)用戶上傳內(nèi)容的表現(xiàn)方式做保護(hù)處理,對(duì)用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對(duì)任何下載內(nèi)容負(fù)責(zé)。
- 6. 下載文件中如有侵權(quán)或不適當(dāng)內(nèi)容,請(qǐng)與我們聯(lián)系,我們立即糾正。
- 7. 本站不保證下載資源的準(zhǔn)確性、安全性和完整性, 同時(shí)也不承擔(dān)用戶因使用這些下載資源對(duì)自己和他人造成任何形式的傷害或損失。
最新文檔
- 行政管理中的組織行為與試題及答案分析
- 新疆昌吉市高二上學(xué)期期末考試語(yǔ)文試題
- 醫(yī)院等級(jí)評(píng)審專項(xiàng)復(fù)習(xí)試題含答案
- 高考后學(xué)業(yè)規(guī)劃培訓(xùn)體系
- 行政執(zhí)法中的程序正義試題及答案
- 實(shí)踐經(jīng)驗(yàn)回顧2025年護(hù)士試題及答案
- 2025年執(zhí)業(yè)護(hù)士考試主題試題及答案
- 文藝演出項(xiàng)目簽約演員合同范本:演出質(zhì)量與責(zé)任界定
- 社交平臺(tái)用戶忠誠(chéng)度管理與隱私保護(hù)協(xié)議
- 境外財(cái)產(chǎn)申報(bào)與稅務(wù)風(fēng)險(xiǎn)管理合同
- 2025年“鑄牢中華民族共同體意識(shí)”競(jìng)賽試卷題庫(kù)及答案
- 2025年二級(jí)風(fēng)力發(fā)電運(yùn)維值班員職業(yè)技能鑒定考試題庫(kù)(濃縮500題)
- 《一帶一路主題樂(lè)園規(guī)劃》課件
- 江蘇省南京市、鹽城市2025屆高三年級(jí)5月第二次模擬考試化學(xué)試題及答案(南京鹽城二模)
- 2025新能源汽車技術(shù)的應(yīng)急管理策略試題及答案
- 八年級(jí)下學(xué)期家長(zhǎng)會(huì)課件《攜手同行共筑夢(mèng)想》
- 建筑勞務(wù)分包合同中的稅務(wù)問(wèn)題3篇
- 【課件】Unit+7+A+Day+to+Remember+Section+A(1a-1d)+課件+2024-2025學(xué)年人教版英語(yǔ)七年級(jí)下冊(cè)
- 中國(guó)世界文化遺產(chǎn)長(zhǎng)城的資料整理
- 檔案袋密封條模板
- I-am-a-bunny-繪本教學(xué)課件
評(píng)論
0/150
提交評(píng)論