demo:
packagemain import"fmt"//注释 //注释 funcmain(){ fmt.Printf("HelloWorld\n") }执行:
gorundemo.go编译成可执行文件
gobuilddemo.go直接访问字符串的下标是不可以的,需要先转换为byte类型,通过string函数转换回来。
其他操作符
PrecedenceOperator(s)
Highest: */%<<>>&&^ +-|^ ==!=<<=>>= <- &&Lowest
||for有三种形式
forinit;condition;post{}类似C的for forcondition{}类似C的while for{}类似C的for(;?死循环golang中没有do和while
funcmain(){ fori:=0;i<10;i++{ fmt.Printf("i:%d\n",i) } j:=0 forj<10{ fmt.Printf("j:%d\n",j) j++ } k:=0 for{ fmt.Printf("k:%d\n",k) k++ ifk>9{ break } } } rang可以方便地遍历对象 l:=[]string{"a","b","c"} fork,v:=rangel{ fmt.Printf("pos:%d,value:%s\n",k,v) } switch funcmain(){ i:=0 switchi{ case-1,0: fmt.Printf("is-1or0") case1: fmt.Printf("is1") default: fmt.Printf("default") } } case中逗号表示或Table2.3.Go中的预定义函数 closenewpaniccomplex closedmakerecoverreal lenappendprintimag capcopyprintln len和cap可用于不同的类型,len用于返回字符串、slice和数组的长度。参阅”array、slices和map”小节了解更多关于slice、数组和函数cap的详细信息。 new用于各种类型的内存分配。参阅”用new分配内存”在59页。 make用于内建类型(map、slice和channel)的内存分配。参阅”用make分配内存”在59
页。 copy用于复制slice。append用于追加slice。参阅本章的”slice”。 panic和recover用于异常处理机制。参阅”恐慌(Panic)和恢复(Recover)”在37页了
解更多信息。 print和println是底层打印函数,可以在不引入fmt包的情况下使用。它们主要用于调
试。 complex、real和imag全部用于处理复数。有了之前给的简单的例子,不用再进一步讨论
复数了。array就是Python的数组
map就是Python的字典 5.1arrayarray使用[n]定义,其中n是数组的大小,type是元素的类型。n是可选的。
数组的定义和使用。 l1:=[]string{"a","b"} varl2[2]int l2[0]=1 l2[1]=2 varl3[2][3]int l3[0][0]=1 print(l1[0]) print(l2[0]) print(l3[0][0])当传递一个array给函数的时候,函数得到的是一个array的副本,即传值。
5.2slice(切片)slice和array类似,不同的是slice是array的一个指针,所以修改slice,是会影响array的,而且传递一个slice给函数的时候,传递的是指针,所以是传址。
l1:=[]string{"a","b","c","d"} s1:=l1[1:2] s2:=l1[:]//类似l1[0:4] s3:=l1[:2]//类似l1[0:2] print(s1[0]) print(s2[0]) print(s3[0])append用户向切片中添加元素,返回新的切片,新的切片的内存地址可能和之前的不一样。
l1:=[]string{"a","b","c","d"} s2:=append(l1,"e","f") print(s2[4]) print(s2[5]) 5.3mapmap的定义:map[<fromtype>]<totype>
varmap1=make(map[string]int) map2:=map[string]int{ "k1":11,"k2":12, } print(map2) map1["k1"]=12 v,ok:=map1["k1"]//12true print(v,ok,"\n") v1,ok1:=map1["k2"]//0false print(v1,ok1,"\n") //map1["k2"]=0,false//删除,不知道为什么测试失败遍历:
map2:=map[string]int{ "k1":11,"k2":12, } fork,v:=rangemap2{ print(k,v,"\n") }func(pmytype)funcname(qint)(r,sint){return0,0} 保留字func用于定义一个函数; 函数可以定义用于特定的类型,这类函数更加通俗的称呼是method。这部分称
作receiver而它是可选的。它将在6章使用; funcname是你函数的名字; int类型的变量q是输入参数。参数用pass-by-value方式传递,意味着它们会被复
制; 变量r和s是这个函数的namedreturnparameters。在Go的函数中可以返回多个
值。参阅”多个返回值”在32。如果想要返回无命名的参数,只需要提供类型:(int,
int)。如果只有一个返回值,可以省略圆括号。如果函数是一个子过程,并且没有任
何返回值,也可以省略这些内容; 这是函数体,注意return是一个语句,所以包裹参数的括号是可选的。DEMO:
funcadd(aint,bint)(int,int,int){ returna+b,a,b } funcmain(){ a,b,c:=add(12,13) print(a,b,c) }函数的参数都是传值的形式。
1.命名返回的参数 funcadd(aint,bint)(int){ sum:=a+b returnsum } funcadd(aint,bint)(sumint){ sum=a+b return }在定义函数的返回类型的时候,加上类型对应的变量名,然后在函数体中,return后面不带参数,这样go就会找到函数体中的变量sum,然后返回。注意,由于定义函数的时候已经定义了sum变量,所以后面修改的时候不需要加冒号。
2.定义函数退出前执行的函数例如在打开文件的时候,每一次返回都需要关闭文件描述符,这样会有大量代码重复,在go中,可以定义函数退出前执行的函数。
functest(aint)(sumint){ deferprint("testdone") ifa<0{ return-1 }else{ return1 } }这样无论a是大于还是小于0,都会输出文字。
3.可变参数类似于Python的*args
functest(args...int)(int){ fori,v:=rangeargs{ print(i,v,"\n") } return1 } funcmain(){ test(1,2,3,4,5) } 4.快速定义函数类似于Python的lambda
add_one:=func(iint)int{ return1+i } print(add_one(2)) 5.函数作为参数 functest(iint,funfunc(int)int)(int){ i++ returnfun(i) } funcmain(){ add_one:=func(iint)int{ return1+i } print(test(2,add_one)) }最后的值是4,
6.恐慌和恢复go中没有异常的处理,只有恐慌和恢复
functhrownPanic(funfunc()int)(bbool){ deferfunc(){ ifr:=recover();r!=nil{ b=true } }() fun() return } funcmain(){ add_one:=func()int{ a:=[]int{1,2,3} print(a[0]) return1 } print(thrownPanic(add_one)) }在thrownPanic中,会调用fun,然后在函数结束前执行defer的函数,如果fun中产生了异常,r会为非nil,这样返回true,否则返回false
这样外层的函数就能知道调用fun是否产生了异常。 "runtime/debug" "reflect" "fmt" ) functest_func(){ deferfunc(){ iferr:=recover();err!=nil{ fmt.Println("Panic",err,reflect.TypeOf(err)) debug.PrintStack() } }() list:=[]int{1} println(list[1]) } funcmain(){ test_func()程序在执行println(list[1])的时候,会产生恐慌,也就是异常,但是程序不会立刻退出,还会执行defer的函数,这时,通过revocer函数,可以catch住这个异常,然后把异常信息打印出来,这样程序可以继续正常运行,其实跟tryexept差不多。
包说明
目录结构
/ test.go /util util1.go util2.goutil1.go:
packageutil funcadd(aint,bint)int{ //私有函数,只能在包内被调用 returna+b } funcAdd(aint,bint)int{ //公有函数,可以在其他包中调用 returna+b }test.go
packagemain import"./util"//注释 //注释 funcmain(){ print(util.Add(12,13)) }有多个地方用到util这个名字:
test.go中的import test.go中调用Add时的前缀 utils1.go中的package名字 utils1.go的文件名其中1,2,3需要一样,4可以不一样
在包中,变量或者函数名,根据首字母是否大写来判断该变量或函数是否公有的
在一个包中,也就是文件夹,不同的文件中的变量或函数名不能重复。 别名 importu"./util"//注释 //注释 funcmain(){ print(u.Add(12,13)) }导入util并设置别名为u
packagemain import."./util"//注释 //注释 funcmain(){ print(Add(12,13)) }别名设置为点,就不需要名字了
导入路径上面的导入方法是相对路径导入,即在util前面加上./
还有绝对路径的导入 import"shorturl/model"//加载GOROOT/src/shorturl/model模块 包的文档每个包都应该包含文档,如果包中有多个文件,文档可以在任意一个。格式:
/* Theregexppackageimplementsasimplelibraryfor regularexpressions. Thesyntaxoftheregularexpressionsacceptedis: regexp: concatenation|concatenation */ packageregexp 单元测试在util目录下面创建文件util_test.go:
packageutil import"testing" funcTestAdd(t*testing.T){ ifAdd(12,13)!=24{ t.Log("testAdd1213fail") t.Fail() } }然后cd到util目录,执行gotest,这样go就会调用所有*_test.go文件里面的Test*函数。在函数里面,如果测试失败,就调用t.Fail()
常用的包标准的Go代码库中包含了大量的包,并且在安装Go的时候多数会伴随一起安装。浏
览$GOROOT/src/pkg目录并且查看那些包会非常有启发。无法对每个包就加以解说,不过下
面的这些值得讨论:bfmt
包fmt实现了格式化的I/O函数,这与C的printf和scanf类似。格式化短语派生于C
。一些短语(%-序列)这样使用:
%v
默认格式的值。当打印结构时,加号(%+v)会增加字段名;
%#v
Go样式的值表达;
%T
带有类型的Go样式的值表达; io
这个包提供了原始的I/O操作界面。它主要的任务是对os包这样的原始的I/O进行封
装,增加一些其他相关,使其具有抽象功能用在公共的接口上。 bufio
这个包实现了缓冲的I/O。它封装于io.Reader和io.Writer对象,创建了另一个对象
(Reader和Writer)在提供缓冲的同时实现了一些文本I/O的功能。 sort
sort包提供了对数组和用户定义集合的原始的排序功能。
b描述来自包的godoc。额外的解释用斜体。
54Chapter4:包 strconv
strconv包提供了将字符串转换成基本数据类型,或者从基本数据类型转换为字符串
的功能。 os
os包提供了与平台无关的操作系统功能接口。其设计是Unix形式的。 sync
sync包提供了基本的同步原语,例如互斥锁。 flag
flag包实现了命令行解析。参阅”命令行参数”在第92页。 json
json包实现了编码与解码RFC4627[22]定义的JSON对象。 template
数据驱动的模板,用于生成文本输出,例如HTML。
将模板关联到某个数据结构上进行解析。模板内容指向数据结构的元素(通常结构的
字段或者map的键)控制解析并且决定某个值会被显示。模板扫描结构以便解析,而
“游标”@决定了当前位置在结构中的值。 http
http实现了HTTP请求、响应和URL的解析,并且提供了可扩展的HTTP服务和基本
的HTTP客户端。 unsafe
unsafe包包含了Go程序中数据类型上所有不安全的操作。通常无须使用这个。 reflect
reflect包实现了运行时反射,允许程序通过抽象类型操作对象。通常用于处理静态类
型interface{}的值,并且通过Typeof解析出其动态类型信息,通常会返回一个有接
口类型Type的对象。包含一个指向类型的指针,*StructType、*IntType等等,描述
了底层类型的详细信息。可以用于类型转换或者类型赋值。参阅6,第”自省和反射”
节。 exec
exec包执行外部命令。go中也有指针,但是和C有区别,不能进行指针运算。
varp*int;//p=nil //*p=8;//这样会报错,因为p还没有分配内存 variint; p=&i;//令p的值等于i的内存值 *p=8;//相当于修改i的值 print(p,&i,i)//看到,p和*i是一样的,i=8 在类型的前面加星号,表示定义一个该类型的指针,定义之后,没有为指针分配内存,指针为nilgo中有两种方法可以分配内存:new和make
2.1newnew是声明一个变量,返回变量的指针。
varp1*int;//p=nil p2:=new(int) varp3int print(p1,"\n") print(*p2,"\n") print(p3,"\n")p1是一个指针,但是它还没有初始化
p2也是一个指针,它已经初始化了,初始化值为0
p3是一个变量,已经初始化,初始化值为0 2.2makemake只能声明slice,map和channel。返回值,而不是指针。
也可以使用new来声明指针,然后使用make来初始化: p2:=new([]int) //(*p2)[0]=1//这样是不行的,因为p2还没有初始化 *p2=make([]int,11) (*p2)[0]=1 print((*p2)[0])make([]int,11)声明了一个长度为11的切片slice
go的结构和C的结构类似,然后使用new来定义
typePersonstruct{ namestring ageint } p1:=new(Person) p1.name="kevin" p1.age=23 println(p1.name) println(p1.age)定义结构的方法:
typePersonstruct{ namestring ageint } func(p*Person)SetAge(vint)int{ p.age=v returnv } funcmain(){ p1:=new(Person) p1.SetAge(12) println(p1.age) }go中一个很重要的概念是goroutine,协程的英文是coroutine,第一个字母不同,即goroutine类似于协程,但是又有所不同,是go特殊的概念。
goroutine的特点: goroutine并行执行的,有着相同地址空间的函数。 轻量的 初始化的代价很低一个并发的DEMO:
packagemain import"time" //注释 funcf(namestring){ fori:=0;i<10;i++{ println(name,i) time.Sleep(1*1e9) } } funcmain(){ gof("f1") gof("f2") time.Sleep(15*1e9) }类似于线程,然后启动的方法也比较方便,只需要在前面加一个go的关键字。
1e9是一个内部的常量,是秒的意思goroutine之间通过channel来通讯,channel类似于队列,即Python中的Queue。
定义channel的时候,需要指定channel接受的类型,可以为int,string,和interface等。 varcchanint;//定义一个接受int类型的channel c=make(chanint) c<-1//向c中put一个对象 item:=<-c//从c中取一个对象所以上面的并发程序可以改为:
funcf(namestring){ fori:=0;i<10;i++{ println(name,add(i)) time.Sleep(1*1e9) } c<-1//向c中put一个对象 } funcmain(){ c=make(chanint) gof("f1") gof("f2") <-c <-c }在goroutine中,可以put,也可以get。
2.1缓冲上面的channel是无缓冲的,也就是put完之后,goroutine就会阻塞,直到有goroutine取走。
定义有缓冲的channel: ch:=make(chanint,1)1就是缓冲的数量
获取的时候,也是阻塞的,可以使用非阻塞的方法:
v,ok:=<-c如果有值,ok为true,否则为false
尽管是叫并发,但是同一时刻,只有一个goroutine在执行,也就是占用CPU,类似Python的线程和协程。
可以通过runtime.GOMAXPROCS(n)来设置同一个时刻运行的goroutine的数量,也可以修改环境变量GOMAXPROCS。读取文件
import"os" funcmain(){ buf:=make([]byte,1024) f,_:=os.Open("./test.data") deferf.Close() for{ n,_:=f.Read(buf) ifn==0{ break } os.stdout.write(buf[0:n])//必须使用write,如果使用println,会输出切片的内存地址 } }通过bufio读取文件
import"os" import"bufio" //注释 funcmain(){ buf:=make([]byte,1024) f,_:=os.Open("/etc/passwd") deferf.Close() r:=bufio.NewReader(f) w:=bufio.NewWriter(os.Stdout) deferw.Flush() for{ n,_:=r.Read(buf) ifn==0{break} w.Write(buf[0:n]) } }创建目录
if_,e:=os.Stat("name");e!=nil{ os.Mkdir("name",0755) }else{ //error }go的特点就是高性能和高并发
测试用例: 从1加到1000,执行一百万次,计算需要的时间。
使用linux的time命令来进行计时。
sum.go
packagemain funcsum(numint)int{ sum:=0 fori:=1;i<=num;i++{ sum+=i } returnsum } funcmain(){ sum_:=0 forj:=0;j<1000000;j++{ sum_+=sum(1000) } println(sum_) println(sum(1000)) }编译:
gobuildsum.go执行
time./sum结果:
real0m0.464s user0m0.460s sys0m0.001ssum.c
#include<stdio.h> longintsum(intnum){ longintsum=0; inti=0; for(i=1;i<=num;i++){ sum=sum+i; }; returnsum; } intmain(){ inti; longintsum_=0; for(i=0;i<1000000;i++){ sum_+=sum(1000); } printf("%ld\n",sum(1000)); printf("%ld\n",sum_); //printf(sum_); return0; };编译:
gccsum.c-fPIC-shared-osum.so执行
time./sumc结果:
real0m2.874s user0m2.856s sys0m0.000stest_sum.py
defsum(num): s=0 foriinxrange(1,num+1): s+=i returns defmain(): sum_=0 foriinrange(1000000): sum_+=sum(1000) printsum_ if__name__==__main__: main()执行
timepythontest_sum.py结果
real0m35.146s user0m34.814s sys0m0.125s在Python中调用C
test_sum_c.pyfromctypesimportcdll c_lib=cdll.LoadLibrary(./sum.so)
ifname==main: c_lib.main()
执行
timepythontest_sum_c.py结果
real0m2.899s user0m2.874s sys0m0.006s语言|go|C|Python|Python调用c
---|
用时:|0.464s|2.874s|35.146s|2.899s go竟然比c还快,而且快很多 Python非常慢博文为作者原创,未经允许,禁止转载。
本文内容总结:一、基础,1.HelloWorld程序,2.声明和赋值,3.控制结构,4.预定义的函数,5.array,slice和map,二、函数,三、包,四、进阶,1.指针,2.内存分配,2.3结构定义,五、接口,六、并发,1.goroutine,2.channel,3.并发问题,七、通讯,1.文件,2.命令行参数,3.网络,八、性能测试,1.go,2.C,3.Python,5.比较,
原文链接:https://www.cnblogs.com/Xjng/p/5913954.html