golang內(nèi)置函數(shù)len的小技巧_第1頁
golang內(nèi)置函數(shù)len的小技巧_第2頁
golang內(nèi)置函數(shù)len的小技巧_第3頁
golang內(nèi)置函數(shù)len的小技巧_第4頁
golang內(nèi)置函數(shù)len的小技巧_第5頁
已閱讀5頁,還剩2頁未讀 繼續(xù)免費閱讀

下載本文檔

版權說明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權,請進行舉報或認領

文檔簡介

第golang內(nèi)置函數(shù)len的小技巧len是很常用的內(nèi)置函數(shù),可以測量字符串、slice、array、channel以及map的長度/元素個數(shù)。

不過你真的了解len嗎?也許還有一些你不知道的小知識。

我們來看一道GO101的題目,這題也被GO語言愛好者周刊轉(zhuǎn)載:

packagemain

import"fmt"

funcmain(){

varx*struct{

s[][32]byte

fmt.Println(len(x.s[99]))

題目問你這段代碼的運行結(jié)果,選項有編譯錯誤、panic、32和0。

我們分析一下,別看x的聲明定義一大長串,實際上就是定義了一個有個[][32]byte的結(jié)構體,然后x是這個結(jié)構體的指針。

然后我們沒有初始化x,所以x是一個值為nil的指針??吹竭@里你也許以及有答案了,對nil指針解引用訪問它的成員s,那不就是panic嘛。即使引用x的成員合法,我們的s也沒有初始化,訪問沒有初始化的slice也會panic。

然而這么想你就錯了,代碼的實際運行結(jié)果是32!

為什么呢?我們看看len的幫助文檔:

Forsomearguments,suchasastringliteralorasimplearrayexpression,theresultcanbeaconstant.SeetheGolanguagespecification's"Lengthandcapacity"sectionfordetails.

這句話很重要,對于結(jié)果是數(shù)組的表達式,len可能會是一個編譯期常量,而且數(shù)組類型的長度在編譯期是可知的,所以熟悉c++的朋友大概會立刻想到這樣的常量是不需要進行實際求值的,簡單類型推導即可獲得。不過口說無憑,我們看看spec里的描述:

Theexpressionlen(s)isconstantifsisastringconstant.Theexpressionslen(s)andcap(s)areconstantsifthetypeofsisanarrayorpointertoanarrayandtheexpressionsdoesnotcontainchannelreceivesor(non-constant)functioncalls;inthiscasesisnotevaluated.Otherwise,invocationsoflenandcaparenotconstantandsisevaluated.

如果表達式是字符串常量那么len(s)也是常量。如果表達式s的類型是array或者array的指針,且表達式不是channel的接收操作或是函數(shù)調(diào)用,那么len(s)是常量,且表達式s不會被求值;否則len和cap會對s進行求值,其計算結(jié)果也不是一個常量。

其實說的很清楚了,但還有三點需要說明。

第一個是視為常量的表達式里為什么不能含有chan的接收操作和函數(shù)調(diào)用?

這個答案很簡單,因為這兩個操作都是使用這明確希望發(fā)生“副作用”的。特別是從chan里接收數(shù)據(jù),還會導致goroutine阻塞,而我們的常量len表達式不會進行求值,這些你期望會發(fā)生的副作用便不會產(chǎn)生,會引發(fā)一些隱蔽的bug。

第二個是我們注意到了函數(shù)調(diào)用前用non-constant修飾了,這是什么意思?

按字面意思,一部分函數(shù)調(diào)用其實是可以在編譯期完成計算被當成常量處理的,而另一些不可以。

在進一步深入之前我們先要看看golang里哪些東西是常量/常量表達式。

首先是各種字面量以及對字面量的類型轉(zhuǎn)換產(chǎn)生的值了,無需多說。

一部分內(nèi)置函數(shù):len、cap、imag、real、complex,它們在參數(shù)是常量的時候本身也是常量表達式。

unsafe.Sizeof,因為類型的大小也是編譯期就能確定的,所以它是常量表達式也很好理解。

所有的常量之間的運算(加減乘除位運算等,除了非常量表達式函數(shù)的調(diào)用)都是常量表達式。

從上面的描述里可以看出兩點,內(nèi)置函數(shù)和unsafe.Sizeof的調(diào)用我們可以看成是constantfunctioncalls,所有常量表達式除了浮點數(shù)和復數(shù)表達式都可以在編譯期完成計算。而其他函數(shù)比如用戶自定義函數(shù)的調(diào)用,雖然仍然有可能在編譯期被求值優(yōu)化,但本身不屬于常量表達式。所以語言標準會加以區(qū)分。比如下面這個:

funcadd(x,yint)int{

returnx+y

funcmain(){

fmt.Println(add(512,513))//1025

如果我們看生成的匯編,會發(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)用不是常量表達式,所以下面的代碼會報錯:

constnumber=add(512,513)//error!!!

//example.go:11:7:constinitializeradd(512,513)isnotaconstant

spec給出的實例是調(diào)用的內(nèi)置函數(shù),內(nèi)置函數(shù)也只有在參數(shù)是常量的情況下被調(diào)用才算做常量表達式:

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的表達式里如果用了non-constant的函數(shù)調(diào)用,那么就len本身不能算是常量表達式了。

這就有了最后一個疑問,題目中的x不是常量,為什么len的結(jié)果是常量呢?

標準只說表達式里不能有chan的接收和非常量表達式的函數(shù)調(diào)用,沒說其他的不可以。你也可以這么理解,表達式都有結(jié)果值,任何值除了無類型常量(這里顯然不是)都是要有一個確定的類型的,只要這個類型是數(shù)組或者數(shù)組的指針,那len就能獲得數(shù)組的長度,而這一切不需要s一定是常量表達式,編譯器可以簡單推導出表達式的值的類型。不能包含non-constantfunctioncalls和chan接收是我在第一點里解釋的,杜絕所有可能的副作用發(fā)生從而保證即使不對表達式求值程序也是正確的,不包含這兩個操作的表達式既可以是常量的也可以不是,所以這里我們能用x.s[99]作為len的參數(shù)。

說了這么多,只要len的參數(shù)類型為array或者array的指針并且符合要求,就不會進行求值,而題目里的表達式正好滿足這點,所以雖然我們看起來是會導致panic的代碼,但是本身并未進行實際求值,因此程序可以正常運行。另外cap也遵循同樣的規(guī)則。

最后,還有個小測驗,檢驗一下自己的學習吧:

//以下哪些語句是正確的,哪些是錯誤的

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. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請下載最新的WinRAR軟件解壓。
  • 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請聯(lián)系上傳者。文件的所有權益歸上傳用戶所有。
  • 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁內(nèi)容里面會有圖紙預覽,若沒有圖紙預覽就沒有圖紙。
  • 4. 未經(jīng)權益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
  • 5. 人人文庫網(wǎng)僅提供信息存儲空間,僅對用戶上傳內(nèi)容的表現(xiàn)方式做保護處理,對用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對任何下載內(nèi)容負責。
  • 6. 下載文件中如有侵權或不適當內(nèi)容,請與我們聯(lián)系,我們立即糾正。
  • 7. 本站不保證下載資源的準確性、安全性和完整性, 同時也不承擔用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。

評論

0/150

提交評論