Go语言基础-基础语法

83 阅读7分钟

这是我参与「第五届青训营 」伴学笔记创作活动的第 1 天

Go语言基础语法

一、Go语言的特点

  1. 高性能、高并发
  2. 语法简单
  3. 丰富的标准库
  4. 完善的工具链
  5. 静态链接
  6. 快速编译
  7. 跨平台
  8. 垃圾回收

二、Go语言的结构

package main
​
import (
    "fmt"
)
​
func main() {
    fmt.Println("hello world")
}
​

1. package main

在Go语言编写的程序中,如果只有一个文件,那package必须是main(表明其是一个可独立执行的程序),否则会报错

package定义的是包名,每一个源文件都必须在非注释的第一行指明该文件属于什么包。

2. import “fmt”

Go的import和java的import一样,是导入包,但不需要像java一样一个一个去import,go支持了花括号的导入(只用写一个import,包写在花括号里就行)

fmt是go语言的一个包,它里面实现了标准化的IO(输入与输出)的函数。

fmt.Println和java的System.out.println是一样的用法,后面的ln均表示换行。

fmt也支持和C一样的格式化输出,不过fmt的格式化是可以用%v,然后由编译器自动推导,+v可以打印出详细的结果,#v会更加详细

    s := "hello"
    n := 123
    p := point{1, 2}
    fmt.Println(s, n) // hello 123
    fmt.Println(p)    // {1 2}
​
    fmt.Printf("s=%v\n", s)  // s=hello
    fmt.Printf("n=%v\n", n)  // n=123
    fmt.Printf("p=%v\n", p)  // p={1 2}
    fmt.Printf("p=%+v\n", p) // p={x:1 y:2}
    fmt.Printf("p=%#v\n", p) // p=main.point{x:1, y:2}
​
    f := 3.141592653
    fmt.Println(f)          // 3.141592653
    fmt.Printf("%.2f\n", f) // 3.14

3.func

和C一样,main函数是go程序开始执行的函数,和C不同的就是函数的定义和声明前面要加一个func

三、Go语言变量的声明

1.变量的声明

var name type
const name 

其中var是声明变量的关键字,name是变量的名字,type是变量的类型(type可以不加,go是可以自动推导类型的)

const是声明常量的关键字,go的常量没有确定的类型,一般会根据上下文去自动推导

声明变量也可以使用:=

a:=1
//相当于
var a int
a = 1var a := 1 //这会产生编译错误,var和:=重复声明了

多变量的声明

var name1, name2 type //声明同一类型的多个变量var (
    a int
    b string
    c []float32
    d func() bool
    e struct {
        x int
    }
)
//声明多种类型的多个变量

如果你声明了一个局部变量却没有在相同的代码块中使用它,会发生编译错误

2.变量的类型及初始化

  1. 整形:0
  2. 浮点型:0.0
  3. 布尔型: false
  4. 字符串型:""(空字符串)

以下几种特殊的变量为nil:

var a *int  //指针
var a []int  //数组
var a map[string] int//kv数据,和c++的map相似
var a chan int//管道,进行进程间通信的
var a func(string) int//函数
var a error //错误提示

四、Go语言的基本语法

1. 判断语句

go语言的if else语句和C++的写法类似,只不过go对于if后的判断条件是不需要加()的,而且判断条件的后面是必须有{}(如果加了(),那么在保存的时候编辑器会自动帮你去掉的)

if 7%2 == 0 {
        fmt.Println("7 is even")
    } else {
        fmt.Println("7 is odd")
    }

2.循环语句

与C++、java等语言最大的不同的是,go语言只有for循环,没有wihle、do while循环,最简单的for循环就是在for后面什么都不写,也就相当于一个死循环,for循环同样可以使用break或continue来跳出或继续循环

package main
​
import "fmt"func main() {
​
    i := 1
    for {
        fmt.Println("loop")
        break
    }
    for j := 7; j < 9; j++ {
        fmt.Println(j)
    }
​
    for n := 0; n < 5; n++ {
        if n%2 == 0 {
            continue
        }
        fmt.Println(n)
    }
    for i <= 3 {
        fmt.Println(i)
        i = i + 1
    }
}
​

3.条件分支语句

switch语句和C++也类似,不过也和for循环一样,switch后面是不用加()的,可以跟变量,也可以什么都不加,和C++最大的区别是不需要用break来跳出分支,它自己就可以跳出分支

package main
​
import (
    "fmt"
    "time"
)
​
func main() {
​
    a := 2
    switch a {
    case 1:
        fmt.Println("one")
    case 2:
        fmt.Println("two")
    case 3:
        fmt.Println("three")
    case 4, 5:
        fmt.Println("four or five")
    default:
        fmt.Println("other")
    }
​
    t := time.Now()
    switch {
    case t.Hour() < 12:
        fmt.Println("It's before noon")
    default:
        fmt.Println("It's after noon")
    }
}
​

4.数组

数组是一个具有编号且长度固定的元素序列,和C++的用法基本一致,可以用下标去访问数组元素,也可以使用初始值列表来初始化

    var a [5]int
    a[4] = 100
    fmt.Println("get:", a[2])
    fmt.Println("len:", len(a))
​
    b := [5]int{1, 2, 3, 4, 5}
    fmt.Println(b)
​
    var twoD [2][3]int
    for i := 0; i < 2; i++ {
        for j := 0; j < 3; j++ {
            twoD[i][j] = i + j
        }
    }
    fmt.Println("2d: ", twoD)

5.切片

这是go语言独有的结构(python也有,go的切片是不支持负数索引的),和C++的vecotr容器是类似的,都存有一个长度和一个容量,长度是可以任意更改的(当发生扩容时,append会返回一个全新的silce),slice拥有像python的切片一样的功能

    s := make([]string, 3)
    s[0] = "a"
    s[1] = "b"
    s[2] = "c"
    fmt.Println("get:", s[2])   // c
    fmt.Println("len:", len(s)) // 3
​
    s = append(s, "d")
    s = append(s, "e", "f")
    fmt.Println(s) // [a b c d e f]
​
    c := make([]string, len(s))
    copy(c, s)
    fmt.Println(c) // [a b c d e f]
​
    fmt.Println(s[2:5]) // [c d e]
    fmt.Println(s[:5])  // [a b c d e]
    fmt.Println(s[2:])  // [c d e f]
​
    good := []string{"g", "o", "o", "d"}
    fmt.Println(good) // [g o o d]

6.map

和C++的map是类似的,也是key-value模型,同样支持[]的访问(得到的是value值),和C++的map不同的是,go的map是完全无序的,对其进行遍历,得到的是随机的顺序,也就是说对同一个map去遍历,可能得到不同的结果

    m := make(map[string]int)
    m["one"] = 1
    m["two"] = 2
    fmt.Println(m)           // map[one:1 two:2]
    fmt.Println(len(m))      // 2
    fmt.Println(m["one"])    // 1
    fmt.Println(m["unknow"]) // 0
​
    r, ok := m["unknow"]
    fmt.Println(r, ok) // 0 falsedelete(m, "one")
​
    m2 := map[string]int{"one": 1, "two": 2}
    var m3 = map[string]int{"one": 1, "two": 2}
    fmt.Println(m2, m3)

7.range

相当于C++的范围for的使用

    m := map[string]string{"a": "A", "b": "B"}
    for k, v := range m {
        fmt.Println(k, v) // b 8; a A
    }
    for k := range m {
        fmt.Println("key", k) // key a; key b
    }

8.函数

go和其他语言不一样,变量的类型是后置的,同时go语言的函数是支持多个返回值的

package main
​
import "fmt"func add(a int, b int) int {
    return a + b
}
​
func add2(a, b int) int {
    return a + b
}
​
func exists(m map[string]string, k string) (v string, ok bool) {
    v, ok = m[k]
​
    return v, ok
}
​
func main() {
    res := add(1, 2)
    fmt.Println(res) // 3
​
    v, ok := exists(map[string]string{"a": "A"}, "a")
    fmt.Println(v, ok) // A True
}
​

9.指针

和C++的用法基本一致,但支持的操作很有限

func add2(n int) {
    n += 2
}
​
func add2ptr(n *int) {
    *n += 2
}
​
func main() {
    n := 5
    add2(n)
    fmt.Println(n) // 5
    add2ptr(&n)
    fmt.Println(n) // 7
}

10.结构体

结构体是带类型的字段的集合,和C++也基本一致,不过go多了结构体方法(将结构体变量的参数提到函数名的前面),其操作类似于类的成员函数。

package main
​
import "fmt"type user struct {
    name     string
    password string
}
​
func (u user) checkPassword(password string) bool {
    return u.password == password
}
​
func (u *user) resetPassword(password string) {
    u.password = password
}
​
func main() {
    a := user{name: "wang", password: "1024"}
    a.resetPassword("2048")
    fmt.Println(a.checkPassword("2048")) // true
}
​

11.错误处理

由于go语言天然支持函数返回多个值,所以一般直接可以在函数里返回错误信息,不像C++一样还需要异常处理,不仅能精准的定位到错误,还能直接用if else来处理错误

package main
​
import (
    "errors"
    "fmt"
)
​
type user struct {
    name     string
    password string
}
​
func findUser(users []user, name string) (v *user, err error) {
    for _, u := range users {
        if u.name == name {
            return &u, nil
        }
    }
    return nil, errors.New("not found")
}
​
func main() {
    u, err := findUser([]user{{"wang", "1024"}}, "wang")
    if err != nil {
        fmt.Println(err)
        return
    }
    fmt.Println(u.name) // wangif u, err := findUser([]user{{"wang", "1024"}}, "li"); err != nil {
        fmt.Println(err) // not found
        return
    } else {
        fmt.Println(u.name)
    }
}
​

12.数字解析

字符串和数字之间的转换,在go语言中,这些都在strconv这个包下。

ParseInt(),即将字符串转换为整数,第二个参数是控制进制的,ParseFloat则将字符串转换为float型的变量。

atoi()将十进制字符串转化为数字,而itoa()将数字转化为字符串

package main
​
import (
    "fmt"
    "strconv"
)
​
func main() {
    f, _ := strconv.ParseFloat("1.234", 64)
    fmt.Println(f) // 1.234
​
    n, _ := strconv.ParseInt("111", 10, 64)
    fmt.Println(n) // 111
​
    n, _ = strconv.ParseInt("0x1000", 0, 64)
    fmt.Println(n) // 4096
​
    n2, _ := strconv.Atoi("123")
    fmt.Println(n2) // 123
​
    n2, err := strconv.Atoi("AAA")
    fmt.Println(n2, err) // 0 strconv.Atoi: parsing "AAA": invalid syntax
}
​

总结

对于有C++、java、python基础的人来说,go语言的语法十分简单,很多东西是互通,同时,由于go语言是后面才诞生的,所以go呢汲取了前辈语言的的长处,还摒弃了他们的一些短处,比如直接用%v来代替所有的格式化输出,让学习成本更低了,switch自动跳出case,不用显示的写出break,一定程度上减少了代码的冗余。