简单将GO中参数传递分为三类
数字、字符、字符串等类型
结构体
方法
GO的方法本身就是地址的入口,打印一个方法输出的是这个方法的地址
func test_func(){ //0x488a30 fmt.Printf("func address: %v \n",test_func) //下面这行报错: cannot take the address of test_func fmt.Printf("func address: %v \n",&test_func)}
并且在GO语言中没法再对一个方法取地址&,之所以强调GO的方法,是因为在C语言中可以对方法取地址,但取出来的地址还是方法的入口地址,与方法本身的地址一致,这是语言设计的不同
除了方法,剩下的变量,包括结构体、数据、字符、字符串等变量,注意是变量,都可以取地址,可以作为参数传递给方法,其方式有两种
值传递与地址传递
结构体
结构体与其他基本类型又有所不同,结构体的参数,不管是地址传递还是值传递,在使用上没有区别
type Event struct{ ID int Title string Cmd string Res string}func InitEvent(e Event){ e.ID = 1 e.Title = "mysql实例崩溃后,第一次尝试重启" e.Cmd = "/etc/init.d/mysql restart" e.Res = "success"}func InitEvent2(e *Event){ e.ID = 1 e.Title = "mysql实例崩溃后,第一次尝试重启" e.Cmd = "/etc/init.d/mysql restart" e.Res = "success"}
比如InitEvent方法的参数类型一个是值传递,一个是地址传递,但在使用时,都是对象.属性,用法是一致,这一点与其他的一些语言不同;
GO中对结构体取地址的用法,非常非常地普遍,并且GO建议这么做(因为性能高),若无特殊场景需要,结构的传递基本全是以地址的方式进行的,比如你在看一些GO程序的源码时,凡是看到带*的变量,可以全部认为,这就是一个结构体。
GO语言说自己的值传递是一大特色,这个……这个比如在Java中,复合类型默认就是地址传递,方法中对一个对象属性的修改后,方法外对象属性值永久性变化;
在GO中如果使用值传递的方式,在方法修改结构体这种复合类型,方法外结构体属性不会变化;从这一点讲,GO并不一个完全面向对象的语言,它是面向过程与面向对象的结合。
基本类型
基本类型值传递和地址传递在使用上要有所区别
func test_var(j *int){ var i int = 1 //0xc000012068 fmt.Printf("i address: %v \n",unsafe.Pointer(&i)) //0xc000074010 fmt.Printf("j address: %v \n",j) //9 fmt.Printf("j address: %v \n",*j)}
基本类型,地址传递输出的就是16进制的地址,而不是变量值本身,取其值要使用*符号,*j = 1,表示将1写入j变量所在的地址;
这一点,几乎所有的语言处理方法是一致的,需要注意的是GO的字符串
在GO中字符串可以归到甚至类型这一类型中,GO中对字符中处理都是将字符串本身作为参数传递到一个方法中进行的,比如,
fmt.Println(strings.Contains("seafood", "foo")) //true
strInt64 := strconv.FormatInt(id64, 10)这一点与C语言是一致的,而在面向对象的语言(比如Java),字符串本身就可以包含常用的字符处理方法,比如"123".equals("123")
package mainimport ( "fmt" "unsafe")//unsafe.Pointer() 函数来查看一个变量的内存地址func test_var(j *int){ var i int = 1 //0xc000012068 fmt.Printf("i address: %v \n",unsafe.Pointer(&i)) //0xc000074010 fmt.Printf("j address: %v \n",j) //9 fmt.Printf("j address: %v \n",*j)}func test_string(s *string){ fmt.Printf("s address: %v \n",*s) //abc fmt.Printf("s address: %v \n",s) // fmt.Printf("s address: %v \n",unsafe.Pointer(&s))}type Event struct{ ID int Title string Cmd string Res string}func InitEvent(e Event){ e.ID = 1 e.Title = "mysql实例崩溃后,第一次尝试重启" e.Cmd = "/etc/init.d/mysql restart" e.Res = "success"}func InitEvent2(e *Event){ e.ID = 1 e.Title = "mysql实例崩溃后,第一次尝试重启" e.Cmd = "/etc/init.d/mysql restart" e.Res = "success"}func test_struct(){ e1 := Event{ID:0} InitEvent(e1) fmt.Printf("e1 address: %v \n",e1) InitEvent2(&e1) fmt.Printf("e1 address: %v \n",e1)}func test_func(){ //0x488a30 fmt.Printf("func address: %v \n",test_func) //下面这行报错: cannot take the address of test_func //fmt.Printf("func address: %v \n",&test_func)}func main(){ //var j = 9 //test_var(&j) ss := "abc" test_string(&ss) //test_struct() //test_func()}
函数作为参数传递
GO方法也可以作为参数传递,但就没有值传递与地址传递这一说法了
package mainimport ( "fmt")//声明了一个函数类型type testInt func(int) boolfunc isOdd(integer int) bool { if integer%2 == 0 { return false } return true}func isEven(integer int) bool { if integer%2 == 0 { return true } return false}//声明的函数在这个地方当做了一个参数func filter(slice []int, f testInt) []int { var result []int for _, value := range slice { if f(value) { result = append(result, value) } } return result}func main() { slice := []int{ 1, 2, 3, 4, 5, 7} fmt.Println("slice = ", slice) //将函数当做值来传递 odd := filter(slice, isOdd) fmt.Println("奇数是:", odd) //将函数当做值来传递 even := filter(slice, isEven) fmt.Println("偶数是:", even)}
数组与切片
数组与切片参数传递相当于地址传递
package mainimport ( "fmt")func t1(){ var aa [3]int for i:=0;i<3;i++ { aa[i] = i+10 } fmt.Println(aa) ar := t2(aa) //[10 9 12] t2(ar)}func t2(ar [3]int) [3]int{ fmt.Println(ar) ar[1]=9 return ar}func s1(){ var ss []int for i:=0;i<3;i++ { ss = append(ss,i) } fmt.Println(ss) var s2 []int = make([]int,3,5) copy(s2,ss) //len=3 cap=5 slice=[0 1 2] x := printSlice(s2) //len=3 cap=5 slice=[0 10 2] printSlice(x)}func printSlice(x []int) []int{ fmt.Printf("len=%d cap=%d slice=%v\n",len(x),cap(x),x) x[1] = 10 return x}func main(){ t1() //s1()}