Go 语言学习的入门部分,如果有C语言的基础类比学习会非常迅速。总结来说 Go语言 和 C语言很相似,语法更为简单,所以写起来会相对迅速很多。以下是基础语法的笔记。
>教程来自 点击这里
感谢博主的精心编写。
导入包
import "fmt" import "math"
或者下面:
import ( "fmt" "math" )
导出名字
go 中,首字母大写名称是被导出的。全小写的名字是不会被导出。
fmt.Println(math.Pi) //这里PI 改为 pi 就是不可以的。
函数
func add(x int ,y int) int { return x + y }
或者
func add(x,y int) int { return x + y }
函数中命名返回值
func split (sum int )(x,y int){ x = sum * 4 /9 ; y = sum - x ; return ; }
变量
变量定义看起来颇为随意,很方便。
var c, python , java = true , false , "no!" var i , j int = 1 , 2 func main(){ fmt.Println(i,j,c,python,java) }
变量在函数内还可以去掉var等关键字,直接退化为如下操作:
func main(){ var i , j int = 1 , 2 k := 3 c , python , java = true , false , "no!" fmt.Println(i,j,k,c,python,java) }
基本数据类型
bool string int int8 int16 int32 int64 uint uint8 uint16 uint32 uint64 uintptr byte //uint8 别名 rune //int32 的别名 //代表一个unicode码 float32 float64 complex64 complex128
int,uint,uintptr在32位系统上是32位,而在64位系统上是64位。一般直接使用int就可以了。一般仅有特定理由才用定长整型或者无符号整型。
var ( ToBe bool = false MaxInt uint64 = 1<<64 - 1 z complex128 = cmplx.Sqrt(-5 + 12i) ) func main(){ const f = "%T(%v)\n" fmt.Printf(f,z,z) fmt.Printf(f,MaxInt,MaxInt) fmt.Printf(f,ToBe,ToBe) }
零值
变量在定义时候没有明确的初始化时会赋值为零值.
数值类型为 **0** 布尔类型为 **false** 字符串为 "" **空字符串** package main import "fmt" func main(){ var i int var f float64 var b bool var s string fmt.Printf("%v %v %v %q\n",i,f,b,s) }
类型转换
表达式T(v)将值v转换为类型T。
一些关于数值的转换
var i int = 42 var f float64 = float64(i) var u uint = uint(f) //或者更为简单的形式 i := 42 f := float64(i) u := uint(f)
类型推导
在定义一个变量却并不显式指定其类型的时候(使用 := 语法或者 var =表达式语法),变量的类型由(等号)右侧的值推导得出.
当右值定义了类型时候,新变量的类型与其相同:
var i int j := i // j也是一个int
当时当右边包含了未指明类型的数字常量时候,新的变量就可能是int、float64或complex128 。 这取决于常量的精度:
i := 42 //int f := 3.142 //float64 g := 0.867 + 0.5i //complex128
常量
常量的定义与变量类似,只不过使用 const 关键字。
常量可以是字符、字符串、布尔或数字类型的值。
常量不能使用 := 语法定义。
package main import "fmt" const Pi = 3.14 func main(){ const World = "世界" fmt.Println("Hello",World) fmt.Println("Happy",Pi,"Day") const Truth = true fmt.Println("Go rules?",Truth) }
数值常量
数值常量是高精度的值。
一个未指定类型的常量由上下文来决定其类型。
也尝试一下输出 needInt(Big) 吧。(int 可以存放最大64位的整数,根据平台的不同有时会更少。)
const( Big = 1 << 100 small = Big >> 99 ) func needInt(x int) int {return x * 10 + 1} func needFloat(x float64) float64 { return x * 0.1 }
控制流
for
Go 只有一种循环结构 — for循环。这点和C比较相似。具体如下:
func main(){ sum : = 0 for i := 0 ; i < 10 ; i++ { sum += i } fmt.Println(sum) }
其中循环初始化语句和后置语句都是可选的:
func main(){ sum := 1 for ; sum < 1000 ; { sum += sum } fmt.Println(sum) }
这里和C语言很相似,但是不需要括号,如果有括号反而会报错。
for 也可以变成 C语言的中while
func main(){ sum := 1 for sum < 1000 { sum += sum } fmt.Println(sum) }
或者变为死循环
func main(){ for { //无退出条件,变成死循环 } }
if
就像 for循环一样,Go 的 if语句也不要求用 ()括号括起来,但是 {} 大括号表示作用域的内容还是必须要有的。
func sqrt(x float64) string { if x<0 { return sqrt(-x) + "i" } return fmt.Sprint(math.Sqrt(x)) }
if的便捷语句
跟 for语句一样,if语句可以在条件之前执行一个简单的语句。由这个语句定义的变量的作用域仅在 if 范围之内。
func pow(x,n,lim float64) float64 { if v := math.Pow(x,n); v < lim { return v } return lim }
if和else
在 if 的便捷语句定义的变量同样可以在任何对应的else块中使用。(提示:两个 pow 都在main 中调用 fmt.Println 前执行完了)
func pow(x,n,lim float64) float64 { if v:math.Pow(x,n); v<lim{ return v } else { fmt.Printf("%g >= %g\n",v,lim) } return lim }
swith语句
switch除非以 fallthrough 语句结束,否则分支会自动终止。
switch os: rumtime.GOOS ; os{ case "darwin": fmt.Println("OS X") case "linux": fmt.Println("Linux") default: //freebsd , openbsd, //plan9,windows... fmt.Printf("%s.",os) }
switch 的执行顺序
switch 的条件从上到下执行,当匹配成功的时候停止。
switch i { case 0 : case f(): } //当i==0时候不会调用 **f()** today := time.Now().Weekday() switch time.Saturday { case today + 0 : fmt.Println("Today.") case today + 1: fmt.Println("Tomorrow") case today + 2: fmt.Println("In two days") default: fmt.Println("Too far away") }
没有条件的swtich
没有条件的switch 同 switch true一样。这一构造使得可以用更清晰的形式来编写长的if-then-else 链。
t := time.Now() switch { case t.Hour() < 12 : fmt.Println("Good morning!") case t.Hour() < 17 : fmt.Println("Good afternoon!") default: fmt.Println("Good evening.") }
defer 语句
defer 语句会延迟函数的执行,直到上层函数返回。延迟调用的参数会立即生成,但是在上层函数返回前函数都不会调用。
func main(){ defer fmt.Println("world") fmt.Println("hello") }
defer 栈
延迟函数调用被压入一个栈中。当函数后进先出的顺序调用被延迟的函数调用。
fmt.Println("counting") for i:= 0 ; i<10 ; i++{ defer fmt.Println(i) } fmt.Println("done")
指针
go 具有指针。指针保存了变量的内存地址。
类型 *T 是执行类型为 T 的指针,其零值是 nil。
var p *int
&符号会生成一个指向其作用对象的指针。
i := 42 p = &i
* 符号表示指针指向的底层的值。
fmt.Println(*p) //通过指针p读取i *p = 21 //通过指针p设置i
这也就是通常所说的“间接引用”或“非直接引用”。
与C不同,Go 没有指针运算。
i,j := 100 , 666 p := &i fmt.Println(*p) //通过p读取I的值 *p = 777 fmt.Println(i) p = &j *p = *p / 37 fmt.Println(j)
结构体字段
结构体字段使用点号来访问(这点和C很像)
type Vertex struct { X int Y int } func main(){ v:= Vertex(1,2) v.X = 4 fmt.Println(v.X) }
结构指针
结构体字段可以通过结构体指针来访问。
通过指针间接的访问是透明的
type Vertex struct{ X int Y int } func main(){ v := Vertex{1,2} p := &v p.X = 1e9 fmt.Println(v) }
结构体符文
结构体符文表示通过结构体字段的值作为列表来新分配一个结构体。
使用 Name: 语法可以仅列部分字段。(字段名的顺序无关。)
特殊的前缀 & 返回一个指向结构体的指针。
type Vertex struct { X,Y int } var ( v1 = Vertex{1,2} //类型为 Vertex v2 = Vertex{X:1} //Y:0 被省略 v3 = Vertex{ } //X:0 和 Y:0 p = &Vertex{1,2} //类型为 *Vertex )
数组 (和C语言的数组概念类似)
类型 [n]T 是一个有 n 个类型为 T 的值的数组。
表达式
var a [10]int
定义变量 a 是一个有十个整数的数组。
数组的长度是其类型的一部分,因此数组不能改变大小。这看起来是一个制约,但是Go提供一个更加便利的方式来使用数组。
切片(slice)
一个 slice 会指向一个序列的值,并且包含了长度信息。
[]T 是一个元素类型为 T 的切片(slice)。
len(s) 返回 slice s 的长度。
func main(){ s := []int{2,3,4,5,11,13} fmt.Println("s ==",s) for i:= 0 ; i < len(s) ; i++{ fmt.Printf("s[%d] == %d\n",i,s[i]) } }
slice的切片
切片slice 可以包含任意的类型,包括另一个 slice
func main(){ //create a tic-tac-toe board game := [][]string{ []string{"_","_","_"}, []string{"_","_","_"}, []string{"_","_","_"}, } game[0][0] = "X" game[2][2] = "O" game[2][0] = "X" game[1][0] = "O" game[0][2] = "X" printBoard(game) } func printBoard(s [][]string){ for i:=0 ; i < len(s); i++{ fmt.Printf("%s\n",strings.Join(s[i]," ")) //TODO 不懂 } }
对slice切片
slice可以重新切片,创建新的 slice 值指向相同的数组。
表达式:
s[lo:hi]
表示从 lo 到 hi-1 的slice元素,含前端,不包含后端。因此:
s[lo:lo]
是空的,而
s[lo:lo+1]
具备一个元素。
func main(){ s := []int {2,3,5,7,11,13} fmt.Println("s == ",s) fmt.Println("s[1:4] ==",s[1:4]) //省略下标从0 开始 fmt.Println("s[:3] == ",s[:3]) //省略上标到 len(s) 结束 fmt.Println("s[4:] == ",s[4:]) }
构造 slice
slice 由函数 make 创建。这会分配一个全是零值的数组并且返回一个 slice 指向这个数组:
a := make([]int,5) // len(a) = 5
为了指定容量,可传递第三个参数到make :
b := make([]int,0,5) //len(b) = 0 , cap(b) = 5 b = b[:cap(b)] //len(b) = 5 , cap(b) = 5 b = b[1:] //len(b) = 4 , cap(b) = 4 以下为示例代码: func main(){ a := make([]int,5) printSlice("a",a) b := make([]int,0,5) printSlice("b",b) c := b[:2] printSlice("c",c) d := c[2:5] printSlice("d",d) } func printSlice(s string, x []int){ fmt.Printf("%s len=%d cap = %d %v\n",s,len(x),cap(x),x) }
nil slice
slice 的零值是 nil
一个 nil 的 slice 的长度和容量是 0
var z []int fmt.Println(z,len(z),cap(z)) if z == nil{ fmt.Println("nil!") }
向slice添加元素
向 slice 的末尾添加元素是一种常见操作,因此 Go 提供了一个内建函数 append 。内建函数的文档对 append 有详细介绍。
func append(s []T, vs ...T) []T
append 的第一个参数是 s 是一个元素类型为 T 的 slice ,其余类型为 T 的值将会附加到该 slice 的末尾。
append 的结果是一个包含原 **slice** 所有元素加上新添加的元素 **slice**。
如果 s 的底层数组太小,而不能容纳所有值的时候,会分配一个更大的数组。返回 slice 会指向这个新分配的数组。
func main(){ var a []int printSlice("a",a) a = append(a,0) printSlice("a",a) a = append(a,1) printSlice("a",a) a = append(a,2,3,4) printSlice("a",a) } func printSlice(s string, x []int){ fmt.Printf("%s len=%d cap=%d %v\n", s , len(x) , cap(x) , x) }
范围(range)
for 循环的 range 格式可以对 slice 或者 map 进行迭代循环。
当使用 for 循环遍历一个 slice 时候,每次迭代 range 将返回两个值。第一个是当前的下标(序号),第二个是该下标所对应元素的一个拷贝。
var pow = []int{1,2,4,8,16,32,64,128} func main(){ for i,v:= range pow{ fmt.Printf("2 ** %d = %d \n",i,v) } }
通过赋值给 _ 来忽略序号和值。
如果只需要索引值,去掉 “,value” 的部分就可以了。
func main(){ pow := make([] int,10) for i := range pow{ pow[i] = 1 << uint(i) } for _,value := range pow{ fmt.Printf("%d\n",value) } }