变量、类型和关键字
完整的整数类型列表(符号和无符号)是
int8 , int16 , int32 , int64 和 byte , uint8 , uint16 , uint32 ,
uint64
混合用这些类型向变量赋值会引起编译器错误,例如下面的代码:
package main
func main() {
var a int //← 通用整数类型
var b int32 //← 32 位整数类型
a = 15
b = a + a //← 混合这些类型是非法的
//cannot use a + a (type int) as type int32 in assignment
println(b)
b = b + 5 //← 5 是一个(未定义类型的)常量,所以这没问题
println(b)
}
字符串
字符串在 Go 中是 UTF-8 的由双引号(”)包裹的字符序列。如果你使用单引号(’)则
表示一个字符(UTF-8 编码)——这种在 Go 中 不是 string。
一旦给变量赋值,字符串就不能修改了:在 Go 中字符串是不可变的。从 C 来的用户,
下面的情况在 Go 中是非法的。
var s string = "hello"
s[0] = 'c' //← 修改第一个字符为 ’c’ ,这会报错
在 Go 中实现这个,需要下面的方法:
package main
import "fmt"
func main() {
var s = "hello"
var c = [] rune (s)//转换 s 为 rune 数组
c[0] = 'c'//修改第一个字符为 ’c’
var s2 = string (c)//创建 新的 字符串 s2 保存修改;
fmt.Printf("%s\n", s2)
}
多行字符串
基于分号的置入,你需要小心使用多行字符串。如果这样写:
s := "Starting part"
+ "Ending part"
会被转换为:
s := "Starting part" ;
+ "Ending part" ;
这是错误的语法,应当这样写:
//正确的方式一:
s := "Starting part" +
"Ending part"
正确的方式二:
Go 就不会在错误的地方插入分号。另一种方式是使用反引号 ` 作为 原始 字符串符号:
//正确的方式二:
s := `Starting part
Ending part`
fmt.Printf("Value is: %v", s)//Printf() 的 %v 参数含义是用默格式打印这个值
控制结构
在 Go 中只有很少的几个控制结构例如这里没有 do 或者 while 循环,只有
for 。有(灵活的) switch 语句和 if ,而 switch 接受像 for 那样可选的初始化语句。还有叫做类型选择和多路通讯转接器的 select,语法有所不同(同 C 相比):
无需圆括号,而语句体必须 总是 包含在大括号内。
if
if x > 0 { //← { 是强制的
return y
} else {
return x
}
//===============
//下面的语法在 Go 中是非法的:
if err ! = nil
{ // ← 必须同 if 在同一行
return err
}
//if 和 switch 接受初始化语句,通常用于设置一个(局部)变量。
if err := Chmod(0664) ; err ! = nil { //← nil 与 C 的 NULL 类似
fmt.Printf(err) //← err 的作用域被限定在 if 内
return err
}
goto
Go 有 goto 语句——明智的使用它。用 goto 跳转到一定是当前函数内定义的标签。例如假设这样一个循环:
package main
func main() {
myfunc()
}
func myfunc() {
i := 0
Here: // ← 这行的第一个词,以分号结束作为标签,标签大小写敏感
println(i)
i++
if i > 10 { //打印1 到 10 ,这里不加条件会是无限循环
return
}
goto Here// ← 跳转
}
for循环
Go 的 for 循环有三种形式,只有其中的一种使用分号。
for init ; condition ; post { } //← 和 C 的 for 一样
for condition { } //← 和 while 一样
for { } //← 死循环
//由于 Go 没有逗号表达式,而 ++ 和 – 是语句而不是表达式,如果你想在 for 中
//执行多个变量,应当使用 平行赋值。
// Reverse a
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 后指定标签。用标签决定 哪个 循环被终止:
package main
func main() {
myfunc()
}
func myfunc() {
for j := 0 ; j < 5 ; j++ {
for i := 0 ; i < 10 ; i++ {
if i > 2 {
break //← 现在终止的是 j 循环,而不是 i 的那个
}
println(i)
}
println(j)
}
}
//continue
package main
func main() {
for i := 0 ; i < 10 ; i++ {
if i > 5 {
continue //← 跳过循环中所有的代码 println(i)
}
println(i)
}
}
range迭代器
range 是个迭代器,当被调用的时候,从它循环的内容中返回一个键值对
package main
import "fmt"
func main() {
list := [] string { "a", "b", "c", "d", "e", "f" }
for k, v := range list {
// 对 k 和 v 做想做的事情
fmt.Printf("list[%d] = %s\n",k,v)
}
}
// list[0] = a
// list[1] = b
// list[2] = c
// list[3] = d
// list[4] = e
// list[5] = f
switch
可以使用 fallthrough 使其匹配失败后自动向下尝试。
没有 fallthrough :
switch i {
case 0: // 空的 case 体
case 1:
f() // 当 i == 0 时, f 不会被调用!
}
而这样:
switch i {
case 0: fallthrough
case 1:
f() // 当 i == 0 时, f 会被调用!
}
用 default 可以指定当其他所有分支都不匹配的时候的行为。
switch i {
case 0:
case 1:
f()
default :
g() // 当 i 不等于 0 或 1 时调用
}
分支可以使用逗号分隔的列表。
package main
// import "fmt"
func main() {
println(shouldEscape('?')) //true
}
func shouldEscape(c byte ) bool {
switch c {
case ' ', '?', '&', '=', '#', '+':// ← , as ”or”
return true
}
return false
}
内建函数
//Go 中的预定义函数
close new panic complex
delete make recover real
len append print imag
cap copy println
可以使用命令查看函数文档
go doc builtin // 获得关于内建类型和函数的在线文档。
查看某个函数文档
//获得copy的文档 go doc builtin copy
close
//用于 channel 通讯。使用它来关闭 channel,
delete
//用于在 map 中删除实例。
len 和 cap
//可用于不同的类型, len 用于返回字符串、slice 和数组的长度。
new
//用于各种类型的内存分配。
make
//用于内建类型(map、slice 和 channel)的内存分配。
copy
//用于复制 slice。
append
//用于追加 slice。
panic 和 recover
//用于 异常 处理机制。
print 和 println
//是底层打印函数,可以在不引入 fmt 包的情况下使用。它们主要用于调试。
complex 、 real 和 imag
//全部用于处理 复数。
array、slices 和 map
array
array 由 [n]<type>
定义,n 标示 array 的长度,而 <type>
标示希望存储的内容的类型。对 array 的元素赋值或索引是由方括号完成的:
package main
import "fmt"
func main() {
// var arr = [10] int 这样的数组类型有固定的大小。大小是类型的一部分 。由于不同的大小是不同的类型,因此不能改变大小
var arr [10] int
arr[0] = 42
arr[1] = 13
fmt.Printf("The first element is %d\n", arr[0])
}
var arr = [10] int
这样的数组类型有固定的大小。大小是类型的一部分 。由于不同的大小是不同的类型,因此不能改变大小。数组同样是值类型的:将一个数组赋值给另一个数组,会 复制 所有的元素。尤其是当向函数内传递一个数组的时候,它会获得一个数组的副本,而不是数组的指针。
可以像这样声明一个数组:var a [3] int,如果不使用零来初始化它,则用复合声明:
a := [3] int{ 1, 2, 3 }
也可以简写为 a := [...] int{ 1, 2, 3 }
,Go 会自动统计元素的个数。
//多维数组
a := [3][2] int { [2] int { 1,2 } , [2] int { 3,4 } , [2] int { 5,6 } }
slice
slice 是一个 指向 array 的指针,这是其与 array 不同的地方;slice 是引用
类型,这意味着当赋值某个 slice 到另外一个变量,两个引用会指向同一个 array。
sl := make ([] int , 10)
//错误的示例,如果要扩展slice需要使用append和copy
package main
func main() {
var array [100] int //← Create array, index from 0 to 99
slice := array[0:99] //← Create slice, index from 0 to 98
slice[98] = 'a'// ← OK
slice[99] = 'a'// ← Error: ”throw: index out of range”
}
使用append扩展slice
package main
import "fmt"
func main() {
s0 := [] int { 0, 0 }
s1 := append (s0, 2)//追加一个元素, s1 == []int{0, 0, 2} ;
fmt.Printf("%v\n",s1)
s2 := append (s1, 3, 5, 7)//追加多个元素, s2 == []int{0, 0, 2, 3, 5, 7} ;
fmt.Printf("%v\n",s2)
//...可变长参数 http://studygolang.com/articles/1965
s3 := append (s2, s0...)//追加一个 slice, s3 == []int{0, 0, 2, 3, 5, 7, 0, 0} 。注意这三个点!
fmt.Printf("%v\n",s3)
}
// [0 0 2]
// [0 0 2 3 5 7]
// [0 0 2 3 5 7 0 0]
copy的使用
package main
import "fmt"
func main() {
var a = [...] int { 0, 1, 2, 3, 4, 5, 6, 7 }
var s = make ([] int , 6)
n1 := copy (s, a[0:])// ← n1 == 6, s == []int{0, 1, 2, 3, 4, 5}
fmt.Printf("%v\n",s)
fmt.Printf("%v\n",n1)
//copy返回拷贝的元素个数
n2 := copy (s, s[2:])// ← n2 == 4, s == []int{2, 3, 4, 5, 4, 5}
fmt.Printf("%v\n",s)
fmt.Printf("%v\n",n2)
}
map
一般定义 map 的方法是: map[<from type>]<to type>
package main
import "fmt"
func main() {
monthdays := map [ string ] int {
"Jan": 31, "Feb": 28, "Mar": 31,
"Apr": 30, "May": 31, "Jun": 30,
"Jul": 31, "Aug": 31, "Sep": 30,
"Oct": 31, "Nov": 30, "Dec": 31,// ← 逗号是必须的
}
// monthdays := make ( map[ string ] int )//当只需要声明一个 map 的时候,使用 make 的形式
fmt.Printf("%d\n", monthdays["Dec"])//打印出 12 月的天数
year := 0
for _, days := range monthdays { //← 键没有使用,因此用 _, days
year += days
}
fmt.Printf("Numbers of days in a year: %d\n", year)
//向 map 增加元素,可以这样做:
monthdays["Undecim"] = 30 //← 添加一个月
monthdays["Feb"] = 29 //← 闰年时重写这个元素
//检查元素是否存在的两种方式
//var value int
var present bool
//value, present = monthdays["Jan"]
_, present = monthdays["Jan"] //← 如果存在, present 则有值 true
println(present)
//← 或者更接近 Go 的方式
//v, ok := monthdays["Jan"]
_, ok := monthdays["Jan"]// ← “逗号 ok ”形式
println(ok)
//也可以从 map 中移除元素:
delete(monthdays, "Mar") //← 删除 ”Mar” 吧
}
参考文章: