学习笔记从2012年初开始和研究Go语言虽不敢说精通但总体上还是_第1页
学习笔记从2012年初开始和研究Go语言虽不敢说精通但总体上还是_第2页
学习笔记从2012年初开始和研究Go语言虽不敢说精通但总体上还是_第3页
学习笔记从2012年初开始和研究Go语言虽不敢说精通但总体上还是_第4页
学习笔记从2012年初开始和研究Go语言虽不敢说精通但总体上还是_第5页
已阅读5页,还剩211页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

从2012年初开始学习和研究Go语言,虽精通,但总体上还是比较熟悉和了解的。这几年踏踏实实读着它的源码一路走过来,有GoC的底子。换句话说,抛开goroutine等等时髦概念,其本质上还是NextC的路子。这本笔记原则上力求简洁,属于工具书而非。目的是在需要的时候,花上很短的时间就可从头到尾复习完所有知识点。对熟练的程序员而言,工具书似乎比更重要些。本书在不作者个利的情况下,可自由散播。•:不定期更新 联系雨痕2012-01- Go2012-01- R602012-03- 升级到1.02012-06-151.0.22013-03- 升级到1.12013-12- 1.22014-05- 1.32014-06-05reflect2014-06- 第一部分语 第1章类 变 常 基本类 类 类型转 字符 指 自定义类 第2章表达 保留 运算 初始 控制 第3章函 函数定 变 返回 函 延迟调 错误处 第4章数 第5章方 方法定 5.2字 方法 表达 第6章接 接口定 执行机 接口转 接 第7章并 第8章 工作空 源文 包结 文 第9章进 内存布 指针陷 第二部分源 Memory 初始 分配流 释放流 其 Garbage 2.1回 内存释 状态输 Goroutine 初始 创建任 执行任 连续 系统调 系统 178状态输 第三部分附 工 工具 条件编 跨平台编 调 Data 测 .5.第一部分语言第1章类型变Govar定义变量,自动初始化为零值。如果提供初始化值,可省略变量类型,由varvarxvarffloat32=1.6vars="abc"在函数内部,可用更简略的":="funcfuncmain()x:=}//varvarx,y,zvars,n="abc",var)funcmain()n,s:=0x1234,println(x,s,}o,data,data,i:=[3]int{0,1,2},i,data[i]=2,//(i=0)->(i=2),(data[0]=特殊只写变量"_"funcfunctest()(int,{return1,}funcmain()_,s:=test()}varvarsfunc{i:=}Errorideclaredandnotused。(可使用"_i规避s:="abc"s:="abc"s,y:= o",println(&s,重新赋值与前s通常函数多返回值err{s,z:=1000,println(&s,定义新同名变量}f30f18常constconstx,yint=1,consts= o,const=10,bool=))funcmain()constx=} 不支持1UL、2LLconstconst)=//x=常量值还可以是len、cap、 constconst = = =)constconst)byte=int=//intto//float64toint,const)iotaconst)Sunday=023456constconst)=int64=1<<(10*//iota=//iota=与KBiota=在同一常量组中,可以提供多个iotaconstconstA,B=iota,iota<< //0,0<<C, //1,1<<)iotaconstconst)===//////c4,显式恢复。注意计数包含了C、D//typetypeColorconstBlackColor=iota)functest(cColor){}funcmain(){c:=Blackx:=test(x)//Error:cannotusex(typeint)astypeColorinfunction}更明确的数字类型命名,支持Unicode11040UnicodeCodePoint,int,403264int8,10-128~127,0~int16,20-32768~32767,0~int32,40-21~21亿0~42int64,804884足以指针的uint32或uint64整UTF-8支持八进制、十六进制,以及科学记数法。标准库matha,a,b,c,d:=071,0x1F,1e9,空指针值nil,而非C/C++NULL类类型包括slice、map和channel。它们有复杂的内部结构,除了申请内存外,还需newmake会被编译器翻译aa:=[]int{0,0,a[1]=b:=make([]int,b[1]=//slice.goc:c:=c[1]=//Error:invalidoperation:c[1](indexoftypevarbbyte=//varnint=bvarnint=int(b)

//Error:cannotuseb(typebyte)astypeintin<-chanint(c)(<-chanint)(c)

//相当于相当于<-(chanboola:=ifa

//Error:non-boola(typeint)usedasif}字符字符串是不可变值类型,内部用指针指向UTF-8默认值是空字符串""用索引号某字节,如s[i]不能用序号获取字节元素指针。&s[i]struct{struct{ 使用索引号字符(byte)ss:=println(s[0]=='\x61',s[1]=='b',s[2]==truetruetrue使用"`"s:=`as:=`a输出c连接跨行字符串时,"+" ss:=o,"s2:=o,+//Error:invalidoperation:+untypedss:=o,s1:=s2:=s3:=o////UnicodeCodePoint\uFFFF、\U7FFFFFFF、\xFF格式。对应rune类型,UCS-4。funcfuncmain()fmt.Printf("%T\n",varc1c2rune\u6211'们println(c1我string(c2}truerune是int32要修改字符串,可先将其转换成[]rune或[]byte,完成后再转换为string。无论哪种转 funcfuncmain()s:=bs:=bs[1]=u电脑usus:=us[1]='话'}输出forbyterunefuncfuncmain()s"abc汉字fori:=0;i<len(s);{fmt.Printf("%c,",}//for_,r:=range{fmt.Printf("%c,",//}指支持指针类型*T,指针的指针**T,以及包含包名前缀的*<package>.TnilNULL操作符"&"取变量地址,"*"透过指针目标对象不支持指针运算,不支持"->"运算符,直接用"."目标成员funcfuncmain()typedatastruct{aintvard=data{1234}varp*datap=fmt.Printf("%p,fmt.Printf("%p,%v\n",p, }0x2101ef018,0x2101ef018,xx:=p:=&x//Error:invalidoperation:p+=1(mismatchedtypes*intand可以在unsafe.Pointerfuncfuncmain()x:=p:=unsafe.Pointer(&x)n:=(*[4]byte)(p)//*int->//Pointer->fori:=0;i<len(n);{fmt.Printf("%X",}}78785634返回局部变量指针是安全的,编译器会根据需要将其分配在GCHeapfunctest(){x:=return

使用runtime.new分配x}将Pointer转换成uintptrfuncfuncmain()d:=struct }{"abc",p:=uintptr(unsafe.Pointer(&d)) //*struct->Pointer->uintptrp+=unsafe.Offsetof(d.x) //uintptr+offsetp2:=unsafe.Pointer(p)px:=(*int)(p2)*px=

//uintptr->//Pointer->//d.x=fmt.Printf("%#v\n",}structstruct{sstring;xint}{s:"abc",注意:GC把uintptr当成普通整数对象,它无法"关联"对象被回收bool、int、stringarray、slice、map等和具体元素类型、长度等有关,属于未命名类型。具有相同元素类型和长度的array具有相同元素类型的slice具有相同键值类型的map具有相同元素类型和传送方向的channel具有相同字段序列(字段名、类型、、顺序)的struct签名相同(参数和返回值,不包括参数名称)的function(方法名、方法签名相同,和次序无关)的interfacevarastruct{xint`a`}varvarastruct{xint`a`}varbstruct{xint`ab`}//cannotusea(typestruct{xint"a"})astypestruct{xint"ab"}inassignmentb=atypefuncfuncmain()typebigintvarxbigint=100}x:=varbbigint=bigint(x)varb2int64=int64(b)

varsmyslice=[]int{1,2,3}vars2[]int=s

第2章表达式保留运算+&()-|<[]*^>{}/=,;%!.: *+/-&|<^&<&=|=^==NOTa0a1<<:a1<<:a=a&^清除bit6 nn:=p:=//b:=//ifn++==1////syntax//syntax//syntax//没有"~",取反运算也用"^"xx:=x,//0001,-初始////varastruct{xint}={100 //syntax//varb[]int={1,2,3//syntax//c:=struct{xint;y//////syntaxerror:unexpectedsemicolonorvara=struct{xintvarb=[]int{1,2,","",""}"aa:=2//Error:needtrailingcommabeforenewlineincomposite}a[]int{,//b//控制xx:=//ifx>//////Error:missingconditioninififn:="abc";x>0}elseifx<{}elseprintln("init"注意elseif和else不支持三元操作符"abab"whiless:=forin0len(s)ini+ 常见的for}n:=len(s)forn>0}替代whilen0替代forn0;{替代while(true替代forfuncfunclength(sstring) length.")return}funcmain()s:=forin0length(s)ini+ 避免多次调用lengthprintln(i,}}callcall0123(索引,值)(键,值)1st1st2nd unicode,"_"ss:=fori:=range{)忽略2ndvalue,支持string/array/slice/mapfor_,c:=range{}忽略indexm:=map[string]int{"a":1,"b":fork,v:=rangem{ (keyvalue)。println(k,v)}注意,range会对象aa:=[3]int{0,1,fori,v:=rangea//index、value都是 ifi==0a[1],a[2]=999,}确认修改有效,输出[0999999]a[i]=v+//使 品中取出的value修改原数组} 输出[100101102] ss:=[]int{1,2,3,4,forfori,v:=rangesstructslicepointerlencapifi==0s=s[:3]s[2]=100}对slicerangeprintln(i,}001234另外两种类型map、channel是指针包装,而不像slice是struct分支表达式可以是任意类型,不限于常量。可省略breakxx:=[]int{1,2,i:=switchicasecase1,3:}aa如需要继续下一分支,可使用fallthroughxx:=switch{casecase0:}if...elseif...elseswitchswitchcasex[1]>0:casex[1]<0:}switchi:={casei>casei<0:}Goto,Break,支持在函数内goto跳转。名区分大小写,未使用错误funcfuncmain()variintfor{ifi>2{gotoBREAK}//Error:labelEXITdefinedandnot 配合,break和continue可在多级嵌套循环中跳出funcfunc{forx:=0;x<3;x++fory:=0;y<5;y++ify>2{continueL2}ifx>1{breakL1}print(x,":",y,"}}}0:00:00:11:01:1附:break可用于for、switch、select,而continue仅能用于forxx:={casex>=ifx==0{break第3章函数不支持嵌套(nested)、重载(overload)和默认参数(defaultparameter)使用关键字funcfunctest(x,yint,sstring)(int,{n:=x+returnn,fmt.Sprintf(s,

}funcfunctest(fnfunc()int){return}typeFormatFuncfunc(sstring,x,yint)funcformat(fnFormatFunc,sstring,x,yint){returnfn(s,x,}funcmain()s1:=test(func()int{return100//直接 s2:=format(func(sstring,x,yint){returnfmt.Sprintf(s,x,},"%d,%d",10,println(s1,}变变参本质上就是slicefuncfunctest(sstring,){varxfor_,i:=range{x+=}returnfmt.Sprintf(s,}funcmain()println(test("sum:%d",1,2,}slicefuncfuncmain()s:=[]int{1,2,println(test("sum:%d",}返回"_"funcfunctest()(int,{return1,}funcmain()//s:=make([]int,//s= //Error:multiple-valuetest()insingle-valuex,_:=test()}funcfunctest()(int,{return1,}funcadd(x,yint){returnx+}funcsum(){varxfor_,i:=range{x+=}return}funcmain()}命名返回参数可看做与形参类似的局部变量,最后由returnfuncfuncadd(x,yint)(z{z=x+y}funcmain()println(add(1,}funcadd(x,yint)(zint){varz=x+returnz

//不能在一个级别 "zredeclaredinthisblock"错误//Error:zisshadowedduring}}命名返回参数允许defer延迟调用通过闭包和修改funcfuncadd(x,yint)(z{deferfunc(){z+=100z=x+y}funcmain()println(add(1 输出}显式returnfuncfuncadd(x,yint)(z{deferfunc() 输出z=x+returnz+执行顺序zz200(calldefer}funcmain()println(add(1 输出}3.4函函数可赋值给变量,做为结构字段,或者在channel////functionvariable--fn:=func(){println(" o,World!")}//functioncollection--fns:=[](func(xint)func(xint)int{returnx+1},func(xint)int{returnx+2}}//functionasfieldd:=struct{fnfunc()fn:func()string{return o,World!"}//channeloffunctionfc:=make(chanfunc()string,2)fc<-func()string{return" o,World!"}funcfunctest(){x:=fmt.Printf("x(%p)=%d\n",&x,returnfunc()fmt.Printf("x(%p)=%d\n",&x,}}funcmain()f:=test()}xx(0x2101ef018)=100x(0x2101ef018)=在汇编层面,test实际返回的是FuncVal对象,其中包含了函数地址、闭包对象指FuncVal{func_address,closure_var_pointer... 关键字defer用于延迟调用。这些调用直到ret前才被执行,通常用于释放资源或错funcfunctest()errorf,err:=os.Create("test.txt")iferr!=nil{returnerr}defer return}多个defer,按FILO次序执行。哪怕函数或某个延迟调用发生错误,这些调用依旧funcfunctest(x{deferprintln("a")deferdefer{println(100//div0异常 defer}funcmain()}panic:runtimeerror:integerdivideby延迟调用参数在时求值或,可用指针或闭包"延迟"funcfunctest()x,y:=10,deferfunc(i{println("defer:",yxx+=y+=println("x=",x,"y=",}xx=20y=defer:10defer可能会导致性能问题,尤其是在一个"大循环"里。varvarlockfunctest()}func{lock.Lock()}func arkTest(b{fori:=0;i<b.N;i++{}}func arkTestDefer(b{fori:=0;i<b.N;i++{}}输出 43128没有结构化异常,使用panic抛出错误,recoverfuncfunctest()deferdeferfunc()iferr:=recover();err!=nil{}将panic("panic}由于panic、recover参数类型为interface{}funcpanic(vinterface{})funcfuncpanic(vinterface{})funcrecover()interface{}funcfunctest()defer{defer{panic("deferpanic("test}func{deferdeferrecovernil。任何未funcfunctest()deferdeferfunc(){func()panic("test}func{}deferdeferpanic:testfuncfunc{}functest()deferexcept()}funcfunctest(x,y{varzfunc()deferfunc()ifrecover()!=nil{z=0z=x/yprintln("x/y=",}除用panic中断性错误外,还可返回error类型错误对象来表示函数调用状态typetypeerror{Error() error.Newfmt.Errorferror接口的错误对象。通过判断错varvarErrDivByZero=errors.New("divisionbyfuncdiv(x,yint)(int,error)ify==0{return0,ErrDivByZero}returnx/y,nil}funcmain()switchz,err:=div(10,0);{case}}如何区别使用panic和error两种方式?惯例是:在包panic,对外API使error第4章数据数组长度必须是常量,且是类型的组成部分。[2]int和[3]int支持"=="、"!="指针数组[n]*T,数组指针*[n]Taa:=[3]int{1,b:=[...]int{1,2,3,c:=[5]int{2:100,//未初始化元素值为0d:={namestringageuint8{"user1",{"user2",}aa:=[2][3]int{{1,2,3},{4,5,b:=[...][2]int{{1,1},{2,2},{3,//第2纬度不能用"..."值拷贝行为会造能问题,通常会建议使用slice,或数组指针funcfunctest(x{fmt.Printf("x:%p\n",&x)x[1]=1000}funcmain()a:=fmt.Printf("a:%p\n",}a:a:0x2101f9150x:0x2101f9170[00]lencap(元素数量)aa:=println(len(a), //2,需要说明,slice并不是数组或数组指针。它通过内部指针和相关属性数组片段,以structstruct{//mustnotmove//actual//numberof//allocatednumberof属性len表示可用元素数量,读写操作过该限制属性cap表示最大扩张容量,出数组限制slice==nillen、cap0datadata:=[...]int{0,1,2,3,4,5,slice:=//[low:high:+- high- +- len=high-lowcap=max- data|0|1|2|3|4|5|6 slice|pointer|len=3|cap=4|<len ++|||< |||||+++<<<slice.arraypointer data:=[...]int{0,1,2,3,4,5,6,7,8, ++++123468省略67855省略high、max1123456783省略low、maxdatadata:=[...]int{0,1,2,3,4,s:=data[2:4]s[0]+=100s[1]+=[102[102[011022034slices1s1:=[]int{0,1,2,3,8:fmt.Println(s1,len(s1),//s2:=make([]int,6,fmt.Println(s2,len(s2),使用make创建,指定len和caps3:=make([]int,fmt.Println(s3,len(s3),省略cap,相当于caplen12300099000068000066使用make动态创建slice,避免了数组必须用常量做长度的麻烦。还可用指针直接 ss:=[]int{0,1,2,pp:=*p+=*int[0[01102至于[][]T,是指元素类型为[]Tdatadata:=[]int{1,2,[]int{100,[]int{11,22,33,}resliceslice创建新slicecapss:=[]int{0,1,2,3,4,5,6,7,8,s1:=s2:=s3:=//[23//[456//data|0|1|2|3|4|5|6|7|8|9 |2|3|4 len=3,cap= |4|5|6|7 len=4,cap= |7|8|X error:sliceboundsoutofss:=[]int{0,1,2,3,4,5,6,7,8,s1:=s1[2]=//[23s2:=//[10056s2[3]s2[3]=[0123100562008 sliceslicess:=make([]int,0,5)fmt.Printf("%p\n",s2:=append(s,1)fmt.Println(s,[]简单点说,就是在array[slice.high]datadata:=[...]int{0,1,2,3,4,5,6,7,8,s:=s2:=append(s,100,121002005678112100一旦超出原slice.capdatadata:=[...]int{0,1,2,3,4,10:s:=s=append(s,100,一次append两个值,超出s.capfmt.Println(s,fmt.Println(s,fmt.Println(&s[0] [0[01100200][01234000000]0x20819c1800x20817c0c0从输出结果可以看出,append后的s重新分配了底层数组,并数据。如果只追加一个值,则不会超过s.cap限制,也就不会重新分配。通常以2倍容量重新分配底层数组。在大批量添加数据时,建议分配足够大的空间,以减少内存分配和数据开销。或初始化足够长的len属性,改用索引号进行操作。及时释放不再使用的slice对象,避免持有过期数组,造成GC无法回收。ss:=make([]int,0,1)c:=cap(s)fori:=0;i<50;{s=append(s,ifn:=cap(s);n>{fmt.Printf("cap:%d->%d\n",c,n)c=n}}输出1-22-44-88-16->32->函数copy在两个slice间数据,长度以len小的为准。两个slice可指向同一底层数组,允许元素区间。datadata:=[...]int{0,1,2,3,4,5,6,7,8,s:=s2:=copy(s2,copy(s2,//dst:s2,[8[8923[892345678应及时将所需数据copy到较小的slice类型,哈希表。键必须是支持相等运算符(==、!=)类型,比如number、string、pointer、array、struct,以及对应的interface。值可以是任意类型,没有限制。mm:={namestringageint{"user1",{"user2",}make函数一个合理元素数量参数,有助于提升性能。因为事先申请一大块内存,mm:=make(map[string]int,mmmap[string]int{":}判断keyifv,ok:=m["a"];{对于不存在的key,直接返回\0m["b"]m["b"]=delete(m,key获取键值对数量。capfork,v:=range{println(k,}从map中取回的是一个value临时品,对其成员的修改是没有任何意义的typetypeuserstruct{namestringmmap[int]user{:m[1].name=//Error:cannotassigntouu:=="Tom"m[1]=u替换valuem2map[int]*user{1:}//返回的是指 forfori:=0;i<5;{m0:"a",1:"a",2:"a",3:"a",4:5:"a",6:"a",7:"a",8:"a",9:}fork:=range{m[k+k]=}}}map[12:xmap[12:x16:x2:x6:x10:x14:x18:x]map[12:x16:x20:x28:x36:x]map[12:x16:x2:x6:x10:x14:x18:x]map[12:x16:x2:x6:x10:x14:x18:x]map[12:x16:x20:x28:x36:x]值类型,赋值和传参会全部内容。可用"_"定义补位字段,支持指向自身类型的指针typetypeNodestruct data*bytenext}funcmain()n1:= }n2:= data:nil,next:}}typetypeUser{namestringageint}u1:=User{"Tom",u2u2:= //Error:toofewvaluesinstructtypetypeFile{namestringsizeintattrstructpermintowner}}f:=size:1025,//attr:{0755, //Error:missingtypeincomposite}f.attr.owner=f.attr.perm=varattr={perm ownerint}{2,f.attr="=="、"!="maptypetypeUser{idintnamestring}mmap[User]int{,"Tom"}: varvaru1struct{namestring"username"}varu2struct{namestring}u2= //Error:cannotuseu1(typestruct{namestring"username"}) typestruct{namestring}in"节省"set"状态""静varvarnullset:=make(map[string]struct{})set["a"]=null(不含包名的字段。typetypeUser{name}typeManagerstructtitle}m:=User:User{"Tom"},} typetypeResource{id}typeUserstruct}typeManager{titletitle}varmManagerm.id=1="Jack"m.title=typetypeResource{idintnamestring}typeClassify{id}typeUser{Resourcenamestring}Resource.id与Classify.id遮蔽Ru:=} //U:Jack //people////Error:ambiguousselector//typetypeResource{id}typeUserstruct////name//Error:duplicatefield}u:=}面向对象三大特征里 typetypeUser{idintnamestring}typeManagerstructtitle}m:=Manager{User{1,"Tom"},//varuUser= //Error:cannotusem(typeManager)astypeUserinvaruUser Cstructobject|<|<User:24>|<--title:16-- m +|+++1|||||Administrator| |+ | u | |<-id:8-->|<-name:16-- | | 可用unsafe : :0x:m.title:b0,size:40,align:b0,offset:b8,offset:c8,offset:第5章方法方法总是绑定对象实例,并隐式将实例作为第一实参(receiver)参数receiverreceiverT*TT不支持方法重载 value或pointertypetypeQueue{elements}funcNewQueue()*Queuereturn&Queue{make([]interface{},func(*Queue)Push(einterface{}){panic("not}省略receiver//func(Queue)Push(eint)error//Error:methodredeclared: panic("not//func(self*Queuelength(int receiver参数名可以是self、thisreturn}receiverT和*Ttypetypestruct{x}func(selfData){fmt.Printf("Value://funcValueTest(self}}func(self*Data){fmt.Printf("Pointer:%p\n",//funcPointerTest(selffuncmain()d:=Data{}p:=&dfmt.Printf("Data:%p\n",////////}Data:0x2101ef018Value:0x2101ef028Pointer:Data:0x2101ef018Value:0x2101ef028Pointer:0x2101ef018Value:Pointer:字 typetypeUser{idintnamestring}typeManager{}func(self*User)ToString()string{ //receiver=&(Manager.User)returnfmt.Sprintf("User:%p,%v",self,self)}funcmain()m:=Manager{User{1,fmt.Printf("Manager:%p\n",}Manager:Manager: :b0,&{1名方法,就可以实现"override"。typetypeUser{idintnamestring}typeManagerstructtitle}func(self*User)ToString()stringreturnfmt.Sprintf("User:%p,%v",self,}func(self*Manager)ToString()stringreturnfmt.Sprintf("Manager:%p,%v",self,}funcmain()m:=Manager{User{1,"Tom"},}Manager:Manager: :b0,&{{1Tom}b0,&{1方法TreceiverT*TreceiverT+*T如类型S包含字段T,则S方法集包含T方法如类型S包含字段*T,则S方法集包含T+*T方法T*T,*ST+*T用实例value和pointer调用方法(含字段)不受方法集约束,编译器总是查找全部方法,并自动转换receiver实参。表达instance.method(args...)><type>.func(instance, methodvaluemethodexpressionmethodvaluemethodexpression则须显式传参。typetypeUser{idintnamestring}func(self*User){fmt.Printf("%p,%v\n",self,}funcmain()u:=User{1,"Tom"}mValue:=//隐式传递mExpression:=//显式传递},&{1,&{1,&{1需要注意,methodvalue会receivertypetypeUser{idintnamestring}func(selfUser){}funcmain()u:=User{1,"Tom"}mValue:=u.Testu.id,=2,"Jack"}{2{2{1在汇编层面,methodvalue和闭包的实现方式相同,实际返回FuncValFuncVal{method_address,receiver_copy 可依据方法集转换methodexpression,注意receivertypetypeUser{idintnamestring}func(self*User){fmt.Printf("TestPointer:%p,%v\n",self,}func(selfUser){fmt.Printf("TestValue:%p,%v\n",&self,}funcmain()mvmv:=User.TestValuemp:=(*User).TestPointermp2:=(*User).TestValue}*User方法集包含TestValuefuncTestValue(self*User)receivervaluecopy:TestValue:0xTestPointer:TestValue:,{1,{1,&{1c0,{1"还原"typetypeDatafunc(Data)func(*Data)TestPointer()funcmain()varp*Data=nil(*Data)(nil).TestPointer()//methodvalue //methodexpression// //invalidmemoryaddressornilpointer//(Data)(nil).TestValue()//cannotconvertniltotype// //cannotusenilastypeDatainfunction}第6章接口就表示它"实现"了该接口,无须在该类型上显式添加接口。(不包括参数名以及返回值。当然,该类型还ertypetypeStringer{String()}typePrinter{Stringer}typeUser{idintnamestring}func(self*User)String()stringreturnfmt.Sprintf("user%d,%s",self.id,}func(self*User){}funcmain()vartPrinter=&User{1,"Tom"} *UserString、Print。}user1, interface{}没有任何方法签名,也就意味着任何类型都实现了空接口。其作用类似面向对象语言中的根对象object。funcfuncPrint(v{fmt.Printf("%T:%v\n",v,}funcmain() o,}int:int:o,typetypeTester{sinterface{String()string}}typeUser{idintnamestring}func(self*User)String()stringreturnfmt.Sprintf("user%d,%s",self.id,}funcmain()t:=Tester{&User{1,"Tom"}}}user1, (interfacetable)structstruct{ struct{voidtypetypeUser{idintnamestring}funcmain()u:=User{1,"Tom"}variinterface{}=uu.id=2="Jack"fmt.Printf("%v\n",u)}{2{2{1typetypeUser{idintnamestring}funcmain()u:=User{1,varvi,piinterface{}=u,//vi.(User).name="Jack"pi.(*User).name="Jack"//Error:cannotassigntofmt.Printf("%v\n",fmt.Printf("%v\n",}{1Tom}{1Tom}tabdatanilnilvarvarainterface{}=varbinterface{}=//tab=nil,data=tab包含*int类型信息datatypeiface{itab,data}ia:=*(*iface)(unsafe.Pointer(&a))ib:=fmt.Println(a==nil,fmt.Println(b==nil,ib,输出truetrue{0false{5057280}typetypeUser{idintnamestring}}func(self*User)String()stringreturnfmt.Sprintf("%d,%s",self.id,}funcmain()varointerface{}=&User{1,ifi,ok:=o.(fmt.Stringer);{}//ok-u:=//u:=o.(User)//panic:interfaceis*main.User,not}还可用switch做批量类型判断,不支持fallthroughfuncmain()varointerface{}=&User{1,switchv:={casecasefmt.Stringer:casefunc()string:case*User:fmt.Printf("%d,%s\n",v.id,)

//o==//////}}typetypeStringer{String()}typePrinter{String()string}typetypeUser{idintnamestring}func(self*User)String()stringreturnfmt.Sprintf("%d,%v",self.id,}func(self*User){}funcmain()varoPrinter=&User{1,"Tom"}varsStringer=o}接var_fmt.Stringer= "实现"typetypeTester{}typeFuncDofunc(selfFuncDo)Do(){self()funcmain()vartTester=FuncDo(func(){println("}o,World!")第7章并发Go在语言层面对并发编程提供支持,一种类似协程,称作goroutine只需在函数调用语句前添加go关键字,就可创建并发执行单元。开发人员无需了解任何执行细节,调度器会自动将其安排到合适的系统线程上执行。goroutine是一种非常轻量事实上,函数main就以goroutine运行。另有与之配套的channel类型,用以实现"以通讯来共享内存"的CSP模式。相关实现细节可参考本书第二部分的源码剖析。gogofunc()o,调度器不能保证多个goroutine默认情况下,进程启动后仅允许一个系统线程服务于goroutine。可使用环境变量或标准runtime.GOMAXPROCS修改,让调度器用多个线程实现多核并行,而不仅仅是并发。funcfuncsum(id{varxfori:=0;i<math.MaxUint32;{x+=}println(id,}funcmain()wg:=new(sync.WaitGroup)fori:=0;i<2;{gofunc(idint){deferwg.Done()}}}$$gobuild-o$time-p 程序开始到结束时间差(非CPU时间用户态所使用CPU时间片(多核累加CPU$GOMAXPROCS=2time-p01552个核并行,real调用runtime.Goexit将立即终止当前goroutine执行,调度器确保所有已funcfuncmain()wg:=new(sync.WaitGroup)gofunc()deferdeferfunc()//终止当前}和协程yield作用类似,Gosched让出底层线程,将当前goroutine暂停,放回队列等funcfuncmain()wg:=new(sync.WaitGroup)gofunc()deferfori:=0;i<6;{ifi==3{runtime.Gosched()}gofunc()deferwg.Done() }$$gorunmain.go123o,45channelCSPgoroutine通讯。其内部实现了funcfuncmain()data:=make(chanint)exit:=make(changofunc()ford:=range{}closefmt.Println("recvexit<-data<-data<-data<-3fmt.Println("send}1123sendover.recvchannel可减少排队阻塞,具备更高的效率。但应该考虑使用指针规funcfuncmain()data:=make(chanint,3)exit:=make(chanbool)//3data<-data<-data<-gofunc()ford:=range{}}exit<-data<-data<-5}varvara,bchanint=make(chanint),make(chanint,rangeok-idiomchannelforforifd,ok:=<-data;{}else}}向closedchannel发送数据panic错误,接收立即返回零值。而nilchannel,无内置函数len返回未被的缓冲元素数量,cap返回缓冲区大小d1:=make(chanint)d1:=make(chanint)d2:=make(chanint,3)d2<-fmt.Println(len(d1),cap(d1))fmt.Println(len(d2),//0//1可以将channelcc:=make(chanint,varsendchan<-int=c //send-onlyvarrecv<-chanint=c send<-//<-//Error:receivefromsend-onlytypechan<-//recv<-//Error:sendtoreceive-onlytype<-chan不能将单向channel转换为普通channeldd:=(chand:=(chan//Error:cannotconverttypechan<-inttotypechan//Error:cannotconverttype<-chaninttotypechanchannelselectchannel做收发操作,或执行defaultcase。funcfuncmain()a,b:=make(chanint,3),make(changofunc()v,ok,s:=0,false,forselect{ channel,接收数据。casev,ok=<-a:s="a"casev,ok=<-b:s=}ifokfmt.Println(s,}else}}fori:=0;i<5;i++selectchannelcasecasea<-i:caseb<-}}channelmaingoroutine}bbaaab在循环中使用selectdefaultcase需要,避免形成洪水用简单工厂模式打包并发任务和channelfuncfuncNewConsumer()chan{data:=make(chanint,gofunc()ford:=range{}return}funcmain()data:=data<-data<-2select}channel(semaphore)funcfuncmain()wg:=sync.WaitGroup{}sem:=make(chanint,fori:=0;i<3;{gofunc(idint){defersem<-向semforx:=0;x<3;{fmt.Println(id,} goroutine}}$GOMAXPROCS=2run000102101112202122closedchannelfuncfuncmain()varwgsync.WaitGroupquit:=make(chanbool)fori:=0;i<2;{gogofunc(id{defertask:=func()}for{{case<-}}closedchannel}time.Sleep(time.Second* goroutine}用select实现超时(timeout)funcfuncmain()w:=make(chanbool)c:=make(chanint,2)gofunc()selectcasev:=<-c:case<-time.After(time.Second*3):}w<-//c<-//}channel(内部实现为指针)typeRequeststruct data[]intretdata[]intretchanint}funcNewRequest()*Requestreturn&Request{data,make(chanint,1)}funcProcess(req{x:=for_,i:=range{x+=}req.ret<-}funcmain()req:=NewRequest(10,20,30)}第8章包 有严格要求,每个工作空间(workspace)必须由bin、pkg、src三 |+|+//goinstall安 ||||+|//gobuild生成静态库(.a)存 |||||||||+|+|+|++|。+ +||+|+|+|+可在GOPATH环境变量列表中添加多个workspace,但不能和GOROOTexport 通常goget使用第一个workspace保存的第库源文UTF-8格式,否则会导致编译器出错。结束:语句以";"结束,多数时候可以省略。注释:支持"//"、"/**/"命名:采用camelCasing包结所有代码都必须组织在package源文件头部以"package<name>"包名称 包名类似namespace,与包所 可执行文件必须包含packagemain,函数main说说明:osArgs返回命令行参数,osExit要获取正确的可执行文件路径,可用filepathAbs(execLookPath(osArgs[0]))public:首字母大写,可被包外 使用包成员前,必须先用importimportimport"相 /包主文件名相 是指 importimport->import"os/exec"-> import"yuhen/test"importM"yuhen/test"importimport"yuhen/test"importM"yuhen/test"import.import_默认模式包重命名//简便模式非导入模式仅让该执行初始化函数。未使用的导入包,会被编译器视为错误(不包括"import_")./main.go:4:./main.go:4:importedandnotused:对于当 下的子包,除使用默认完整导入路径外,还可使用local方式|+|+|+|+|+importimportimportgorunmain.go8.2.2在所有初始化函数结束后才执行main.main因为无法保证初始化函数执行顺序,因此全局变量应该直接用varvarvarnow=funcinit()fmt.Printf("now:%v\n",}funcinit()fmt.Printf("since:%v\n",}可在初始化函数中使用goroutinevarvarnow=funcmain()fmt.Println("main:",}funcinit()fmt.Println("init:",int(time.Now().Sub(now).Seconds()))w:=make(chanbool)gofunc()time.Sleep(time.Second*3)w<-true}init:init:main:文扩展工具godoc(中间没有空行)自动转换URL为自动合并多个源码文件中的package无法显式packagemain建议用专门的doc.go保存package包文档第一整句(中英文句号结束)被当做packages只要Example oExampleT_M,ExampleUser,用了该文件中的其他成员,那么示例会显示整个文件内容,而不仅仅是测试函数自己非测试源码文件中以BUG(author)开始的注释,会在帮助文档Bugs//BUG(yuhen):memory 第9章进阶 | |3.14| |1|2|3|4 ++++|pointer|len=5 +s=|||h|e|l|l|o ||+|pointer|len=2 sub=+++|1|2|0|0|3|0|0|0|struct{abyte;bbyte;cint32}={1,2,3 +++|pointera|b+struct{a*int;bint||+++|int++++++++|pointer|len=8|cap=8 +x=[]int{0,1,2,3,4,5,6,7|||0|1|2|3|4|5|6|7 || |pointer|len=2|cap=5|y= ++|++*itab+||++*data struct+|||Itab |data + +++|pointer++s=|||0|0|0 +++++|pointer|len=1|cap=3+slice=make([]int,1,|+++||0|0|0 ++|pointer++;|| .hashmap.cHmap ++|pointer++channelmake(chanint)|| .chan.cHchan 对象内存分配会受编译参数影响。举个例子,当函数返回对象指针时,必然在堆上分配。可如果该函数被内联,那么这个指针就不会跨栈帧使用,就有可能直接在栈上分配,以实现代码优化目的。因此,是否内联对指针输出结果有很大影响。unsafe.PointeruintptruintptrGC当做普通整数对象,它不能所""对象被回收。typetypedatastructx[1024*}functest(){p:=return}funcmain()constN=cache:=fori:=0;i<N;{cache[i]=test()}}$gobuild-otest&&GODEBUG="gctrace=1"0->0->0->合法的unsafe.Pointerfuncfunctest(){p:=return}funcmain()constN=cache:=fori:=0;i<N;{cache[i]=test()}}$$gobuild-otest&&GODEBUG="gctrace=1"指向对象成员的unsafe.Pointertypetypedatastruct [1024*100]byte }functest(){d:=return}funcmain()constN=cache:=fori:=0;i<N;{cache[i]=test()}}$gobuild-otest&&GODEBUG="gctrace=1"由于可以用unsafe.Pointer、uintptr创建"danglingpointer"等指针,所以在使用时需要特别。另外,cgoC.malloc等函数所返回指针,与GC无关。指针构成的"循环"加上runtime.SetFinalizer会导致内存typetypeDatastruct [1024*100]byte }functest()vara,ba.oa.o=b.o=runtime.SetFinalizer(&a,func(d*Data){fmt.Printf("a%pfinal.\n",d)})runtime.SetFinalizer(&b,func(d*Data){fmt.Printf("b%pfinal.\n",d)}func{for}}$gobuild-gcflags"-N-l"&&GODEBUG="gctrace=1"(1180-53)(2226-75)(4307-109)回收器能正确处理"指针循环",但无法确定Finalizer依赖次序,也就无法调 通过cgo,可在Go和C/C++代码间相用。受CGO_ENABLED参数限制packagepackage#include<stdio.h> o() o,}importfuncmain() }cgo代码很件很麻烦的事,建议单独保存到.c文件中。这样可以将其当做独立的#ifndef#ifndef#define#include"test.h" o() o,} 避免和Gmain(intargc,char return}packagepackage#includeimportfuncmain() }编译和调试C$$gcc-g-D -otest由于cgo仅扫描当前,如果需要包含其他C项目,可在当前新建一个C文件,然后用#include指令将所需的.h、.c都包含进来,记得在CFLAGS中使用"-I"参数指定原路径。某些时候,可能还需指定"-std"参数。可使用#cgo命令定义CFLAGS、LDFLAGS#cgoCFLAGS:-#cgoCFLAGS:-

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论