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 时,我们并没有创建一个新的切片,而是创建了一个新的切片头(包含 ptr、len 和 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 大小的数据。