day1 Go语言基础 | 豆包MarsCode AI 刷题

108 阅读15分钟

- Go 语言入门指南:基础语法和常用特性解析

1. package(包)

1.1 定义和作用

  • 在 Go 语言中,package是一种组织代码的方式,用于将相关的 Go 源文件组合在一起,类似存放相关文件的文件夹。一个package中的代码可被其他package引用和使用。
  • 例如开发 Web 应用程序时,可能有处理 HTTP 请求的package、数据库操作的package和业务逻辑的package,这种组织方式使代码结构更清晰,便于维护和扩展。

1.2 main 包的特殊之处

  • 当 Go 程序中有package被命名为main时,此package被视为程序入口点。在main包中必须有main函数,它是程序的起点。

2. import(导入)

2.1 定义和作用

  • import语句用于将其他包中的代码引入当前包,使当前包能使用被导入包中的函数、类型、变量等。例如,若要在程序中使用fmt包中的Println函数输出,需用import语句导入fmt包。

2.2 导入方式

2.2.1 标准库导入
  • Go 语言标准库的包可直接用import语句导入。如导入用于数学计算的math包,可使用import "math"。导入后可使用其中功能,如math.Sqrt(4)可计算 4 的平方根。
2.2.2 第三方包导入
  • 对于第三方包,首先要确保已安装(通常用go get命令安装)。例如导入名为github.com/gin - gonic/gin的 Web 框架包,先在命令行运行go get github.com/gin - gonic/gin,然后在 Go 程序中用import "github.com/gin - gonic/gin"导入,之后即可使用该包功能构建 Web 应用程序。

2.3 导入路径的注意事项

  • 导入路径必须准确,尤其对于第三方包,应指向包的实际存储位置。如github.com/gin - gonic/gingithub.com表示包存于 GitHub 上,gin - gonic是 GitHub 上的用户或组织名,gin是具体包名。
  • 若导入路径错误,Go 编译器无法找到相应包,导致编译错误,如拼写错误或使用错误版本路径(导入不存在的版本标签)都可能引发问题。

3. 变量定义

3.1 基本变量定义

3.1.1 使用var关键字
  • Go 语言中基本变量定义使用var关键字,语法格式为var <变量名> <类型>。如var age int定义名为age的整数类型变量,这种方式定义变量时可不初始化,此时变量被赋予其类型零值(整数类型零值是 0,字符串类型零值是"",布尔类型零值是false)。
  • 也可在定义变量同时初始化,如var name string = "John",定义名为name的字符串变量并初始化为"John"
3.1.2 类型推断
  • Go 语言有类型推断功能。若初始化变量时提供初始值,编译器可根据初始值推断变量类型。如var height = 175,编译器推断height是整数类型(int)变量,这种方式在变量类型明显时可使代码更简洁。

3.2 短变量定义(:=

3.2.1 语法和使用场景
  • Go 语言用:=操作符作为更简洁的短变量定义方式。如count := 10定义并初始化名为count的变量。此方式只能在函数内部使用,且变量必须新定义,不能用于在同一作用域内重新定义已有变量。
  • 短变量定义在函数内快速声明和初始化局部变量很有用。如在函数内用for i := 0; i < 10; i++ { // 使用短变量定义的 i 进行循环}临时使用变量存储计算结果或遍历数据。

3.3 多个变量定义

3.3.1 使用var关键字定义多个变量
  • 可用var关键字一次定义多个变量,语法格式为var (<变量名 1> <类型 1>, <变量名 2> <类型 2>,...)。如var (width int, height int)定义两个整数变量widthheight,也可在定义时初始化,如var (x int = 1, y int = 2)
3.3.2 使用短变量定义多个变量
  • 短变量定义也可一次定义多个变量。如a, b := 1, 2定义两个变量ab并分别初始化为 1 和 2。这种方式在接收返回多个值的函数调用时很有用,如result1, result2 := someFunction()

3.4 变量的作用域

3.4.1 块级作用域
  • 在 Go 语言中,变量作用域遵循块级规则,{}(花括号)包裹的代码块定义变量的作用域。如在函数内部定义的变量,其作用域仅限于该函数内部,函数执行完毕,变量超出作用域并(在自动内存管理情况下)被销毁。
  • iffor等控制结构中定义的变量,其作用域也仅限于相应控制结构块内。如if condition := true; condition { // 变量 condition 的作用域仅限于这个 if 块}
3.4.2 全局变量和局部变量
  • 全局变量是在函数外部定义的变量,其作用域从定义位置开始到整个源文件结束,可在多个函数中访问,但使用全局变量可能增加代码复杂性和耦合性。例如var globalVar int定义一个全局变量globalVar
  • 局部变量是在函数内部或块内部定义的变量,其作用域仅限于定义它的函数或块,通常用于存储临时数据,如函数参数、函数内部计算结果等。

4. for 循环

  • Go 语言的for循环有三种形式。
  • 最常见的是类似 C 语言的 “三段式”for循环:for 初始语句; 条件表达式; 后置语句 { 循环体 }。例如for i := 0; i < 10; i++ { fmt.Println(i) },其中i := 0是初始语句(定义并初始化循环变量i),i < 10是条件表达式(用于判断是否继续循环),i++是后置语句(用于更新循环变量)。
  • Go 语言可省略初始语句、条件表达式或后置语句中的部分内容。若省略条件表达式,相当于while循环。例如i := 0; for i < 10 { fmt.Println(i); i++ },先定义i,只要i < 10就执行循环体并在内部更新i
  • 还有for - range形式,主要用于遍历数组、切片、字符串、通道(channel)等数据结构。例如遍历切片:nums := []int{1, 2, 3}; for index, value := range nums { fmt.Printf("Index: %d, Value: %d\n", index, value) },它能自动获取切片中元素的索引(index)和值(value)。

5. if语句

  • Go 语言的if与c语言中的if差别在go的if语句后的判断条件省略小括号,并且条件表达式的结果必须是布尔类型(bool),执行语句块中须保留大括号。

6.array

6.1 定义

  • 在 Go 语言中,数组是具有固定长度的同类型元素的序列。定义数组时需要指定元素类型和数组长度。语法是[长度]元素类型。例如,var numbers [5]int定义了一个包含 5 个整数的数组。数组中的元素会被初始化为对应类型的零值,对于int类型,零值是 0,所以numbers数组初始值为[0,0,0,0,0]

6.2 初始化

-   可以在定义数组时进行初始化。有多种初始化方式:

    -   **完整初始化**:可以指定数组中所有元素的值。例如,`var fruits [3]string = [3]string{"apple", "banana", "cherry"}`。也可以省略数组长度,让编译器根据初始化的值自动推断数组长度,如`var colors = [3]string{"red", "green", "blue"}`。
    -   **部分初始化**:如果只初始化部分元素,未初始化的元素会被初始化为零值。例如,`var scores [5]int = [5]int{1, 2}`,那么`scores`数组的值为`[1, 2, 0, 0, 0]`。
  • 6.3 与切片(Slice)的区别

    • 数组长度固定,而切片长度是可变的。切片可以看作是对数组的一个引用窗口,它底层引用了一个数组,但可以通过一些操作(如append)来改变长度。例如,slice := numbers[1:3]创建了一个切片,它引用了numbers数组中索引从 1 到 2(不包括 3)的元素。切片在 Go 语言中使用更加灵活,是很多数据处理场景中的常用数据结构。

7.slice

  • 7.1 定义与初始化

    • 定义:切片(Slice)是 Go 语言中一种灵活的数据结构,它是对数组的抽象和扩展。切片本身不存储数据,而是引用了底层数组的一部分,可以理解为是一个动态大小的 “视图”。

    • 初始化方式

      • 基于数组创建:可以通过对数组进行切片操作来创建。例如,有数组arr := [5]int{1, 2, 3, 4, 5},那么slice := arr[1:3]就创建了一个切片,它引用了arr数组中索引为 1 和 2 的元素(左闭右开区间,即包含起始索引对应的元素,不包括结束索引对应的元素),slice的值为[2, 3]
      • 使用make函数创建:语法为make([]元素类型, 长度, 容量)。长度(len)是切片中当前元素的数量,容量(cap)是切片底层数组的大小。例如,slice := make([]int, 3, 5)创建了一个长度为 3、容量为 5 的int切片,初始值为[0, 0, 0]。如果省略容量参数,容量默认等于长度。
  • 7.2 长度和容量

    • 长度(len :可以使用len函数获取切片的长度,它返回切片中当前元素的个数。例如,对于切片slice := []int{1, 2, 3}len(slice)的值为 3。
    • 容量(cap :使用cap函数获取切片的容量,它表示切片底层数组的大小。例如,slice := make([]int, 3, 5)cap(slice)的值为 5。当切片的长度小于容量时,可以通过append操作来增加切片的长度。
  • 7.3 操作函数

    • append函数:用于向切片末尾添加元素。例如,slice := []int{1, 2}newSlice := append(slice, 3)会将 3 添加到slice的末尾,得到新的切片newSlice,其值为[1, 2, 3]。如果append操作导致切片长度超过了底层数组的容量,Go 会自动创建一个新的、更大的底层数组,并将原来的数据复制过来。
    • copy函数:用于将一个切片中的元素复制到另一个切片中。例如,srcSlice := []int{1, 2, 3}dstSlice := make([]int, 3)copy(dstSlice, srcSlice)会将srcSlice中的元素复制到dstSlice中。需要注意的是,copy函数会根据两个切片中较短的长度来进行复制。

8. map

  1. 定义与初始化
-   **定义**:在 Go 语言中,`map`是一种无序的键 - 值对(`key - value`)数据结构,用于存储和检索数据。`map`中的键必须是唯一的,并且可以通过键快速地查找、插入和删除对应的值。
-   **初始化方式**    -   **使用`make`函数**:语法为`make(map[键类型]值类型)`。例如,`m := make(map[string]int)`初始化了一个键为字符串类型、值为整数类型的`map`    -   **字面量初始化**:可以使用`{}`来初始化`map`。例如,`m := map[string]int{"apple": 1, "banana": 2}`,这个`map`包含了两个键 - 值对,键`"apple"`对应的值是 1,键`"banana"`对应的值是 2。
  1. 访问和修改元素

    • 访问元素:通过键来访问map中的值。例如,对于m := map[string]int{"apple": 1, "banana": 2},可以使用m["apple"]来获取键"apple"对应的值,这里会返回 1。如果访问的键不存在,会返回值类型的零值。例如,对于上述mapm["cherry"]会返回 0(因为int类型的零值是 0)。
    • 修改元素:可以通过键来修改map中的值。例如,m["apple"] = 3会将键"apple"对应的值修改为 3。
    • 添加元素:向map中添加新的键 - 值对就像修改元素一样操作。如果键不存在,就会添加一个新的键 - 值对。例如,m["orange"] = 4会在m这个map中添加一个新的键 - 值对{"orange": 4}
  2. 检查键是否存在

    • 由于访问不存在的键会返回值类型的零值,所以有时候需要检查键是否真正存在于map中。可以使用双值赋值的方式来检查。例如,value, ok := m["cherry"],这里ok是一个布尔值,如果键"cherry"存在于map中,oktrue,并且value是键对应的真实值;如果键不存在,okfalsevalue是值类型的零值。
  3. 删除元素

    • 可以使用delete函数来删除map中的键 - 值对。语法为delete(map变量, 键)。例如,对于m := map[string]int{"apple": 1, "banana": 2}delete(m, "apple")会删除键为"apple"的键 - 值对,之后m就只剩下{"banana": 2}这个键 - 值对了。
  4. 遍历map

    • map可以使用for - range循环来遍历。例如,对于m := map[string]int{"apple": 1, "banana": 2},可以使用for key, value := range m { fmt.Printf("Key: %s, Value: %d\n", key, value) }来遍历map中的所有键 - 值对,每次循环会将一个键 - 值对的键赋值给key,值赋值给value。需要注意的是,map的遍历顺序是不确定的,因为map本身是无序的数据结构。
  5. map作为函数参数

    • map作为函数参数是引用传递。这意味着在函数内部对map的修改会影响到函数外部的map。例如,func modifyMap(m map[string]int) { m["apple"] = 3 },当调用这个函数并传入一个map时,函数内部对map的修改会反映在外部的map上。这种特性使得map在函数间传递和共享数据时非常方便。

9.函数

  1. 函数定义

    • 在 Go 语言中,函数使用func关键字来定义。基本语法格式为func 函数名(参数列表) 返回值列表 { 函数体 }。例如,func add(x, y int) int { return x + y }是一个简单的函数定义。
    • 函数名是自定义的标识符,用于调用该函数。按照 Go 语言的命名惯例,函数名通常是小写字母开头的单词组合。
    • 函数体是花括号{}包裹的代码块,包含了函数要执行的具体操作。
  2. 函数参数

    • 参数列表:参数列表定义了函数接受哪些输入。参数可以是一个或多个,每个参数由参数名和参数类型组成。在上面的add函数示例中,x, y int表示函数add接受两个整数类型的参数xy
    • 参数传递方式:Go 语言中函数参数默认是值传递。这意味着在函数内部对参数的修改不会影响到函数外部的变量。例如,func modifyValue(n int) { n = 10 },在调用modifyValue(num)(假设num是一个整数变量)时,num的值不会被修改为 10,因为modifyValue函数接收到的是num的一个副本。
    • 可变参数:Go 语言支持可变参数函数,即函数可以接受不定数量的参数。可变参数使用...语法。例如,func sum(numbers...int) int { total := 0; for _, n := range numbers { total += n }; return total },这个函数可以接受任意数量的整数参数,并返回它们的总和。
  3. 函数返回值

    • 返回值列表:返回值列表定义了函数执行完毕后返回的数据。返回值可以是一个或多个,每个返回值都有对应的类型。在add函数中,int表示函数返回一个整数,即xy相加的结果。
    • 返回值命名(可选) :可以在函数定义的参数列表之后给返回值命名。例如,func divide(x, y int) (result int, err error) { if y == 0 { err = fmt.Errorf("除数不能为0") return } result = x / y; return },这里resulterr是返回值的名字,在函数体中可以像操作普通变量一样操作它们,最后通过return语句返回这些值。
    • 多返回值的用途:Go 语言的多返回值功能很有用,例如可以同时返回函数执行的结果和可能出现的错误。在实际编程中,这种方式使得错误处理更加方便和清晰,比如在数据库查询函数中,可以返回查询结果和可能出现的查询错误。

9.指针

  1. 指针运算方面

    • Go 语言

      • Go 语言对指针运算有严格限制。与 C 语言不同,Go 语言不支持指针的算术运算,如指针的加法、减法等。例如,在 C 语言中可以通过p++p为指针)来使指针指向下一个内存单元,但在 Go 语言中这样的操作是不允许的。这种限制是为了简化语言,减少因指针运算可能导致的内存错误,如越界访问等。
  2. 空指针方面

    • Go 语言

      • 在 Go 语言中,指针的零值是nil。当一个指针变量没有被初始化或者没有指向任何有效内存地址时,它的值为nil。并且 Go 语言在很多操作中会对nil指针进行安全检查,例如,当尝试通过nil指针进行解引用操作(*p,其中pnil)时,会产生运行时panic(类似于运行时错误)。
  3. 指针和数组的关系方面

    • Go 语言

      • 在 Go 语言中,虽然数组名可以被看作是指向数组第一个元素的指针,但这种关系相对较弱。例如,不能像在 C 语言中那样,简单地通过对数组名进行指针运算来访问数组元素。Go 语言鼓励使用更安全的方式,如索引访问(arr[i])或者for - range循环来操作数组。

性。

  1. 指针的安全性

    • Go 语言

      • Go 语言的指针设计更侧重于安全性和简单性。通过限制指针运算等操作,减少了因指针使用不当而导致的内存错误。同时,Go 语言的垃圾回收机制(GC)也使得开发者不需要像在 C 语言中那样,手动管理由指针引用的内存的释放,降低了内存泄漏的风险。

10.结构体

type user struct { name string password string }与c++类似