Go 语言入门指南:基础语法和常用特性解析 | 豆包MarsCode AI刷题

107 阅读6分钟

1. 通过HelloWorld了解Go语言的基本结构

package main

import "fmt"

func main() {
    fmt.Println("Hello World go!")
}

该代码主要分为三个部分:

  • package main:包名,GoLand中包名默认与创建的空文件名相同,只有main包里才可以编译运行。
  • import "fmt":导入包,其中"fmt"是一个标准库包,用于格式化输入输出。它提供了类似于C语言中 printfscanf 的功能,但是更加强大和灵活(这点在后面会有体现)。
  • func main():主程序入口。

2. 变量与常量

2.1 变量

变量声明形如:

var a = "initial"
var b, c int = 1, 2
var d = true
var e float64

var声明的变量,Go会自动推断其类型,类似于python和C#,简单易用。

另一种声明方法形如:

f := float32(e)
g := a + "foo"

2.2 常量

在变量的基础上,将var改为const即可,形如:

const h string = "constant"
const i = 100000

3. if

与C++、java的if相似,区别在于条件不需要加括号

另有特殊形式:

if num := 9; num < 0 {
	fmt.Println(num, "is negative")
} else if num < 10 {
	fmt.Println(num, "has 1 digit")
} else {
	fmt.Println(num, "has multiple digits")
}

重点在于num := 9,可以像for一样在条件中声明变量

注意:else ifelse分支必须跟在上一个分支的} 后面,不能换行

4. for

与C++、java的for相似,区别在于:

  • for后面的三个部分(例如i := 0; i < 5; i++),每个部分都可以删除
  • for如果后面不跟条件,其效果等同于while死循环。

5. switch

与C++、java的switch相似,区别在于:

  • case不用加break,默认每个分支独立执行,相较于C++、java而言更加简易,但也有可能会使某些需要连续执行case的问题变得更加复杂
  • switch后面可以不写条件,而直接在case写条件,该部分几乎没有限制,相较于在switch后面必须写条件而言更加灵活。

6. 数组

var a [5]int
fmt.Println(a[4], len(a))
b := [5]int{1, 2, 3, 4, 5}

其中len(a)为获取a的长度,其余用法与C++、java相似,同样是长度固定的数组,因此写代码时更多使用的是切片

7. 切片

s := make([]string, 3) // 构造函数,3为长度
len(s) // 返回切片长度
s = append(s, "d") // 在切片末尾添加元素
copy(c, s) // 复制切片
s[2:5] // 索引2到5的元素,不包括索引5
s[:5] // 第1个元素到索引5的元素,不包括索引5
s[2:] // 索引2的元素到末尾
fmt.Println(s)

需要注意的是,append调用后必须赋值给原切片,这点与java、C#中的append略有区别。

8. map

m := make(map[string]int) // string为index的数据类型,int为value的数据类型
m := map[string]int{"a": 1, "b": 2, "c": 3}
m["one"] = 1
delete(map, "one")

类似C#中的索引器,简单来说就是索引不局限于整数类型的无序数组,有更灵活的操作空间。

需要注意的是,map通常是无序存储

9. range

m := map[string]int{"a": 1, "b": 2, "c": 3}
for k, v := range m {
    fmt.Println(k, v) // a 1 b 2 c 3
}
for k := range m {
    fmt.Println(k) // a b c(顺序不固定)
}

其中k为索引(key),v为数据(value),v可以省略

10. 函数

func add(a int, b int) int {}
func add(a, b int) (v int, ok bool) {}

函数名后第一个括号为传参,第二个括号为返回值(不写则表示无返回值),可以有多个,但第一个是真正的返回值,第二个值通常表示错误信息

11. 指针

func add2ptr(n *int) { 
    *n += 2 
}
add2ptr(&n)

形式类似C++,但传参逻辑类似java:传入常值时为副本,不会修改原本的变量,传入索引则可以修改原数据

12. 结构体

type [结构名] struct {
	name string
	password string
}

// 构造方法
a := user{ name:"wang", password:"1024" }
b := user{ "wang", "1024" } // 变量名可以省略
var d [结构名]

// 赋值变量
d.name = "wang"

不需要定义构造函数,有参无参都不需要,相较于C++而言更加简便。

13. 结构体方法

func ([形参名] [结构名]) checkPassword(password string) bool {}

在一般方法的关键字func后面(方法名前面)加上([形参名] [结构名]),同样可以使用指针。

14. 错误处理

// 导入包
import (
    "errors"
)

func findUser(users []user, name string) (v *user, err error) {
    return u, errors.New("not found")
}
u, err := findUser([]user{{"wang", "1024"}}, "wang")
if err != nil {} // nil表示空,类似于null

一般作为第二个返回值进行传递和处理。

15. 字符串

import (
    "strings" 
)

a := "hello"
strings.Count(a, "l") // 返回2('h'和'e',计算第一个"l"之前的字符数)

方法很多,就不一一列举了。

16. 字符串格式化

p := point{1, 2}
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.Printf("%.2f\n", f) // 3.14,2表示位数

类似C++中的printf,但更方便,只用%v就可以表示任意数据类型(这点比C++区分%s(字符串)、%d(十进制整数)等方便太多了),还可以通过%+v%#v%.来改变输出形式。

17. JSON处理

import (
    "encoding/json"
)
a := userInfo{Name:"wang", Age:18, Hobby:[]string{"Golang", "TypeScript"}}
buf, err := json.Marshal(a)
fmt.Println(buf) // [123 34 78 97](假设) 数字化序列
fmt.Println(string(buf)) // 字符串序列
var b userInfo
err = json.Unmarshal(buf, &b)

Marshal为数字化序列方法,Unmarshal为反数字化序列方法。

18. 时间处理

import (
    "time"
)
now := time.Now() // 获取当前时间
t := time.Date(2022, 3, 27, 1, 25, 36, 0, time.UTC) // 年、月、日、小时、分钟、秒、纳秒

同样有很多方法。

19. 数字解析

import (
    "strconv"
)
strconv.ParseFloat("1.234", 64) // 1.234
strconv.ParseInt("111", 10, 64) // 将111转为十进制的64位数字
strconv.ParseInt("0x1000", 0, 64) // 4096
strconv.Atoi("123") // 字符串转数字
strconv.itoA(123) // 数字转字符串

ParseInt("111", 10, 64)中,10表示十进制(0表示默认十进制),64表示位大小(通常为int32或int64)。

20. 进程信息

import ( "os" "os/exec" )
fmt.Println(os.Args) // 进程信息
fmt.Println(os.Getenv("PATH"))
fmt.Println(os.Setenv("AA", "BB"))

buf, err := exec.Command("grep", "127.0.0.1", "/etc/hosts").CombinedOutput()
  • os.Args 是一个字符串切片([]string),它包含了程序启动时从命令行接收到的参数。os.Args[0] 通常是程序的名称,而 os.Args[1:] 包含了传递给程序的其他参数。
  • exec.Command 函数用于创建一个新的进程来执行指定的命令。exec.Command 返回一个 *exec.Cmd 结构体,它代表了一个正在运行的或者尚未运行的命令。CombinedOutput 方法执行该命令,并返回命令的输出和执行时的错误(如果有的话)。