对于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])
}
}