Go入门笔记。有很多东西还没有涉及。仅供参考。
求Go大佬指点。
安装(Sublime Text 3)
-
官网下载最新版Go并安装。同时安装好Sublime Text 3与Sublime插件Package Control。
-
使用Sublime插件Package Control安装插件Golang Build和GoSublime。
-
用
.go文件存储源代码。注意每个文件夹内的Go文件是自动相互关联的,即所有文件会被视作一个整体,编译产生的可执行程序名称为文件夹名称。用Sublime打开后可以Ctrl + Shift + B并选择编译选项。 -
GoSublime选项支持打开一个终端。用go help查看Go语言的终端命令。注意Sublime进行Go语言程序的运行都不能输入。
基本情况
1 | package main |
Hello World程序范例。
-
空格不区别,文末回车不区别,左大括号强制不换行。
-
使用
import "xxx"引入模块,可以一次引入多个模块形如import("xxx","yyy","zzz")。引用没有使用到的模块、声明没有使用的变量、废弃的返回值会导致编译报错。Go语言也支持直接从Github或者网络获取模块。 -
区分大小写,变量名只能以下划线和字母开头,不能以数字开头,不能包含空格(同C++)。变量名不能包含下述字符:
~ ! @ # $ % ^ & * ( ) ; - : " ' < > , . ? / { } [ ] + = / -
//进行单行注释,/**/进行多行注释。Go语言的许多语法与C和C++几乎完全一致。
输入输出
- 输入输出包含在
fmt模块中。
Println
输出并换行。逗号连接多个参数等价于空格作为分隔符输出。
输出。逗号连接多个参数等价于空格作为分隔符输出。
Printf
输出。使用方式和C++几乎完全一致。对于格式化字符串,新增%t表示布尔类型(输出true或false),%v表示通用类型(包括自定义结构体),%+v输出自定义结构体时会输出属性名称,%#v输出自定义结构体新建时的Go语法,%T输出类型,形如%[n]v指示输出第n个提供的参数(从1开始),%q输出未经转义的字符串。
Scanln
输入一行。Scanln(&a,&b,&c,...)直接将输入以空格作为分隔符赋值给各个变量。
Scan
输入。Scan(&a,&b,&c,...)直接将输入以空格或回车作为分隔符赋值给各个变量。
Scanf
输入。使用方式和C++几乎完全一致。可以用形如%5s表示输入5个字符,用%v以空格作为分隔符输入通用类型的各个参数。
数据类型
-
变量声明
var NAME TYPE。或者数组形如var NAME [n]TYPE。变量总是会被默认初始化为0。常量定义形如const NAME TYPE = xxx。省略TYPE会导致推断。 -
区别于
=进行赋值,用:=表示声明并赋值。类似于Python支持多个变量的赋值与声明,相应地支持一个函数的多返回值。 -
各种不同的声明方式:
1 | var ( |
-
用形如
A{3,3.5,"aaa",7}方式新建一个结构体对象。 -
iota是一个从0开始的自增量:
1 | var ( |
就会得到a,b,c,d分别是0,1,2,3。
int类型
支持^ ! * / % << >> & &^ + - | ^ == != < <= > >= && || = += -= *= /= %=和C类似(运算符优先级从左到右)。其中高优先级的^是单目运算符表示C中的~,而优先级的则表示异或。a &^ b表示a&(~b)。++和--只能作为独立语句,不能再作为表达式的一部分。
支持(u)int__的定义方式,__可以是8,16,32,64。注意类型之间不能自动转换,单独的声明(u)int类型根据机器字长而变化,是不可移植的。
float类型
支持float32和float64。
complex类型
支持complex64和complex128。
Slice类型和数组
形如A := []int32{1,2,3}声明一个数组。多维数组只有第一维可以省略。
也可以使用A := make([]TYPE,CUR_SIZE,MAX_SIZE)来声明一个长度为MAX_SIZE的切片A,其中前CUR_SIZE个元素被初始化了。
Go语言中的数组是一个值!与此相对,切片是一个引用,这与C++和Python都恰好相反。
类似于Python可以使用a[l:r]可以获得一个子列表包含a中[l,r)的元素,即一个切片(Slice)。可以省略l或r甚至两个都省略,表示一直延伸到两端。
1 | package main |
会发现a,b,c都改变了而d没有改变。
append(A,x,y,...): 在A切片后面追加x(CUR_SIZE后面追加,超过MAX_SIZE后会重新分配空间),返回值是新的切片,注意原切片不变(可以视作是一对L,R),而数组改变了。
string类型
必须用双引号定义。本质是一个byte数组。byte类型的本质是uint8,可以说两者等价。
单引号定义得到字符类型,是一个rune类型。rune类型的本质是int32,可以说两者等价,但是rune被用于表示字符。
用英文的`符号可以定义原生字符串。
字符串的默认值是"",而不是nil。
支持!= == < > + +=。
字符串本身是不可直接修改的,需要强行转换为[]byte或[]rune。
map类型
通过M := make(map[KEY_TYPE]VAL_TYPE)声明,基本使用方法和map几乎一致。KEY_TYPE需要定义==、!=,map自动按照关键字顺序排序。默认返回值是0。
delete(M,key)来删除一个元素。
迭代期间的增删是安全,但是并发程序同时读写或写写map会导致报错。
结构语法
结构语法和C++几乎完全相同,if和for等的实现不需要大括号。
if
if的基本用法例如:
1 | if EXPRESSION { |
switch
1 | switch EXPRESSION { |
与C++不同的是去除了每个case后面需要break的要求。相反用fallthrough来跳过默认的break。
for
for的基本用法例如:
1 | for i := 0; i < n; i++ { |
而for完全取代了while,例如:
1 | for EXPRESSION { |
甚至可以表示while(true),例如:
1 | for { |
可以用for-range来访问一个数组(切片)。range得到的对象是一个二元组i,A[i]。注意如果定义了i却不使用是会编译错误的,用_代替可以避免报错。例如数组求和:
1 | s := int32(0) |
这里用range访问数组得到的是数组A中元素的复制,在循环体中改变A不会影响a的值。
goto, break, continue
和C++一致。不过配合goto LABEL的标签,可以指定break LABEL或continue LABEL来指定跳出/重新循环的层级。
函数
1 | func FUNCTION(ARGUMENTS) RETURN_TYPE { |
也支持隐式返回值: 给返回值命名NAME RETURN_TYPE。然后在程序操作过程中修改NAME后最后直接return。
一个简单的多返回值函数例如:
1 | package main |
多变量赋值的时候是现在计算完所有的右值,然后进行赋值操作。
ARGUMENTS填入...可以表示任意数量参数。使用range来访问:
1 | package main |
也可以使用一个匿名函数(闭包):
1 | package main |
匿名函数也可以作为值传递:
1 | package main |
用defer FUNCTION关键字表示延迟调用,在函数结束的时候(返回返回值以后)再调用FUNCTION。
1 | package main |
结构体
一个结构体可以嵌入其他结构体,直接获得其他结构体的成员。
1 | type NAME struct { |
结构体的方法:
1 | package main |
这里的(p *Point)类似于Python的self。Go语言不支持p->x这种写法,然而可以直接用指针调用成员函数,即p.x和(*p).x等价。
1 | type NEW_TYPE TYPE |
类似于C++的typedef。
接口
1 | type NAME interface { |
类似于C++中的通用类型(template)。即对于多个不同的STRUCT,各自支持一个或几个意义相近而实现不同的函数FUNCTION,那么就可以用接口来实现通用类型。例如:
1 | package main |
这样AorB接口既可以作为一个B的对象又可以作为一个A的对象。注意要实现一个接口必须要求A和B都定义了AorB中所描述的函数,而且接口函数必须传入值本身而不是指针,本例中必须定义func (a A) F()而不是func (a *A) F()。接口对于实现一个通用类型函数非常有用。
异常处理
error类型是一个接口类型,只需要包含Error()函数,返回一条错误信息字符串。
可以用errors.New(string)返回一个error对象。也可以使用fmt.Errorf(string)。
panic(SOMETHING)立即中断当前函数流程,执行延迟调用,在延迟调用中配合recover()可以将SOMETHING作为返回值。
error当没有发生异常时为nil。
并发
goroutine
相对于普通函数调用F(),将其写为go F()就会启动一个新的goroutine运行目标函数,其比普通的线程更加高效(内存消耗更少,创建与销毁的开销更小,切换开销更小)。
注意函数运行时并不会等待goroutine,所以往往配合人工制造的等待time.Sleep()。
channel
A := make(chan TYPE)来声明一个通道。然后goroutine之间就可以使用通道来互相传递信息。
A <- SOMETHING向通道内输入信息。
<-A将通道内的信息以值的形式输出,使用方法例如SOMETHING = <-A。输出会导致堵塞: 如果当前通道内没有内容则会一直等待直到有内容输出或通道关闭。
close(A)来关闭一个通道。
select语句相当于通道处理的switch语句,例如:
1 | select { |
这里SOMETHING往往是一个通道的输入输出动作。如果当前缓冲区内没有滞留的输入输出,就会执行default,如果没有写default则会堵塞直到出现一个通道输入输出操作并执行。如果当前缓冲区内已经有多个滞留的输入输出,则会随机执行一个。与无条件for循环配合可以实现持续监听。
模块
BIF
new(): 新建一个对象。
len(x): 返回一个对象的长度或是包含的元素个数。
append(): 数组、切片追加元素。
copy(A,B): 将B拷贝到A,拷贝的实际长度是两者长度的最小值。
imag()、real(): 复数的实部和虚部。
delete(): 删除map元素。
close(): 关闭channel。
panic(),recover(): 异常处理。
os
OpenFile(PATH,TYPE,PERM): 其中TYPE包括只读O_RDONLY,只写O_WRONLY,可读可写O_RDWR,创建O_CREATE。而如果文件不存在,PERM指定新建文件的权限,一般为0777表示可读可写。返回一个*os.File对象和一个error信息。
一个使用的例子是:
1 | file,_ := os.OpenFile("/a.txt",os.O_WRONLY|os.O_CREATE,0777) |
File.Read([]byte)读取文件内容,返回读取字节数和error信息。File.Write()写入文件。File.Close()关闭。
getwd(): 返回当前工作目录绝对路径PATH和error信息。
Chdir(PATH): 将工作目录改变到PATH返回error信息。
strings
Contains(A,B): B是不是A的子串。
ContainsAny(A,B): A和B是否有相同字符。
ContainsRune(A,r): r是否是A的字符。
Index(A,B): 返回B在A第一次出现的位置或-1。
IndexAny(A,B): 返回B中某个字符在A第一次出现的位置或-1。
IndexFunc(A,r,F): 返回在A第一次出现的满足F(r)为true的字符或-1。
Count(A,B): 求B在A中出现的次数。
HasPrefix(A,B): B是不是A的前缀。
HasSuffix(A,B): B是不是A的后缀。
ToUpper(A)/ToLower(A): 转换大小写后返回字符串。
Split(A,TOKEN): 将A按照TOKEN分割,去掉TOKEN返回一个字符串数组。
SplitAfter(A,TOKEN): 将A按照TOKEN分割,TOKEN分到前一个字符串里返回一个字符串数组。
Join(A[],TOKEN): 将A[]以TOKEN为分隔符连接返回一个字符串。
Replace(A,B,C,n): 将A中的前n个子串B替换为C返回一个字符串。
strconv
ParseBool(STRING): 将STRING转换为bool。其中1,t,T,TRUE,true,True都会转换为真,类似的转换为假,其他值得到错误。
ParseFloat(STRING,SIZE): 将STRING转换为float,SIZE指定大小(32或64)。
ParseInt(STRING,BASE,SIZE): 将STRING转换为int,BASE指定进制,SIZE指定大小。
Atoi(STRING): 将STRING转换为int。一般使用这个而不是ParseInt()。
FormatInt(INT,BASE): 将int转换为STRING,BASE指定进制。
Itoa(INT): 将int转换为STRING。一般使用这个而不是FormatInt()。
io/ioutil
Reader()是一个接口。这里的TYPE只需要自定义Read函数。同理有Writer()接口和Closer()接口。
var a bytes.Buffer建立一个缓冲区。支持a.Write(SOMETHING)将数据写入a,已经a.Read()读入、a.ReadByte()逐字节读入和a.ReadRune()逐4字节读入返回一个字符。
ioutil包中是许多io的接口:
Discard是一个io.Write对象,其Write()函数什么也不做。
ReadAll(Reader): 读取所有数据([]byte)。
ReadFile(STRING): 读取字符串对应的文件中的所有数据。
WriteFile(STRING,DATA,PERM)是一个io.Write对象,清空文件重写,如果文件不存在就创建。DATA是一个[]byte。
fmt
Println(),Print(),Printf(),Scanln(),Scan(),Scanf()。
Errorf(): 输出到标准错误流。
Fprint(writer,...),Fprintf(writer,...): 输出到writer,只要是一个io.Writer对象。
time
time.Sleep(T): 睡眠。其中T是一个时间类型,常用的有time.Second和time.Millisecond。例如: time.Sleep(300 * time.Millisecond)来睡眠300ms。
time.After(T): 返回一个通道,睡眠时间T后这个通道会输出一个消息。
time.Tick(T): 返回一个通道,每隔T时间这个通道就会输出一个消息。
sync
WaitGroup类型。var a sync.WaitGroup用于新建一个计数器。每次可以用a.Add(x)使计数器增加x,用a.Done()使计数器减1。a.Wait()会堵塞直至计数器为0。
Mutex类型。var a sync.Mutex用于新建一个锁。a.Lock()和a.Unlock()用于加锁和解锁。注意传递锁的时候需要用指针,否则复制新锁,是没有意义的。不支持重复锁定(即一个goroutine在持有同一把锁的时候再次申请这把锁)。
runtime
Gosched(): 让当前goroutine暂停,等待下次调度的时候恢复执行。
Goexit(): 执行defer语句后退出当前goroutine。
NumCPU(): 当前系统的CPU核数量。
GOMAXPROCS(x): 设置最大可同时使用的CPU核数目为(x)。
reflect
注意reflect速度较慢。
TypeOf(x): 获得x的类型。x.Name()是这个变量类型(可以是自己定义的类型),x.Kind()表示这个变量类型的本质(底层实现)。
ValueOf(x): 获得x的值,返回一个reflect.value对象。value.Interface.(TYPE)可以强制转换为TYPE。如果x是一个指针用value.Elem()可以得到*x的值。
value.CanSet(x): 表示是否可以更改。
net/http
1 | import "net/http" |
来实现一个服务器。OPERATION例如:
1 | fmt.Fprintf(w,"Hello World\n") |
扫描二维码即可在手机上查看这篇文章,或者转发二维码来分享这篇文章:

