1,go对参数的处理偏向保守,不支持有默认值可选参数,不支持命名实参。调用时,必须按签名顺序传递指定类型和数量的实参,就算以"_"命名的参数也不能忽略。在参数列表中,相邻通类型的参数可以合并
package main
func test(x,y int ,s string, _ bool) *int {
return nil
}
func main(){
test(1,2,"abc")
}
2,参数可视作函数局部变量,因此不能在相同层次定义同名变量
package main
func add(x,y int){
x:=100 // error no new variable on left side of :=
var y int // error y declare in this block
return x + y
}
3,不管是指针,引用类型,还是其他类型参数,都是值拷贝传递。区别无非就是拷贝目标对象,还是拷贝指针而已。在函数调用前,会为形参和返回值分配内存空间,并将实参拷贝到形参内存
package main
import "fmt"
func test(x *int){
fmt.Printf("pointer:%p,target : %v \n ",&x,x)
}
func main(){
a:=0x100
p:=&a
fmt.Printf("pointer :%p target:%v\n",&p,p)
test(p)
}
输出:
pointer :0xc42000c020 target:0xc420012088
pointer:0xc42000c030,target : 0xc420012088
从输出结果看出,尽管实参和形参都指向同一目标,但传递指针时依然被复制
4,特别说明:表面上看,指针参数的性能会更好,但是要具体问题具体分析。被复制的指针会延长目标对象的生命周期,还可能会导致它分配到堆上,其性能消耗就得加上对内存分配和垃圾回收的成本.
package main
import "fmt"
func test(p *int ){
go func(){
fmt.Println(p)
}()
}
func main(){
x:=100
p:= &x
test(p)
}
//查看结果:
./test.go:13:6: &x escapes to heap
./test.go:12:5: moved to heap: x
5,如果函数参数过多,建议将其重构为一个复合结构类型。
package main
import (
"time"
"log"
)
type serverOption struct {
address string
port int
path string
timeout time.Duration
log *log.Logger
}
func newOption() *serverOption {
return &serverOption {
address:"0.0.0.0",
port : 8080,
path : "/var/test",
timeout:time.Second*5,
log:nil,
}
}
func server(option *serverOption){}
func main(){
opt := newOption()
opt.port = 8086 //命名参数设置
server(opt)
}
6,变参:变参本质上是一个切片,只能接受一到多个同类型参数,切必须放到列表尾部;将切片作为参数时,需进行展开操作。如果是数据,先将其切换为切片
package main
import "fmt"
func test(s string,a... int){
fmt.Printf("%T,%v\n",a,a)
}
func test2(a... int){
fmt.Println(a)
}
func main(){
func main(){
test("abc",1,2,3,4)
s:=[]int{1,2,3}
test("a",s...) //必须进行展开操作,否则报错
a:=[3]int{10,20,30}
test2(a[:]...) //转换为slice后展开
}
7,返回值类型: