【golang学习之旅】复杂数据类型——指针 & 函数

06-29 1167阅读

系列文章

【golang学习之旅】使用VScode安装配置Go开发环境

【golang学习之旅】报错:a declared but not used

【golang学习之旅】Go 的基本数据类型

【golang学习之旅】深入理解字符串string数据类型

【golang学习之旅】go mod tidy

【golang学习之旅】记录一次 panic case : reflect: reflect.Value.SetInt using unaddressable value

【golang学习之旅】记录一次 error case : full error output: cc1: error: unrecognized command line option

【golang学习之旅】Go程序快速开始 & Go程序开发的基本注意事项

【golang学习之旅】Go语言常用转义字符

【golang学习之旅】Go中的变量(1)

【golang学习之旅】Go中的变量——基本数据类型(1)

【golang学习之旅】Go中的变量——基本数据类型(2)


  • 系列文章
  • 指针
  • 函数
    • 基本语法
    • 函数注意事项和细节讨论
    • init()函数
    • 匿名函数
    • 闭包

      指针

      基本数据类型,变量存的就是值,所以也叫值类型。

      指针类型,指针变量存的是一个地址,这个地址所指向的空间存的才是值

      package main
      import "fmt"
      func main() {
      	var a int = 10
      	fmt.Println("a 的地址:", &a)
      	// ptr是一个指针变量,它的类型是*int,它指向int类型的变量a
      	var ptr *int = &a
      	fmt.Printf("ptr的值: %v, ptr 的地址: %v\n", ptr, &ptr)
      	fmt.Println("ptr指向的值:", *ptr)
      	// 修改ptr指向的值
      	*ptr = 20
      	fmt.Println("修改后a的值:", a)
      }
      输出:
      a 的地址: 0xc00000a0b8
      ptr的值: 0xc00000a0b8, ptr 的地址: 0xc00005a028
      ptr指向的值: 10
      修改后a的值: 20
      
      • 获取变量的地址使用&符号

      • 获取指针类型所指向的值,使用*

      • 可以通过指针修改所指向变量的值

        练习:

        【golang学习之旅】复杂数据类型——指针 & 函数

        值类型有:基本数据类型(int系列,float系列,bool,string)、复杂数据类型中的复合数据类型(数组和结构体)

        引用类型有:非值类型就是引用类型


        函数

        基本语法

        func 函数名(形参列表) (返回值列表){
            执行语句...
        }
        
        • 函数声明包含一个函数名,参数列表, 返回值列表和函数体。如果函数没有返回值,则返回列表可以省略
        • 函数可以没有参数或接受多个参数
        • 函数可以返回任意数量的返回值。有返回值的函数,必须有明确的终止语句,否则会引发编译错误

          【golang学习之旅】复杂数据类型——指针 & 函数

          Go中的每一个文件都属于一个包,也就是说Go是以包的形式来管理文件和项目目录结构的。

          包的作用:

          • 区分相同名字的函数、变量等标识符。不同包的文件中允许重名的标识符
          • 当程序文件很多时,可以很好的管理项目。比如module包、dao包、service包等等
          • 控制函数、变量等访问范围

            包的注意事项:

            1. 文件的包名通常和文件所在的文件夹名一致,一般为小写字母。

              【golang学习之旅】复杂数据类型——指针 & 函数

            2. 当一个文件要使用其他包的函数或变量时,需先引入对应的包

              • 引入方式1:import “包名/包路径”

              • 引入方式2:

                import (
                    "包名"
                    "包名"
                )
                

                【golang学习之旅】复杂数据类型——指针 & 函数

              • package指令在文件第一行,然后是import指令。接下来才是变量、函数声明等等

              • 在访问其他包函数、变量时,其语法是包名.函数名

                【golang学习之旅】复杂数据类型——指针 & 函数

              • 如果包名较长,Go支持给包取别名。取别名后,原来的包名就不能使用了

                【golang学习之旅】复杂数据类型——指针 & 函数

              • 在同一个包下,不能有相同的函数名,否则报重复定义

            函数注意事项和细节讨论

            1. 函数的形参列表可以是多个,返回值也可以是多个

            2. 函数的命名遵循标识符命名规范,首字母不能是数字。首字母是大写则该函数可以被其他包访问到,如果是小写,只能被本包文件使用

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

            package main
            import (
            	"fmt"
            )
            func test(name string) {
            	name = "test"
            	fmt.Println("test() ", name)
            }
            func main() {
            	name := "main"
            	test(name)
            	fmt.Println("main() ", name)
            }
            输出:
            test()  test
            main()  main
            
            1. 如果希望函数内的变量能够修改函数外的变量(指的是以值传递的方式的基本数据类型),可以传入变量的地址,函数内以指针的方式操作变量(从效果上类似引用数据类型的使用)

              package main
              import (
              	"fmt"
              )
              func test(name *string) {
              	*name = "test"
              	fmt.Println("test() ", *name)
              }
              func main() {
              	name := "main"
              	test(&name)
              	fmt.Println("main() ", name)
              }
              输出:
              test()  test
              main()  test
              
            2. 在Go中,函数本身也是一种数据类型,可以赋值给一个变量,通过该变量可以实现对函数的调用

              package main
              import (
              	"fmt"
              )
              func getSum(n1, n2 int) int {
              	return n1 + n2
              }
              func main() {
              	a := getSum
              	fmt.Printf("a 的类型 %T\n", a)
              	res := a(1, 2) // 等价于 res := getSum(1, 2)
              	fmt.Printf("res 的值 %d\n", res)
              }
              输出:
              a 的类型 func(int, int) int
              res 的值 3
              
            3. 为了简化数据定义,Go支持自定义数据类型。相当于一个别名

              比如:type myInt int //这时myInt就等价于int来使用了

            4. Go函数支持对返回值命名

              【golang学习之旅】复杂数据类型——指针 & 函数

            5. 使用 _ 标识符时表示忽略该值,可以用作占位符

            6. Go支持可变参数。这里的可变参数实际上是slice切片,通过 args[index]的方式来访问各个值

              package main
              import (
              	"fmt"
              )
              func getSum(num ...int) int {
              	sum := 0
              	for i := 0; i  

            init()函数

            每一个源文件都可以包含一个init()函数,该函数会在main函数执行前,被Go运行框架调用。

            package main
            import (
            	"fmt"
            )
            func init() {
            	fmt.Println("init()...")
            }
            func main() {
            	fmt.Println("main()...")
            }
            输出:
            init()...
            main()...
            
            1. 如果一个文件同时包含全局变量定义、init函数和main函数,那么执行的顺序是 : 全局变量定义->init函数->main函数
            2. init()函数最主要的作用是完成一些初始化的工作

            匿名函数

            使用方式:

            1. 在定义匿名函数时就直接调用
            2. 将匿名函数赋给一个变量(函数变量),再通过该变量来调用匿名函数
            package main
            import (
            	"fmt"
            )
            func main() {
            	// 求两数之和
            	// 匿名函数方式1
            	res1 := func(n1, n2 int) int {
            		return n1 + n2
            	}(10, 20)
            	fmt.Println("匿名函数方式1...res1 = ", res1)
            	// 匿名函数方式2
            	func1 := func(n1, n2 int) int {
            		return n1 + n2
            	}
            	res2 := func1(10, 20)
            	fmt.Println("匿名函数方式2...res2 = ", res2)
            }
            输出:
            匿名函数方式1...res1 =  30
            匿名函数方式2...res2 =  30
            

            闭包

            闭包就是第一个函数和与其相关的引用环境组合的一个整体(实体)。

            现在开始通过例子来说明闭包:

            func incr() func() int {
            	var x int
            	return func() int {
            		x++
            		return x
            	}
            }
            

            调用这个函数会返回一个函数变量。

            i := incr():通过把这个函数变量赋值给 i,i 就成为了一个闭包。

            所以 i 保存着对 x 的引用,可以想象 i 中有着一个指针指向 x 或 i 中有 x 的地址。

            由于 i 有着指向 x 的指针,所以可以修改 x,且保持着状态:

            println(i()) // 1
            println(i()) // 2
            println(i()) // 3
            

            也就是说,x 逃逸了,它的生命周期没有随着它的作用域结束而结束。

            但是这段代码却不会递增:

            println(incr()()) // 1
            println(incr()()) // 1
            println(incr()()) // 1
            

            这是因为这里调用了三次 incr(),返回了三个闭包,这三个闭包引用着三个不同的 x,它们的状态是各自独立的。

VPS购买请点击我

文章版权声明:除非注明,否则均为主机测评原创文章,转载或复制请以超链接形式并注明出处。

目录[+]