Go 注意事项
Go 用首字母大小写区分可见性
一个包中,结构体可以以小写字母开头,虽然在其他语言中比较奇怪
函数参数
- Go 语言中的函数传参都是值拷贝,当要修改(访问)某个变量的时候,可以创建一个指向该变量地址的指针变量
- Go 语言中的指针操作非常简单,只需要记住两个符号:
&(取地址)和*(根据地址取值)
函数内修改非指针变量
你提供的代码示例展示了值传递和指针传递在 Go 语言中的不同效果。以下是代码的详细解释:
package main
import (
"fmt"
)
func modify1(x int) {
x = 100
fmt.Println("这里的值", x)
}
func modify2(x *int) {
*x = 100
}
func main() {
a := 10
modify1(a)
fmt.Println(a) // 10
modify2(&a)
fmt.Println(a) // 100
}
代码解释
-
modify1函数:- 接受一个
int类型的参数x。 - 在函数内部,将
x的值修改为100。 - 打印函数内部的
x值。
- 接受一个
-
modify2函数:- 接受一个
*int类型的参数x,即一个指向int的指针。 - 在函数内部,通过解引用指针
*x将指针所指向的值修改为100。
- 接受一个
-
main函数:- 声明一个
int类型的变量a,并赋值为10。 - 调用
modify1(a),传递a的值。 - 打印
a的值,结果为10,因为modify1中的修改只在函数内部生效,不会影响函数外部的a。 - 调用
modify2(&a),传递a的指针。 - 打印
a的值,结果为100,因为modify2中的修改通过指针影响了函数外部的a。
- 声明一个
内存布局和传递方式
- 值传递:在
modify1中,参数x是a的一个副本。修改x不会影响到a。 - 指针传递:在
modify2中,参数x是a的地址。通过解引用指针*x可以直接修改a的值。
示例输出
这里的值 100
10
100
解释:
modify1内部打印的值是100,但这只是x的副本,外部的a没有改变。modify1调用后,打印a的值仍然是10。modify2调用后,通过指针修改了a的值,打印a的值变为100。
总结
- 在 Go 语言中,函数参数默认是值传递。这意味着传递给函数的是参数的副本,函数内部的修改不会影响到外部的变量。
- 如果希望函数能修改外部变量,可以使用指针传递。在函数内部通过解引用指针,可以直接修改外部变量的值。
go 语言这么设计的目的:通过理解值传递和指针传递的不同,可以更好地控制函数对变量的修改行为,从而编写出更加高效和正确的代码。
为什么修改值需要解引用
- 指针:存储变量的内存地址。
- 解引用:通过指针访问或修改其所指向的变量的值。(这里要理解成:访问,而不要理解成:取值)
- 为什么需要解引用:因为指针本身只包含地址,要访问或修改地址处的值,必须通过解引用操作。
指针需要分配内存才能够存储数据
在 Go 语言中:
- 值类型,自动分配内存
- 引用类型,手动分配内存, new 和 make
new 与 make 的区别
- 二者都是用来做内存分配的。
- make 只用于 slice、map 以及 channel 的初始化,返回的还是这三个引用类型本身;
- 而 new 用于类型的内存分配,并且内存对应的值为类型零值,返回的是指向类型的指针。
字符串数组 与 字符串切片
在 Go 语言中,[]string 表示一个字符串切片(slice)。切片是 Go 语言中的一种数据结构,它提供了对数组 的动态大小的灵活支持。切片和数组之间的关键区别在于,数组的大小是固定的,而切片是动态的,可以根据需要增长或缩小。
切片和数组的区别
-
数组:
- 数组的大小是固定的,在声明时就必须指定。
- 例如,
[3]string表示一个固定大小为 3 的字符串数组。
-
切片:
- 切片是基于数组的一个动态视图,可以动态增长和缩小。
- 例如,
[]string表示一个字符串切片,其大小是动态的。
示例代码中的解释
在你的代码中,sliceMap 是一个 map,其键是字符串,值是字符串切片。具体代码如下:
package main
import "fmt"
func main() {
var sliceMap = make(map[string][]string, 3)
fmt.Println(sliceMap)
fmt.Println("after init")
key := "中国"
value, ok := sliceMap[key]
if !ok {
value = make([]string, 0, 2)
}
value = append(value, "北京", "上海")
sliceMap[key] = value
fmt.Println(sliceMap)
}
代码解释
-
初始化
sliceMap:var sliceMap = make(map[string][]string, 3)sliceMap是一个 map,其键是字符串,值是字符串切片。make(map[string][]string, 3)创建了一个初始容量为 3 的 map。
-
检查键是否存在:
key := "中国"
value, ok := sliceMap[key]
if !ok {
value = make([]string, 0, 2)
}key是字符串"中国"。value, ok := sliceMap[key]检查sliceMap中是否存在键"中国",如果不存在,ok为false。- 如果键不存在,创建一个初始容量为 2 的字符串切片
value。
-
修改切片并更新 map:
value = append(value, "北京", "上海")
sliceMap[key] = value- 使用
append函数将"北京"和"上海"添加到value切片中。 - 更新
sliceMap中键"中国"对应的值为新的value切片。
- 使用
输出
-
初始化后的
sliceMap是空的:map[] -
添加键值对后的
sliceMap:map[中国:[北京 上海]]
总结
在你的代码中,sliceMap 的值是字符串切片([]string),而不是数组。这是因为切片在 Go 语言中提供了更灵活和动态的数组操作方式。切片可以根据需要动态增长或缩小,而数组大小是固定的。
因此,虽然你在代码中使用了字符串数组的概念,但实际上你使用的是字符串切片,这是 Go 语言中的一种动态数组结构。