• Golang入门笔记(7)—— 函数 func


           函数是什么? 函数:为完成某一功能的程序的语句指令的集合。

       

         感觉重头戏逐渐到来了,让我们好好的,认真对待它吧!

            为什么要使用函数? 为了提高代码的复用,减少代码的冗余,提高代码维护性——避免每次都编码一段类似代码。

            基本语法:

    1. func functionName(形参列表...)(返回值的类型列表){
    2. 执行语句...
    3. return + 返回值列表
    4. }

             其中,如果返回值类型是一个,可省略第二个小括弧,代码如下:

    1. package main
    2. import "fmt"
    3. func main() {
    4. sum := calculate(10, 20) //实参。
    5. fmt.Println(sum)
    6. }
    7. func calculate(num1 int, num2 int) int { //形参
    8. sum := 0
    9. sum += num1
    10. sum += num2
    11. return sum
    12. }

            注意事项:       

    •          函数名:

    1.遵循标识符命名规范——驼峰命名法,要能见名知意。

    2.首字符不能是数字。

    3.如果首字母大写该函数可以被本包文件和其他包文件使用;如果首字母小写只能被本包文件使用,其他包不能使用。

    4. Go语言中,函数不支持重载。所以同一block中,函数名不可重复声明,否则会 报 redecleared in this block 的错误。

    •         形参列表:

    1. 用于接收外部传入的参数,其参数的个数,可以0个,也可以是n个。

    2.支持可变参数,即函数参数的数量可根据实际情况弹性伸缩 。(ps 后面等熟悉了函数后,末尾会专门介绍)

    •         返回值列表:

    返回值类型列表:

            1.可以返回0个数据类型,可以返回n个数据类型。

    返回0个数据类型

    1. func main() {
    2. calculate(10, 20)
    3. }
    4. //函数定义中无返回值类型列表
    5. func calculate(num1 int, num2 int) {
    6. sum := 0
    7. sum += num1
    8. sum += num2
    9. fmt.Println(sum)
    10. }

    返回n个数据类型

    1. package main
    2. func main() {
    3. //需要2个变量接受此函数返回的多个类型的数据
    4. r1, r2 := calculate(10, 20)
    5. println(r1, r2)
    6. }
    7. //返回多个数据类型的函数
    8. func calculate(num1 int, num2 int) (int, int) {
    9. sum := 0
    10. sum += num1
    11. sum += num2
    12. var result int = num1 - num2
    13. return sum, result
    14. }

            2. 对于不关注的返回,可以使用_忽略。比如:

    1. //可以用 下划线 ,忽略不关注的返回值。
    2. r1, _ := calculate(10, 20)
    3. println(r1)

    函数执行内存分析:

            先看一段代码:思考执行结果为什么会这样?

    1. package main
    2. import "fmt"
    3. func main() {
    4. num1 := 10
    5. num2 := 20
    6. exchange(num1, num2)
    7. fmt.Printf("num1 = %v , num2 = %v", num1, num2) //num1 = 10,num2 = 20
    8. }
    9. func exchange(num1 int, num2 int) {
    10. var t int
    11. t = num1
    12. num1 = num2
    13. num2 = t
    14. }

            知识预热:

            栈区:用来存储程序执行过程中函数内部定义的信息和局部变量值。 最内层函数后进先出,最内层函数先执行后,释放内存,向上层传递结果。 函数return返回值将函数执行的结果保存下来,返回给调用者。

            上面这个函数,在内存中的函数执行过程,如下图所示:

    当exchange执行完毕之后,出栈 销毁函数栈帧(栈空间)——上面exchage栈空间就会出栈而消失

                       以上函数的内存活动的主要发生在逻辑栈上,根据如上图的内存分析就可得出问题所在。

    可变参数案例:

            相信在这里,大家已经大致了解了函数了,这里 详细看一下 可变参数的案例。

    1. package main
    2. func main() {
    3. res := calculate("*", 1, 2, 3, 4)
    4. println(res)
    5. }
    6. func calculate(op string, num ...int) int {
    7. res := 1
    8. if "*" == op {
    9. for i := 0; i < len(num); i++ {
    10. res *= num[i]
    11. }
    12. }
    13. return res
    14. }

           注意:可变参数,要是必须是最后一个参数,这是一个通用的语言语法规范,js-ecmascript6版本 和 java 也都是如此。

    •         函数的参数的传递方式:

            基本数据类型和数组默认都是值传递,即进行值得拷贝,在函数内修改,不会影响到原来的值。

    1. package main
    2. import "fmt"
    3. func main() {
    4. num := 1
    5. changeValue(num)
    6. println(num)
    7. }
    8. func changeValue(num int) {
    9. num = 1000
    10. fmt.Println(" changeValuetest num --> ", num)
    11. }

           此种情况为什么会出现这样的结果,大概的原理和上面的内存分析模型几乎差不多的。       

             对于以值传递方式的数据类型,如果希望在函数内的变量能修改函数外的变量,可以传入变量的地址&,函数内以指针的方式操作变量。从效果上看类似 引用传递。

    1. package main
    2. import "fmt"
    3. func main() {
    4. num := 1
    5. println("num地址", &num)
    6. changeValue(&num) //调用函数的时候,传入num的地址。
    7. println(num)
    8. }
    9. func changeValue(num *int) { // 定义了一个int类型的指针
    10. //用*访问到这个地址的内存。
    11. *num = 1000
    12. fmt.Println(" changeValuetest num --> ", num)
    13. }

            Go语言中的指针操作非常简单,只需要记住两个符号:&(取地址)*(根据地址取值)每个变量在运行时都拥有一个地址,这个地址代表变量在内存中的位置。区别于C/C++中的指针,Go语言中的指针不能进行 偏移和运算,是安全指针。指针类型的空值是 nil -----> 引用类型的空值都是nil类型。

    1. var x int = 10
    2. var ptr *int = &x
    3. *ptr = 21
    4. println(x)

            关于更多指针的知识,详见:Golang指针解释,一劳永逸_哔哩哔哩_bilibili

    以上代码的内存分析图:

    传入 *int变量地址 的方式

    Go函数也是一种数据类型
            这点和javascript一样,可以赋值给一个变量,则该变量就是一个函数类型的变量,通过该变量可以调用该函数。正式由于函数是一个数据类型的特性,在Go的世界中,函数可以作为形参传递并且调用。这不禁让我想到Javascript 的cb()函数的使用,以及 java 通过抽象一个接口,做一个匿名内部类等方式有得一拼。

    1. package main
    2. import "fmt"
    3. func main() {
    4. a := say
    5. fmt.Printf("a 得类型是 %T,say 函数得类型是 %T \n", a, say)
    6. a("your")
    7. say("my")
    8. }
    9. func say(whose string) {
    10. println("this is " + whose + "callback, just test this feature. ")
    11. }

            根据这种特性,编写回调函数。

    1. package main
    2. func main() {
    3. a := calculate
    4. var res int = test(a, 1, 2, 3, 4, 5)
    5. println(res)
    6. }
    7. func calculate(op string, num ...int) int {
    8. res := 1
    9. if "*" == op {
    10. for i := 0; i < len(num); i++ {
    11. res *= num[i]
    12. }
    13. }
    14. return res
    15. }
    16. func test(cb func(string, ...int) int, nums ...int) int {
    17. return cb("*", nums...) // 根据javascript 的 rest 的语法,瞎试的竟然也成了。
    18. }

            编写以上代码的心得体会:

    * func 函数当参数传递的时候,细节点,要传入定义好的参数类型和返回值类型。 

    * 当遇到可变参数的时候,如这里的 num...  , 可变参数的代表变量要和三个点 在一起打包传递,这样来声明这个是一组可变的参数值。

    自定义数据类型(给数据类型别名):

    1. package main
    2. import "fmt"
    3. func main() {
    4. //自定义数据类型,给类型起了别名。
    5. type myInt int
    6. var num1 myInt = 30
    7. fmt.Println("num1", num1)
    8. var a int = 20
    9. //a = num1 // cannot use num1 (variable of type myInt) as type int in assignment
    10. a = int(num1) // 别名通过 int() 进行强转为原来的类型。
    11. println(a)
    12. }

    自定义函数类型

    1. // 优化前
    2. func test(cb func(string, ...int) int, nums ...int) int {
    3. return cb("*", nums...) // 根据javascript 的 rest 的语法,瞎试的竟然也成了。
    4. }
    5. //优化后,使用 自定义的函数类型,使得代码更加简洁
    6. type operateFunction func(string, ...int) int
    7. func test(cb operateFunction, nums ...int) int {
    8. return cb("*", nums...) // 根据javascript 的 rest 的语法,瞎试的竟然也成了。
    9. }

    支持函数返回值的命名操作:

    1. package main
    2. func main() {
    3. }
    4. // 返回值的列表顺序必须要和返回值的类型,一一对于,不可弄乱。
    5. func test01(num1 int, num2 int) (int, int) {
    6. sum := num1 + num2
    7. sub := num1 - num2
    8. return sum, sub
    9. }
    10. // 通过返回列表声明的变量 字符 ,可以进行映射。可以不用管顺序,减少编码错误。
    11. func test02(num1 int, num2 int) (sub int, sum int) {
    12. sum = num1 + num2
    13. sub = num1 - num2
    14. return
    15. }

            细节好多,多多练习。

  • 相关阅读:
    什么台灯最好学生晚上用?专业的学生台灯分享
    130-Vue中的监听事件——Watch
    ?SW转cad尺寸不一致
    基于Vertx实现可配置及可扩展的IOT服务
    ASEMI光伏二极管GMK4045,GMK4045特征,GMK4045应用
    阻止移动端 touchmove 与 scroll 事件冲突
    Mysql高级——锁(1)
    【Python零基础入门笔记 | 13】面对海量数据,如何优雅地加载数据?请看迭代器与生成器
    消失的数字C/C++
    爬动漫“上瘾”之后,放弃午休,迫不及待的用Python薅了腾Xun动漫的数据,啧啧啧
  • 原文地址:https://blog.csdn.net/wdw18668109050/article/details/127897094