分号 : 如果希望将两个或多个语句放在同一行,必须用分号分隔;一般情况下,不需要分号。
包
Go程序是通过包来组织的,包名为main
的是一个可独立运行的包,编译后产生可执行文件。main
包的main
函数是程序的入口,是个没有参数也没有返回值的函数。
可以使用GOPATH
环境变量或是使用相对路径来import自定义的package。Go约定如下:
1. 在import中,可以使用相对路径如./
或 ../
来引用自定义包;
2. 如果没有使用相对路径,那么Go会去$GOPATH/src/
目录查找。
变量与常量
变量声明
- 类型是后缀的,有时可以省略;
- 以var关键字开头,有时可以省略的;
- 自动类型推断;
- 可以平行定义多个变量;
举例:
var count int // 声明
var total int = 42 // 声明并初始化
var total, avg int = 42, 20 // 同时声明并初始化多个变量
var a, b * int // 声明两个int类型指针
var label = "string value" // 省略了类型,编译器自动推断
i := 123 // 定义并初始化
i, str := 123, "string value" // 平行定义,还可用用函数的返回值来定义
:=
只能在函数内部使用,在函数外使用会出现编译错误;一般用var
方式定义全局变量。
多个var声明可以成组,组用小括号,const和import也这样做。如:
import(
"fmt"
"os"
)
const(
i = 100
pi = 3.1415
prefix = "Go_"
)
var(
i int
pi float32
prefix string
)
当定义一个变量时,它默认赋值为其类型的null
值。如,var a int
,a
的值是0
,var s string
, s
被赋值为零长度的字符串,也就是""
。
一个特殊的变量名是 _
(下划线),任何赋值给它的值都被丢弃。所以 _
可以说是属于任何类型的。
声明但未使用的变量在编译时将报错。
### 常量
常量在编译时被创建,只能是数字、字符串或布尔值。
const (
a = iota
b = iota
)
iota
可以生成数值的递增的枚举值。iota
第一次使用时是0,以后每次使用值增加1。
const (
a = iota
b // 隐式的 b = iota
b string // 明确指定类型
)
数据类型
布尔类型
布尔类型表示由预定义常量true
和false
代表的布尔判定值。布尔类型是bool
。
数字类型
int 和 uint类型的长度取决于硬件平台。但只会是32或64位之一。
完整的整数类型列表:
有符号: int8, int16, int 32, int64
无符号: byte, uint8, uint16, uint32, uint64。
rune 是int32的别名,byte是uint8的别名。
浮点类型的值有float32和float64。(没有float类型)
64位的整数和浮点数总是64位的,即使是在32位的架构上。
这些类型都是独立的,混合使用这些类型向变量赋值会引起编译器错误。
rune
rune 是int32的别名,用UTF-8
编码。 在需要遍历字符串中的字符,为了获得实际的字符,需要使用rune类型。
string
字符串是用双引号或反引号包裹的字符序列,是不可变的,UTF-8
编码。可以用+
操作符来连接两个字符串。
用反引号作为原始字符串符号:
start part
<pre><code>
s :=
ending part`
现在s包含换行。
不像转义字符串标识,原始字符串标识的值在引号内的字符是不转义的。
复数
Go原生支持复数,complex128和complex64,虚数部分分别有64、32位。复数写为:RE + IMi
,RE是实数部分,IM是虚数部分。
var comp complex64 = 4 + 5i
fmt.Printf("value is : %v", comp)
将打印 (4+5i)
类型转换
Go不支持隐式的类型转换,所有的类型转换都必须是显式的。类型转换的方式:v dstType := dstType(srcVal)
。
bytes []byte := []byte("string value") // 字符串到字节数组的转换
str := string(bytes) // 字节数组到字符串的转换
ui := uint32(-128) // 有符号数到无符号数的显式转换
运算符
表格中没有列出的还有逻辑非 !
。
&^
表示位清除。
运算符都是从左到右结合的。Go不支持运算符重载。
Go保留字
array、slices、map
array
array 由[len]type
定义,len表示数组的长度,type表示存储元素的类型。对array元素的赋值或索引是由括号[]
完成的。
var arr [10]int
arr[0] = 43
arr[1] = 20
大小是类型的一部分,不同大小的是不同类型,因此不能改变大小。
数组同样是值类型,将一个数组赋值给另一个数组,会复制所有的元素。当向函数内传递一个数组的时候,它会获得一个数组的副本,而不是数组的指针。
[]
内可显示指定数组长度,也可用 ...
代替,在{}
里写数组元素,使用3个句点时,Go自动计算数组元素的个数。
a := []int {1, 2, 3} // 声明了长度为3的数组
b := [10]int{1, 2, 3} // 声明了长度为10的数组,前3个元素的值分别为1,2,3,其余元素的值为0。
c := [...]int{4, 5, 6} // 采用`...`的方式,Go根据元素个数来计算数组长度。
a2 := [2][2]int{ [...]int{4, 5}, [...]int{8,9} } // 二维数组
a3 := [2][2]int{ {4, 5}, {8,9} } // 二维数组,简化的声明方式
slice
slice与array接近,但在新的元素加入的时候可以增加长度。slice总是指向底层的一个数组。slice是一个指向数组的指针,是引用类型。slice总是与一个固定长度的array成对出现,其影响slice的容量和长度。
切片可以在数组或已有切片上创建:var s = arr[start : end]
,区间为:[start, end)
,start和end都可以省略,冒号:
不能省略,start默认为0,end默认为数组或切片的长度。
s1 := make([]int, 10)
创建一个保存10个元素的slice。
var s []int // 声明一个slice,不需要长度。
s := []byte{'a', 'b', 'c'} // 声明并初始化。
var array = [10]byte {'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j'} // 声明一个数组
sa := array[:] // 定义一个slice,它的元素是数组ar的所有元素。
aSlice := array[:3] // 等价于aSlice = array[0:3] aSlice包含元素: a,b,c
aSlice := array[5:] // 等价于aSlice = array[5:10] aSlice包含元素: f,g,h,i,j
aSlice := array[:] // 等价于aSlice = array[0:10] 这样aSlice包含了全部的元素
var arr[m] int
slice := arr[0 : n]
此时,len(slice) == cap(slice) == n
len(arr) == len(arr) == m
对于slice有几个有用的内置函数:
len
:获取slice的长度cap
:获取slice的最大容量append
:向slice里面追加一个或者多个元素,然后返回一个和slice一样类型的slicecopy
:函数copy从源slice的src中复制元素到目标dst,并且返回复制的元素的个数
注:append
函数会改变slice所引用的数组的内容,从而影响到引用同一数组的其它slice。 但当slice中没有剩余空间(即(cap-len) == 0
)时,此时将动态分配新的数组空间。返回的slice数组指针将指向这个空间,而原数组的内容将保持不变;其它引用此数组的slice则不受影响。
map
map也是一种引用类型。一般定义map的方法是: map[key_type] value_type
。
mapv := map[string]int {"key" : 1, "key2" : 2, }
最后的逗号是必须的。
当只需要声明一个map时候,使用make形式: monthdays := make( map[string]int )
。
检查元素是否存在,可用这种形式:value, present = monthdays["Jan"]
。
删除元素: monthdays["Mar"] = 0, false
或者delete(monthdays, "Mar")
。
添加元素: monthdays["Feb"] = 29
。
流程控制
流程控制分三大类:条件判断,循环控制和无条件跳转。
控制结构的条件判断无需小括号,而语句体必须总是包含在大括号内。
条件语句 if
if [init stmt;] condition {
// true statements
} else {
// false statements
}
// if和switch接受初始化语句,通常用于设置一个(局部)变量。
if err := file.Chmod(0664); err != nil { // 左大括号必须放在行尾
log.Stderr(err)
return err
}
goto
goto 跳转的目标一定是当前函数内定义的标签。标签的定义:单独一行,此行第一个词,以冒号结束,标签名大小写敏感。
func mufunc() {
i := 0
Here: // 此行第一个词,以冒号结束为标签
println(i)
i++
goto Here // 跳转
}
循环语句 for
for循环有三种形式,只有一种使用分号。
for init; condition; post {} // 和C的for一样
for condition {} // 和while一样
for {} // 死循环
for i, j := 0, len(a) - 1; i < j; i, j = i + 1, j - 1 { // 结合使用平行赋值
a[i], a[j]= a[j], a[i]
}
break、continue
break提前退出循环,continue终止当前的循环。
循环嵌套循环时,可以在break后指定标签,用标签决定哪个循环被终止。
break和continue还可以跟着标号,用来跳到多重循环中的外层循环。
range
range可用于在slice、array、string、map、channel上循环。range是个迭代器,当被调用的时候,从它的循环的内容中返回一个键值对,基于不同的内容,range返回不同东西。
当对slice或array做循环时,range返回序号作为键,序号对应的内容作为值。
分支语句 switch
switch 表达式不必是常量或整数,执行的过程从上至下,直到找到匹配项,如果没有表达式,会匹配true
。
fallthrough
可以使匹配的分支执行完后继续执行紧邻的下一个分支的语句。
分支可以使用逗号分隔的列表,表示匹配多个值中的一个:case '0', '1' : stements;
,表示匹配'0', '1'
中的一个。
default可以指定其他所有分支都不匹配时的行为。
举例:
var result int
switch byte {
case 'a', 'b': // 逗号分割列表
result = 1
default:
result = 0
}
switch result := calculate(); true {
case result < 0: // 不再局限于匹配整数、字符
/* negative */
case result > 0:
/* positive */
default:
/* zero */
}
欢迎关注我的微信公众号: coderbee笔记,可以更及时回复你的讨论。