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

60 阅读12分钟

1.Hello World

package main

import(
	"fmt"
)

func main(){
	fmt.Println("这是我的第一个GO程序")
}

代码解析:

(1)package main:程序的入口文件

(2)import( “fmt” ):导入标准库里的fmt包,主要用于输入输出,格式化

(3)func main():主函数

(4) fmt.Println("Hello World"):Prinfln函数打印输出

接收输入的参数:

fmt.Println("请输入一个数字:")
	fmt.Scan(&score)

2.变量

(1)变量声明:

<1>var 变量名 = value

var a=6

这种声明方式会自动推断变量类型,也可以手动给出变量类型:

var a,b int = 1,2

<2>变量名 :=value

f:=float32(e)

(2)常量声明:

const 变量名 = value,

无确定类型,会根据常量的上下文,自动确定类型

const s string = "constant"
const h = 500000000

3.if-else

GoLang中的if-else,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")
	}

但是注意语句不可写在同一行

4.循环

GoLang中只有for 循环,没有while

for循环也有四种,分别是迭代,条件循环,死循环,数组迭代:

(1)迭代:

i := 1
for n := 0; n < 5; n++ {
	if n%2 == 0 {
		continue
	}
	fmt.Println(n) //1 3
}

(2)条件循环:

for i <= 3 {
	fmt.Println(i) //1 2 3
	i = i + 1
}

(3)死循环:

for{
    fmt.Print("这是一个死循环")
    //使用break跳出死循环
    //break
}

(4)数组迭代:

arr := [3]int{1, 2, 3}
	for index, value := range arr {
		fmt.Println("arr[", index, "]=", value, "\n")
	}

运行结果: image.png

5.switch

switch 语句用于基于不同条件执行不同动作,每一个 case 分支都是唯一的,从上至下逐一测试,直到匹配为止。 Golang switch 分支表达式可以是任意类型,不限于常量。可省略 break,默认自动终止。 可以替代多个if-else

实例:

var score int = 85
	var rank string
	switch{
	case score>=90:
		rank="A"
	case score>=80:
		rank="B"
	case score>=70:
		rank="C"
	case score>=60:
		rank="D"
	}
	switch rank{
	case "A":
		fmt.Println("good")
	case "B":
		fmt.Println("nice")
	case "C":
		fmt.Println("ok")
	case "D":
		fmt.Println("bad")
	}

case的类型可以任意,但是同一个switch分支中的case的类型需要一致,而且switch的后面可以有值,也可以没有,由此可见,switch的应用很灵活

6.数组

(1)数组的定义

在Go语言中,数组是具有相同类型的一组已编号且长度固定的元素序列。例如,var numbers [5]int定义了一个包含5个整数的数组。数组的长度是其类型的一部分,所以[5]int[3]int是不同的类型。 - 可以在定义数组时进行初始化,如var fruits = [3]string{"apple", "banana", "cherry"},也可以使用简短声明fruits := [3]string{"apple", "banana", "cherry"}。如果初始化时省略数组长度,Go语言会根据初始化元素的个数自动推断数组长度,例如fruits := []string{"apple", "banana", "cherry"}实际上定义的是一个切片(Slice),它和数组类似但长度可变。

(2)数组的访问和操作

访问元素:可以通过索引来访问数组中的元素。数组索引从0开始,所以对于上面定义的numbers数组,numbers[0]表示第一个元素,numbers[4]表示第五个元素。例如:

package main 
import "fmt" 
func main() { 
   var numbers [5]int 
   numbers[0] = 1 
   numbers[1] = 2 
   fmt.Println(numbers[0]) } 

修改元素:同样通过索引可以修改数组中的元素。例如:

package main 
import "fmt" 
func main() { 
    var fruits = [3]string{"apple", "banana", "cherry"} 
    fruits[1] = "grape" 
    fmt.Println(fruits) 
}

遍历数组:可以使用for循环来遍历数组。有两种常见的方式:

<1>普通 for循环:

package main 
import "fmt" 
func main() { 
   var numbers [5]int 
   for i := 0; i < len(numbers); i++ { 
       numbers[i] = i * 2 
   } 
   for i := 0; i < len(numbers); i++ { 
       fmt.Printf("%d ", numbers[i]) 
   } 
}

<2>for - range循环: 这种方式更简洁,它会返回数组的索引和对应的值。

package main 
import "fmt" 
func main() { 
   var fruits = [3]string{"apple", "banana", "cherry"} 
   for index, value := range fruits { 
       fmt.Printf("Index: %d, Value: %s\n", index, value) 
   } 
} 

(3)数组的传递和函数参数

在Go语言中,数组是值传递的。这意味着当把一个数组作为参数传递给一个函数时,函数会得到数组的一个副本,而不是原始数组。例如:

import "fmt"
func modifyArray(arr [3]int) {
   arr[0] = 100
}
func main() {
   myArray := [3]int{1, 2, 3}
   modifyArray(myArray)
   fmt.Println(myArray)
}

在上述代码中,modifyArray函数内部修改了数组arr,但是在main函数中myArray的值并没有改变,因为传递给modifyArray函数的是myArray的副本。如果想要在函数内部修改原始数组,可以传递数组的指针,如下所示:

import "fmt"
func modifyArrayPointer(arr *[3]int) {
    (*arr)[0] = 100
}
func main() {
    myArray := [3]int{1, 2, 3}
    modifyArrayPointer(&myArray)
    fmt.Println(myArray)
}

(4)多维数组

Go语言支持多维数组。例如,定义一个二维数组var matrix [3][4]int,它可以看作是一个包含3个元素的数组,每个元素又是一个包含4个整数的数组。初始化二维数组可以这样:

import "fmt"
func main() {
    var matrix [3][4]int
    matrix[0][0] = 1
    matrix[0][1] = 2
    matrix[1][0] = 3
    // 初始化二维数组的另一种方式
    anotherMatrix := [2][3]string{
        {"a", "b", "c"},
        {"d", "e", "f"},
    }
    fmt.Println(anotherMatrix)
}

遍历二维数组可以使用嵌套的for循环:

package main
import "fmt"
func main() {
    anotherMatrix := [2][3]string{
        {"a", "b", "c"},
        {"d", "e", "f"},
    }
    for i := 0; i < len(anotherMatrix); i++ {
        for j := 0; j < len(anotherMatrix[i]); j++ {
            fmt.Printf("%s ", anotherMatrix[i][j])
        }
        fmt.Println()
    }
}

7.切片

(1)切片的定义和基本概念

切片(Slice)是Go语言中一种灵活且强大的数据结构,它基于数组实现,但长度是可变的。切片是对数组的一个连续片段的引用,它本身并不存储数据,而是包含了指向底层数组的指针、切片的长度(len)和容量(cap)。

可以通过以下方式创建切片:

<1>从数组创建切片arr := [5]int{1, 2, 3, 4, 5}; slice := arr[1:3],这里slice是一个切片,它引用了数组arr的索引从1(包含)到3(不包含)的元素,即{2, 3}。切片的长度为23 - 1),容量为45 - 1)。

<2>使用make函数创建切片slice := make([]int, 3, 5),这会创建一个int类型的切片,长度为3,容量为5。其中,第一个参数是切片的类型,第二个参数是长度,第三个参数是容量(容量参数可选,如果省略,容量和长度相同)。

(2) 切片的长度和容量

长度(len:切片的长度表示切片中当前包含的元素个数。例如,对于切片slice := []int{1, 2, 3},其长度为3。可以通过len(slice)函数来获取切片的长度。

容量(cap:切片的容量表示切片底层数组中从切片的第一个元素开始,到数组末尾的元素个数。例如,从一个长度为5的数组arr := [5]int{1, 2, 3, 4, 5}创建切片slice := arr[1:3],其容量为4,因为从切片的第一个元素(索引为1的元素)开始,到数组arr末尾还有4个元素。可以通过cap(slice)函数来获取切片的容量。当切片的长度小于容量时,可以向切片中添加新元素。当切片的长度等于容量时,如果要添加新元素,Go语言会自动重新分配内存,扩大切片的容量。

(3)切片的操作

访问元素:和数组一样,切片可以通过索引来访问元素,索引从0开始。例如,对于切片slice := []int{1, 2, 3}slice[0]表示第一个元素1slice[2]表示第三个元素3

修改元素:通过索引也可以修改切片中的元素。例如,slice := []int{1, 2, 3}; slice[1] = 4,这样就将切片中的第二个元素修改为4

添加元素:使用append函数添加元素。例如,slice := []int{1, 2, 3}; newSlice := append(slice, 4),这里newSlice是一个新的切片,它是在slice的基础上添加了元素4后的结果。如果切片的容量足够,append操作会直接在原切片的底层数组上添加元素;如果容量不足,Go语言会重新分配一个更大的底层数组,并将原切片的元素复制到新数组中,然后再添加新元素。 - 可以一次添加多个元素,如slice := []int{1, 2, 3}; newSlice := append(slice, 4, 5),这会将45添加到slice中。

切片复制:可以使用copy函数在两个切片之间复制元素。例如,src := []int{1, 2, 3}; dst := make([]int, 3); copy(dst, src),这里copy函数会将src切片中的元素复制到dst切片中。如果dst切片的长度小于src切片的长度,那么只会复制dst切片长度范围内的元素。

切片截取:可以通过类似于创建切片的方式对现有切片进行截取。例如,slice := []int{1, 2, 3, 4, 5}; subSlice := slice[1:4],这里subSlice是一个新的切片,它引用了slice中索引从1(包含)到4(不包含)的元素,即{2, 3, 4}

(4)切片作为函数参数

切片作为函数参数是引用传递。这意味着当把切片传递给一个函数时,函数内部对切片的修改会影响到原始切片。例如:

import "fmt"
func modifySlice(slice []int) {
    slice[0] = 100
}
func main() {
    mySlice := []int{1, 2, 3}
    modifySlice(mySlice)
    fmt.Println(mySlice)
}

在上述代码中,modifySlice函数内部修改了切片slice的第一个元素,在main函数中可以看到mySlice也被修改了。这与数组作为参数传递(值传递)形成对比,使得切片在函数间传递和操作更加方便。

(5)切片的零值和空切片

零值:切片的零值是nil,它的长度和容量都是0。例如,var slice []int定义了一个nil切片。可以通过检查切片是否为nil来判断切片是否已经初始化。

空切片:空切片是长度为0的切片,它和nil切片不同。可以通过make函数创建空切片,如slice := make([]int, 0),或者通过切片字面量创建,如slice := []int{}。空切片可以用于表示一个空的集合或者在需要返回切片的函数中返回一个没有元素的切片。

8.map

(1)map的定义和基本概念

在Go语言中,map是一种无序的键 - 值对集合,它是引用类型。map的键必须是可以使用==运算符进行比较的类型,如整数、字符串、指针等,而值可以是任意类型。可以使用make函数来创建一个map,例如:m := make(map[string]int),这就创建了一个键为字符串类型,值为整数类型的map。也可以使用字面量来创建,如:m := map[string]int{"key1": 1, "key2": 2}

(2)map的操作

插入和修改元素: 通过指定键来插入或修改map中的元素。例如,对于上面创建的m,可以使用m["key3"] = 3来插入一个新的键 - 值对,或者修改已存在的键对应的的值。如果键不存在,就会插入一个新的键 - 值对;如果键已经存在,就会更新该键对应的的值。

访问元素: 通过键来访问map中的值。例如,value := m["key1"]会获取键key1对应的的值。如果键不存在,会返回值类型的零值。对于整数类型,零值是0;对于字符串类型,零值是""等。可以使用value, ok := m["key4"]这种方式来检查键是否存在,其中ok是一个布尔值,如果oktrue,表示键存在,value就是对应的的值;如果okfalse,表示键不存在,value是值类型的零值。

删除元素:使用delete函数来删除map中的元素。例如,delete(m, "key2")会删除键为key2的元素。如果键不存在,delete操作不会产生任何错误。

(3)遍历map

可以使用for - range循环来遍历map,但是需要注意的是,map是无序的,每次遍历的顺序可能不同。例如:

for key, value := range m {
    println(key, value)
}

在这个遍历过程中,key是键,value是对应的值。每次循环都会获取一个键 - 值对,直到遍历完所有的键 - 值对。

(4)map的嵌套

Go语言支持map的嵌套。例如,可以有一个map,它的键是字符串,值是另一个map。如:nestedMap := make(map[string]map[string]int),然后可以像下面这样插入和访问元素:

nestedMap["outerKey"] = make(map[string]int)
nestedMap["outerKey"]["innerKey"] = 1

遍历嵌套的map需要使用多层for - range循环。例如,先遍历外层map的键,然后对于每个外层键,遍历内层map的键和值。

9.range

Go 特有的一种的遍历结构。它可以遍历任何一个 集合(字符串、数组、切片、Map、通道等)。 语法上类似主流编程语言中的 foreach 语句,但可以获得每次遍历对应的索引。

(1)语法规则:

for key,val := range collection{
}

(2)遍历规则:

key,val均可省略,使用空白标识符

<1>key,val都保留

for key,val := range collection{
}

<2>省略val,不使用空白标识符

for key := range collection{
}

<3>省略key,key的位置需要空白标识符

for _,val range collection{
}

<4>key,val都省略,只执行循环内的语句

for _,_=range collection{
}

(3)实例:

str := "Dathline"
	for i := range str{
		fmt.Println("key=",i)
	} 
	for _,v := range str{
		fmt.Printf("value=%c\n",v)
	}
	arr := []int{1,4,6,8}
	for i,v := range arr{
		arr[0]=2
		fmt.Println("key=",i,"value=",v)
	}
	for _,_= range arr{
		fmt.Println("猜猜会输出什么呢?")
	}
}

运行结果:

image.png

(4)思考:

为什么在循环中使用arr[0]=2修改了数组的第一个值,但是实际输出中并没有修改呢?

解释:在Go语言中,for range循环在遍历切片或数组时,会创建一个副本,而不是直接使用原始的切片或数组。这意味着在循环内部对切片或数组的修改不会影响原始的切片或数组。

在代码中,for i, v := range arr 循环遍历了切片arr,并在每次迭代时将arr的第一个元素修改为2。但是,由于range创建了一个副本,所以实际上修改的是副本的第一个元素,而不是原始arr的第一个元素。因此,原始的arr并没有被修改。