青训营X豆包MarsCode技术训练营笔记

84 阅读4分钟

golang学习笔记

1.go中的基本类型

在go中,定义类型的语法为 var 变量名 类型 (= 值)

var x int//整形
var y string//字符串
var array [10]int //数组
var silce []int//切片
var z *int//指针
var m map[string]int//map
var func(a int) int//函数对象
var st struct{
    var x int = 1
}//结构体

可以看到,在go中,定义基本类型的语法为var 变量名 类型 (= 值),要注意先写变量名,再写具体类型。其中大部分类型都与c++差不多,silce类似c++中的vector数组。

2.一个简单的go程序

package main
import "fmt"

func main(){
    //整形
    var v1 int
    //字符串  
    var v2 string 
    //变量赋值
    v1 = 10
    v2 := "hello"
    fmt.Println("v2:",v2)
    //变量初始化
    v11  := "day02"
    fmt.Println("v11:",v11)
    //第二种初始化方式
    var v12 int = 13
}

package main为包名,其他文件引入本文件函数时也需要引入main包

import "fmt"类似于引入头文件,本程序中需要使用fmt中的println函数

如果在函数内部声明一个变量,并且立即初始化它,可以省略 var 关键字,使用 := 操作符。 这种情况下,变量的类型会根据初始化的值自动推断。例如:v11 := "day02"

运行本程序可以在程序所在的目录下使用cmd命令 go run <文件名>,或者使用vscode等编辑器,安装相应插件运行,使用这种方式时需要注意每一个项目都需要在命令行执行 go mod init <mod文件名>,也可以只在根目录下创建 一个mod,再创建多个子目录,每个子目录中都可以有一个 main 包,这样就不用每次都创建新mod

myproject/ 
├── go.mod 
├── main1/
│    └── main.go 
└── main2/ 
      └── main.go

3.遍历

go中提供for循环遍历操作,很通用,可以遍历各种类型

//遍历数组
for i := 0; i < len(array); i++ { 
    fmt.Println(array[i])
}
//遍历map
for key, value := range mapInstance {
    fmt.Println(key, value)
}

//遍历字符串
hellos := "Hello,我是中国人"
   //字符串遍历,utf-8编码遍历,每个字符byte类型,8字节
   for i:=0; i < len(hellos); i++{
       fmt.Printf("%c", hellos[i])
  }
  //unicode 遍历,每个字符rune类型,变长
  for i, ch := range hellos{
      fmt.Printf("%c\t",ch)
      fmt.Printf("%d\n",i)
  }
//遍历文件
file, err := os.Open("filename.txt") 
if err != nil { 
    log.Fatal(err) 
} 
defer file.Close() 
scanner := bufio.NewScanner(file) 
for scanner.Scan() { 
    fmt.Println(scanner.Text()) 
}

这里要提一下字符串的遍历。在 Go 中,字符串是以 UTF-8 编码的,这意味着每个字符可能由一个或多个字节组成。

当使用 len(str) 来获取字符串的长度时,我们得到的是字节的个数,而不是字符的个数。因此,如果按照字节索引来遍历字符串,并且字符串中包含多字节的字符(如汉字),就会出现乱码。

使用 range 关键字遍历字符串时,Go 会自动处理 UTF-8 编码,返回每个完整的 rune(Go 中的字符类型),而不是字节。这样,即使字符串中包含多字节的字符,也能正确地遍历它们,而不会出现乱码。

4.切片

生成切片有三种方式:

1 通过数组或者切片截取生成新的切片

2 通过make生成 如slice := make([]int, 5)

3 直接初始化 如slice := []int{1, 2, 3, 4, 5}

往切片中添加数据可以采用append方式添加切片数据:
mkslice4 = append(mkslice4, 1, 2, 3)
第一个参数为要添加数据的slice,后边是要加的多个元素。

这个方法同样可以用来删除数据:
slice = append(mkslice4[:3], mkslice4[4:]...)
即截取n-1之前的序列和n之后的序列进行拼接

copy函数提供了切片的深层复制,而赋值操作(=)只是浅拷贝。 对于赋值操作:

oldslice := []int{1, 2, 3, 4, 5}
newslice := oldslice

fmt.Println("newslice is :", newslice)//[1,2,3,4,5]
fmt.Printf("oldslice addr is : %p \n", &oldslice)//0xc00011a018
fmt.Printf("newslice addr is : %p \n", &newslice)//0xc00011a030

oldslice[0] = 100
fmt.Println("newslice is :", newslice)//[100,2,3,4,5]

可以发现虽然两个变量的地址不同,但是oldslice的值改变会影响到newslice,这是因为Slice内部其实存放了一个指针ptr,当执行 newslice := oldslice 时,我们并没有创建一个新的切片,而是创建了一个新的切片头(包含 ptrlen 和 cap),这个新的切片头指向与 oldslice相同的底层数组,所以oldslice的改变会影响到newslice

所以我们需要copy来进行深拷贝

slice1 := []int{1, 2, 3, 4, 5}
slice2 := []int{5, 4, 3}
copy(slice2, slice1)//slice2:  [1 2 3]

这样就会创建出两个不同的数组,一个的改变不会影响到另一个,slice2 大小< slice1时,只拷贝 slice2 大小的数据。