go语言基础语法



go语言笔记

go语法

变量定义

关键字var,而类型信息放在变量名之后,变量声明语句不需要使用分号作为结束符。示例如下:

var v1 int
var v2 string
var v3 [10]int // 数组
var v4 []int // 数组切片
var v5 struct {
    f int
}
var v6 *int // 指针
var v7 map[string]int // map,key为string类型,value为int类型
var v8 func(a int) int
var (
    v1 int
    v2 string
)

变量初始化

var v1 int = 10 // 正确的使用方式1
var v2 = 10     // 正确的使用方式2,编译器可以自动推导出v2的类型
v3 := 10        // 正确的使用方式3,编译器可以自动推导出v3的类型

错误写法:

var i int
i := 2

变量赋值

var v10 int
v10 = 123
i, j = j, i //支持变量直接交换

匿名变量

func GetName() (firstName, lastName, nickName string) {
    return "May", "Chan", "Chibi Maruko"
}
_, _, nickName := GetName() //_接受返回内容,但无法使用

常量

字面常量:

常量无类型,比如常量-12,它可以赋值给int、uint、int32、int64、float32、float64、complex64、complex128等类型的变量。

常量定义:

const Pi float64 = 3.14159265358979323846
const zero = 0.0 // 无类型浮点常量
const (
size int64 = 1024
eof = -1 // 无类型整型常量
)
const u, v float32 = 0, 3 // u = 0.0, v = 3.0,常量的多重赋值
const a, b, c = 3, 4, "foo"
// a = 3, b = 4, c = "foo", 无类型整型和字符串常量

预定义常量iota

const ( // iota被重设为0
    c0 = iota // c0 == 0
    c1 = iota // c1 == 1
    c2 = iota // c2 == 2
)
const (
    a = 1 << iota // a == 1 (iota在每个const开头被重设为0)
    b = 1 << iota // b == 2
    c = 1 << iota // c == 4
)
const (
    u = iota * 42 // u == 0
    v float64 = iota * 42 // v == 42.0
    w = iota * 42 // w == 84
)
const x = iota // x == 0 (因为iota又被重设为0了)
const y = iota // y == 0 (同上)
const ( // iota被重设为0
    c0 = iota // c0 == 0
    c1 // c1 == 1
    c2 // c2 == 2
)
const (
    a = 1 <<iota // a == 1 (iota在每个const开头被重设为0)
    b // b == 2
    c // c == 4
)

枚举

go语言没有类似java的枚举,只有常量,例如:

const (
    Sunday = iota
    Monday
    Tuesday
    Wednesday
    Thursday
    Friday
    Saturday
    numberOfDays // 这个常量没有导出
)

类型

基础类型

布尔类型:bool。
整型:int8、byte、int16、int、uint、uintptr等。
浮点类型:float32、float64。
复数类型:complex64、complex128。
字符串:string。
字符类型:rune。
错误类型:error。

结构类型

指针(pointer)
数组(array)
切片(slice)
字典(map)
通道(chan)
结构体(struct)
接口(interface)

布尔类型:不能接受其他类型的赋值,不支持自动或强制的类型转换:

var b bool
b = 1 // 编译错误
b = bool(1) // 编译错误 

整形:分为int ,int8,int16,int32,int64 和无符号的uint ,uint8,uint16,uint32,uint64
int和int32在Go语言里被认为是两种不同的类型,不能相互赋值,不能相互比较,但和常量可以赋值和比较:

var value2 int32
value1 := 64 // value1将会被自动推导为int类型
value2 = value1 // 编译错误 
value2 = int32(value1) // 通过强制转换后可以赋值,编译通过

var i int32
var j int64
i, j = 1, 2
if i == j { // 编译错误
    fmt.Println("i and j are equal.")
}
if i == 1 || j == 2 { // 编译通过
    fmt.Println("i and j are equal.")
}

位运算:Go语言支持位运算。左移,右移,异或,与,或,取反
x << y 左移 等于乘2
x >> y 右移 等于除以2

浮点型:Go语言定义了两个类型float32和float64。默认不带小数点的数值被自动推断为整形,带小数点的数被自动推导为float64.
浮点数不是一种精确的表达方式,不能直接==比较,推荐的替代方案:

import "math"
// p为用户自定义的比较精度,比如0.00001
func IsEqual(f1, f2, p float64) bool {
    return math.Fdim(f1, f2) < p
}

复数实际上由两个实数(在计算机中用浮点数表示)构成,一个表示实部(real),一个表示虚部(imag)。
对于一个复数z = complex(x, y),就可以通过Go语言内置函数real(z)获得该复数的实
部,也就是x,通过imag(z)获得该复数的虚部,也就是y。

字符串的内容可以用类似于数组下标的方式获取,但与数组不同,字符串的内容不能在初始
化后被修改:

var str string // 声明一个字符串变量
str = "Hello world" // 字符串赋值
ch := str[0] // 取字符串的第一个字符
fmt.Printf("The length of \"%s\" is %d \n", str, len(str))
fmt.Printf("The first character of \"%s\" is %c.\n", str, ch)
str := "Hello world" // 字符串也支持声明时进行初始化的做法
str[0] = 'X' // 编译错误

字符串操作有:x + y 字符串连接,len(s) 字符串长度,s[i] 取字符
Go语言支持两种方式遍历字符串。一种是以字节数组的方式遍历,一种是是以Unicode字符遍历

str := "Hello,世界"
n := len(str)
for i := 0; i < n; i++ {
    ch := str[i] // 依据下标取字符串中的字符,类型为byte
    fmt.Println(i, ch)
}//该方法获取的字符数量是13个

str := "Hello,世界"
    for i, ch := range str {
    fmt.Println(i, ch)//ch的类型为rune
}//该方法获取的字符数量是9个

在Go语言中支持两个字符类型,一个是byte(实际上是uint8的别名),代表UTF-8字符串的单个字节的值;另一个是rune,代表单个Unicode字符。

数组:

//数组定义
[32]byte // 长度为32的数组,每个元素为一个字节
[2*N] struct { x, y int32 } // 复杂类型数组
[1000]*float64 // 指针数组
[3][5]int // 二维数组
[2][2][2]float64 // 等同于[2]([2]([2]float64))

//数组的两种遍历方法
for i := 0; i < len(array); i++ { fmt.Println("Element", i, "of array is", array[i]) } for i, v := range array { fmt.Println("Array element[", i, "]=", v) }

数组是一个值类型(value type),所有的值类型变量在赋值和作为参数传递时都将产生一次复制动作。因此,在函数体中无法修改传入的数组的内容,因为函数内操作的只是所传入数组的一个副本。

数组切片的创建方法主要有两种——基于数组和直接创建。
基于数组的创建:

package main
import "fmt"
func main() {
    // 先定义一个数组
    var myArray [10]int = [10]int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}
    // 基于数组创建一个数组切片
    var mySlice []int = myArray[:5]
    fmt.Println("Elements of myArray: ")
    for _, v := range myArray {
        fmt.Print(v, " ")
    }
    fmt.Println("\nElements of mySlice: ")
    for _, v := range mySlice {
        fmt.Print(v, " ")
    }
    fmt.Println()
}

直接创建:

mySlice1 := make([]int, 5) //创建一个初始元素个数为5的数组切片,元素初始值为0

mySlice2 := make([]int, 5, 10)//创建一个初始元素个数为5的数组切片,元素初始值为0,并预留10个元素的存储空间

mySlice3 := []int{1, 2, 3, 4, 5}//直接创建并初始化包含5个元素的数组切片

事实上还会有一个匿名数组被创建出来了。
数组切片可以动态增减元素。
数组切片支持Go语言内置的cap()函数和len()函数,append()函数,copy()函数

map是一堆键值对的未排序集合。

var myMap map[string] PersonInfo //map声明
myMap = make(map[string] PersonInfo) //map创建
myMap = make(map[string] PersonInfo, 100) //map创建时指定初始存储能力
//创建并初始化
myMap = map[string] PersonInfo{
“1234”: PersonInfo{“1”, “Jack”, “Room 101,…”},
}
myMap[“1234”] = PersonInfo{“1”, “Jack”, “Room 101,…”} //元素赋值
delete(myMap, “1234”) //元素删除.如果“1234”这个键不存在,那么这个调用将什么都不发生,也不会有什么副作用

value, ok := myMap[“1234”]//元素查找
if ok { // 找到了
// 处理找到的value
}

流程控制

条件语句

关于条件语句,需要注意以下几点:
 条件语句不需要使用括号将条件包含起来();
 无论语句体内有几条语句,花括号{}都是必须存在的;
 左花括号{必须与if或者else处于同一行;
 在if之后,条件语句之前,可以添加变量初始化语句,使用;间隔;
 在有返回值的函数中,不允许将“最终的”return语句包含在if…else…结构中,
否则会编译失败:

选择语句

switch i {
    case 0:
        fmt.Printf("0")
    case 1:
        fmt.Printf("1")
    case 2:
        fallthrough//执行接下来的条件下的内容(这个例子中执行case 3 中的内容)
    case 3:
        fmt.Printf("3")
    case 4, 5, 6:
        fmt.Printf("4, 5, 6")
    default:
        fmt.Printf("Default")
}
switch {
    case 0 <= Num && Num <= 3:
        fmt.Printf("0-3")
    case 4 <= Num && Num <= 6:
        fmt.Printf("4-6")
    case 7 <= Num && Num <= 9:
        fmt.Printf("7-9")
}

循环语句

Go语言中的循环语句只支持for关键字

sum := 0
for i := 0; i < 10; i++ { sum += i }

sum := 0
for { sum++ if sum > 100 { break }
}

a := []int{1, 2, 3, 4, 5, 6}
for i, j := 0, len(a) – 1; i < j; i, j = i + 1, j1 { a[i], a[j] = a[j], a[i] }

函数

函数支持多返回值,不定参数(语法糖),闭包

错误处理

Go语言引入了一个关于错误处理的标准模式,即error接口

type error interface { Error() string }

调用时的代码建议按如下方式处理错误情况:

n, err := Foo(0)
if err != nil {
    // 错误处理
} else {
    // 使用返回值n
}

defer

defer可以定义语句或者函数,会在所属函数退出的时候执行。
一个函数中可以存在多个defer语句,因此需要注意的是,defer语句的调用是遵照先进后出的原则,即最后一个defer语句将最先被执行

panic()和recover()

panic()类似于java中的throw
recover类似于java中的try catch

panic(404)
panic("network broken")
panic(Error("file not exists"))
defer func() {
    if r := recover(); r != nil {
        log.Printf("Runtime error caught: %v", r)
    }
}()