对于GoLang函数的定义或者说理解:
函数是结构化编程中最小的模块单元,日常开发过程中,将复杂的算法过程分解为若干个小任务(代码块),使程序的结构性更清晰,程序可读性提升,易于后期维护和让别人读懂你的代码。
另外为了更好的重用你的代码,可以把重复性的任务抽象成一个函数。
Go语言中使用关键词func来定义一个函数,并且左花括号不能另起一行,比如:
func hello(){ //左花括号不能另起一行 println("hello") }
Go语言中定义和应用函数时,有如下需要注意的点:
函数无须前置声明
不支持命名嵌套定义,支持匿名嵌套
函数只能判断是否为nil,不支持其它比较操作
支持多返回值
支持命名返回值
支持返回局部变量指针
支持匿名函数和闭包
func hello() { //左括号不能另起一行 } func add(x,y int) (sum int){ //命名返回值 sum = x + y return } func vals()(int,int){ //支持多返回值 return 2,3 } func a(){} func b(){} func add(x,y int) (*int){ //支持返回局部变量指针 sum := x + y return &sum } func main(){ println(a==b) //只能判断是否为nil,不支持其它比较操作 func hello() { //不支持命名嵌套定义 println("hello") } }
具备相同签名(参数和返回值)的函数才视为同一类型函数,比如:
func hello() { fmt.Println("hello") } func say(f func()){ f() } func main(){ f := hello say(f) }
参数:
Go语言中给函数传参时需要注意以下几点:
不支持默认参数
不支持命名实参
参数视作为函数的局部变量
必须按签名顺序传递指定类型和数量的实参
相邻的同类型参数可以合并
支持不定长变参,实质上是slice
func test(x,y int, s string, _ bool){ //相邻的同类型参数可以合并 return } func add(x ,y int) int { //参数视作为函数的局部变量 x := 100 //no new variables on left side of := var y int = 200 //y redeclared in this block return x +y } func sum(nums ...int) { //变参函数 total := 0 for _, num := range nums { total += num } fmt.Println(total) } func main(){ //test(1,2,"s") //not enough arguments in call to test test(1,2,"s",false) nums := []int{1, 2, 3} sum(nums...) }
不管传递的是指针、引用还是其它类型参数,都是值拷贝传递的,区别在于拷贝的目标是目标对象还是拷贝指针而已。
在函数调用之前,编译器会为形参和返回值分配内存空间,并将实参拷贝到形参内存。比如:
func test1(x *int){ fmt.Printf("%p, %v\n",&x ,x) } func main(){ a := 0x100 p := &a fmt.Printf("%p, %v\n", &p, p) test1(p) } 输出: 0xc42002c020, 0xc42000a320 0xc42002c030, 0xc42000a320 从结构中看出, 实参和形参指向同一目标,但是传递的指针是被赋值了的
如果函数参数和返回值过多,可以将其封装成一个结构体类型,比如:
type serverOption struct{ addr string port int path string timeout time.Duration } func newOption() * serverOption{ return &serverOption{ addr:"127.0.0.1", port:8080, path:"/var/www", timeout: time.Second * 5, } } func (s *serverOption)server(){ println("run server") } func main(){ s := newOption() s.port = 80 s.server() for{} }
变参:
变参本质上是一个切片(slice),只能接收一到多个同类型参数,且必须放在参数列表尾部,比如:
func add(args ...int) int { total := 0 for _, v := range args { total += v } return total } func main() { fmt.Println(add(1,2,3)) }
变参既然是切片,那是否可以直接传个切片或数组呢?
func test1(s string, a ...int){ fmt.Printf("%T, %v\n", a, a) //[]int, [1 2 3 4] } { a := [4]int{1,2,3,4} test1("s", a) //cannot use a (type [4]int) as type int in argument to test1 test1("s", a[:] //cannot use a[:] (type []int) as type int in argument to test1 test1("s", a[:]...) //切片展开 }
变参既然是切片,那么参数复制的是切片的本身,并不包括底层的数组,因此可以修改原数据,但是可以copy底层数据,防止原数据被修改,比如:
func test1(a ...int){ for i := range a{ a[i] += 100 } } func main(){ a := [4]int{1,2,3,4} // b := make([]int,0) // copy(b,a[:]) // test1(b[:]...) test1(a[:]...) for i := range a{ fmt.Println(a[i]) } }