传递参数
按值传参
Go默认使用按值传参,函数接受到传递进来的参数以后,会讲参数值拷贝给声明该参数的变量(也就是形式参数),如果在函数体中对参数值做修改,实际上修改的是形式参数的值,不会影响实际传递进来的参数值(也就是实际参数)
func add(a, b int) int {
a *= 2
b *= 3
return a + b
}
func main() {
x, y := 1, 2
z := add(x, y)
fmt.Printf("add(%d, %d) = %d\n", x, y, z)
}
结果:add(1, 2) = 8
引用传参
如果你想要实现在函数中修改形参值可以同时修改实参值,需要通过引用传参来完成,此时传递给函数的参数是一个指针,而指针代表的是实参的内存地址,修改指针引用的值即修改变量内存地址中存储的值,所以实参的值也会被修改(这种情况下,传递的是变量地址值的拷贝,所以从本质上来说还是按值传参):
func add(a, b *int) int {
*a *= 2
*b *= 3
return *a + *b
}
func main() {
x, y := 1, 2
z := add(&x, &y)
fmt.Printf("add(%d, %d) = %d\n", x, y, z)
}
结果:add(2, 6) = 8
变长参数
基本定义和传值
func myfunc(numbers ...int) { // 在参数类型前加上“...”前缀
for _, number := range numbers {
fmt.Println(number)
}
}
//这种变长参数还支持传递一个 []int 类型的切片
//传递切片时需要在末尾加上 ... 作为标识
//表示对应的参数类型是变长参数
slice := []int{1, 2, 3, 4, 5}
myfunc(slice...)
myfunc(slice[1:3]...)
形如
...type
格式的类型只能作为函数的参数类型存在,并且必须是函数的最后一个参数
基本定义和传值(泛型)
interface{}
是一个空接口
,可以用于表示任意类型(后面我们在 Go 语言面向对象编程接口系列中会详细介绍),但是这个范围太泛了,就像 C 语言中的 void
一样,我们根本不知道真正传递进来的参数到底是什么类型的,这在强类型的静态语言中是不能接受的,所以为了保证代码类型安全,需要在运行时通过反射
对数据类型进行检查,以便让程序在预设的轨道内运行,避免因为类型问题导致程序崩溃。
多返回值
func add(a, b *int) (int, error) {
if *a < 0 || *b < 0 {
err := errors.New("只支持非负整数相加")
return 0, err
}
*a *= 2
*b *= 3
return *a + *b, nil
}
func main() {
x, y := -1, 2
z, err := add(&x, &y)
if err != nil {
fmt.Println(err.Error())
return
}
fmt.Printf("add(%d, %d) = %d\n", x, y, z)
}
命名返回值
在设置多返回值时,可以对返回值进行变量命名,
这样,我们就可以在函数中直接对返回值变量进行赋值,
而不必每次都按照指定的返回值格式返回多个变量了
func add(a, b *int) (c int, err error) {
if *a < 0 || *b < 0 {
err = errors.New("只支持非负整数相加")
return
}
*a *= 2
*b *= 3
c = *a + *b
return
}
这种机制避免了每次进行 return
操作时都要关注函数需要返回哪些返回值,为开发者节省了精力,尤其是在复杂的函数中。