2020-12-22-GO-Day8-函数102

文章对https://www.cnblogs.com/skymyyang/p/7659775.html 进行引用。膜拜
https://www.cnblogs.com/xbblogs/p/11102970.html 该文章介绍了值传递,引用传递、指针传递的区别。给大佬撒花。
https://blog.csdn.net/weixin_42255190/article/details/97944113 值传递与指针传递的区别,很白话

    //函数的定义:
    //函数分为2部分:func(A) (B){} 其中A是输入的自变量,必须; B是输出,可选
    //A: 当A中的自变量类型相同时,可以写 a,b,c int;不同时, a int, b string, c float
    //B: 写变量的类型:int, string, float

    func A(a,b,c int )(d,e,f int) {}
    func B(a int,b string){}
    func C()(a,b,c int) {}
不定长变参
//由于输入的参数的长度无法计算,那么可以使用不定长变参 a...int表示。并且不定长变参必须是最后一个参数

由于不定长变参的引入,那么实参的引用就由几种可能:

  1. 值传递
  2. 引用传递
  3. 指针传递

《值类型》:
  int、float、bool、array、sturct等

值传递是指在调用函数时将实际参数复制一份传递到函数中,这样在函数中如果对参数进行修改,将不会影响到实际参数

声明一个值类型变量时,编译器会在栈中分配一个空间,空间里存储的就是该变量的值

《感觉这个值类型,就像是Python中的局部变量。因此,在值传递作为函数参数,在被调用函数中引用值传递参数时候,需要重新为值传递参数开辟新的用于存放值传递参数的内存空间。内存调用的是不同地址的,变量名是一样的,值》

《引用类型》:
slice,map,channel,interface,func,string等

声明一个引用类型的变量,编译器会把实例的内存分配在堆上

string和其他语言一样,是引用类型,string的底层实现struct String { byte* str; intgo len; }; 但是因为string不允许修改,每次操作string只能生成新的对象,所以在看起来使用时像值类型。

所谓引用传递是指在调用函数时将实际参数的地址传递到函数中,那么在函数中对参数所进行的修改,将影响到实际参数。

需要注意的是:引用类型在函数的内部可以对它的值进行修改,但是如果给形参重新赋值,重新赋值后的形参再怎么修改都不会影响外面的实参了

《指针类型》:
  一个指针变量指向了一个值的内存地址

当一个指针被定义后没有分配到任何变量时,它的值为 nil。nil 指针也称为空指针

一个指针变量通常缩写为 ptr

其实引用类型可以看作对指针的封装

package main
import ("fmt")

func main()  {
    s1:= []int{1,2,3,4}
    a,b :=1,2
    A(a,b)
    fmt.Println(a,b)
    B(s1)
    fmt.Println(s1)
}

func A(a ...int) {
    //这里传进来的实际上是一个slice,引用类型
    a[0] = 3
    a[1] = 4
    //尽管我们在函数A当中接收到的是一个slice,但是它得到的是一个值拷贝
    //和直接传递一个slice的区别看函数B
    fmt.Println(a)
}
func B(s []int)  {
    //这里并不是传递一个指针进去,而是对这个slice的内存地址进行了一个拷贝
    //这里还可以看到像int型、string型进行常规的参数传进去的话,只是进行了个值拷贝,slice传进去虽然也是拷贝,但是它是内存地址的拷贝
    s[0] = 4
    s[1] = 5
    s[2] = 6
    s[3] = 7
    fmt.Println(s)
    //在这里 我们看到我们在函数B当中的修改,实际上影响到了我们main函数当中的变量s1
    //如果直接传递一个slice,它的修改就会影响到这个slice的本身

}

PS:值类型和引用类型进行函数传参拷贝是不一样的,一个是拷贝值,一个是拷贝地址
    //[3 4]
    //1 2
    //[4 5 6 7]
    //[4 5 6 7]

例子2:
写在最开头:https://studygolang.com/articles/7412

& 是取地址符号 , 即取得某个变量的地址 , 如 ; &a
*是指针运算符 , 可以表示一个变量是指针类型 , 也可以表示一个指针变量所指向的存储单元 , 也就是这个地址所存储的值 .

经常会见到: p . *p , &p 三个符号
p是一个指针变量的名字,表示此指针变量指向的内存地址,如果使用%p来输出的话,它将是一个16进制数。

而*p表示此指针指向的内存地址中存放的内容,一般是一个和指针类型一致的变量或者常量。

而我们知道,&是取地址运算符,&p就是取指针p的地址。

等会,怎么又来了个地址,它到底和p有什么区别?区别在于,指针p同时也是个变量,既然是变量,编译器肯定要为其分配内存地址,就像程序中定义了一个int型的变量i,编译器要为其分配一块内存空间一样。而&p就表示编译器为变量p分配的内存地址,而因为p是一个指针变量,这种特殊的身份注定了它要指向另外一个内存地址,程序员按照程序的需要让它指向一个内存地址,这个它指向的内存地址就用p表示。而且,p指向的地址中的内容就用*p表示。

package main
import "fmt"

func value(ss string) {
    fmt.Println("value():")

    fmt.Println("ss = ", ss) //golang
    fmt.Println("&ss = ", &ss) //重新分配一块不同于Main函数的新的内存地址

    fmt.Println("=====================")

    ss = "Kotlin"  //重新赋值字符串,引用类型

    fmt.Println("ss = ", ss) //Kotlin
    fmt.Println("&ss = ", &ss) //分配的是&ss的内存地址,因此在相同一块内存地址上写数据,原有数据被覆盖

}

func value2(ss string) {
    fmt.Println("value2():")

    fmt.Println("ss = ", ss) //在value中最后的输出是Kotlin,但是没有return ,因此在Value2的输入仍是golang
    fmt.Println("&ss = ", &ss) ////重新分配一块不同于Main,不同于value函数的新的内存地址

    fmt.Println("=====================")

    ss = "Kotlin"

    fmt.Println("ss = ", ss) //在同一个内存地址进行操作,覆盖原有值
    fmt.Println("&ss = ", &ss) //同一个内存
}

func pointer(ps *string) {
    fmt.Println("pointer():")

    fmt.Println("ps = ", ps) //把golang的指针打出来
    fmt.Println("&ps = ", &ps) //对*string的指针取地址
    fmt.Println("*ps = ", *ps) //对*string的指针取值

    fmt.Println("=====================")

    *ps = "Kotlin"

    fmt.Println("ps = ", ps)
    fmt.Println("&ps = ", &ps)
    fmt.Println("*ps = ", *ps)
}

func pointer2(ps *string) {
    fmt.Println("pointer2():")

    fmt.Println("ps = ", ps)
    fmt.Println("&ps = ", &ps)
    fmt.Println("*ps = ", *ps)

    fmt.Println("=====================")

    *ps = "Kotlin"

    fmt.Println("ps = ", ps)
    fmt.Println("&ps = ", &ps)
    fmt.Println("*ps = ", *ps)
}

func main() {
    s := "golang"
    fmt.Println("s = ", s) //golang
    fmt.Println("&s = ", &s) //s的内存地址
    fmt.Println("__________________________________________________")
    value(s) //字符串类型,引用传递
    fmt.Println("__________________________________________________")
    value2(s) //字符串类型,引用传递
    fmt.Println("__________________________________________________")
    pointer(&s)
    fmt.Println("__________________________________________________")
    pointer2(&s) //对地址进行引用

}

输出

s =  golang
&s =  0xc0000881e0
__________________________________________________
value():
ss =  golang
&ss =  0xc000088200
=====================
ss =  Kotlin
&ss =  0xc000088200
__________________________________________________
value2():
ss =  golang
&ss =  0xc000088230
=====================
ss =  Kotlin
&ss =  0xc000088230
__________________________________________________
pointer():
ps =  0xc0000881e0
&ps =  0xc0000cc020
*ps =  golang
=====================
ps =  0xc0000881e0
&ps =  0xc0000cc020
*ps =  Kotlin
__________________________________________________
pointer2():
ps =  0xc0000881e0
&ps =  0xc0000cc028
*ps =  Kotlin
=====================
ps =  0xc0000881e0
&ps =  0xc0000cc028
*ps =  Kotlin

Process finished with exit code 0


例子3:

package main

import "fmt"

func paramFunc(a int, b *int, c []int) {
    a = 100
    *b = 200
    c[1] = 300

    fmt.Println("paramFunc:")
    fmt.Println(a)
    fmt.Println(*b)
    fmt.Println(c)
}

func main() {
    a := 1 //int
    b := 1 //int
    c := []int{1, 2, 3} //slide
    paramFunc(a, &b, c) 
    

    fmt.Println("main:")
    fmt.Println(a) //1
    fmt.Println(b) //
    fmt.Println(c)//
}

结果:

paramFunc:
100
200
[1 300 3] //100(分配的值), &b是b的内存地址, ,*&b 表示b的地址的指针的值,=200,
//在c所在的内存地址上已经覆盖了200 
main:
1
200
[1 300 3] //[1,300,3], 在内存地址上直接覆盖 

Process finished with exit code 0

匿名函数
匿名函数如其名字一样,是一个没有名字的函数,除了没有名字外其他地方与正常函数相同。匿名函数可以直接调用,保存到变量,作为参数或者返回值

这个非常类似于Python中的lambda。

package main

import "fmt"

func main() {
    f:= func() (string){
        return "hello kitty"
    }
    fmt.Println(f())
}

结果:

hello kitty

Process finished with exit code 0
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容

友情链接更多精彩内容