zoukankan      html  css  js  c++  java
  • Golang之指针

    5.6 指针

    5.6.1 什么是指针

    • 指针就是一个变量,用来存储另一个变量的内存地址
    func main() {
        // 指针地址,指针类型,指针取值
        // &取地址,*根据地址取值
        a := 10
        b := &a
        fmt.Println(*b)
    
        //指针类型 *int *unit *float *string *array *struct 等
        
        // 指针的定义
        // var 变量名 指针类型
        var p1 *int  //定义空指针
        p1 = &1
    }
    

    5.6.2 数组指针和指针数组

    • 数组指针:是一个指针,用来存储数组的内存地址
    func main() {
        arr := [4] int{1, 2, 3, 4}
        fmt.Println(arr)
      
        // 定义一个数组指针
        var p1 *[4] int
        fmt.Println(p1)  // nil 空指针
        fmt.Ptintf("%T
    ", p1)  // *[4] int
        
        p1 = &arr1
        fmt.Println(p1)
        fmt.Printf("%p
    ", p1)
        fmt.Printf("%p
    ", &p1)
        
        // 通过数组指针操作数组
        (*p1)[0] = 100
        // 可以简写为
        // p1[0] = 200
        fmt.Println(arr)  // [100 2 3 4]
    }
    
    • 指针数组:是一个数组,元素为指针
    func main() {
        a := 1
        b := 2
        c := 3
        d := 4
        arr1 := [4] int{a, b, c, d}
        arr2 := [4] *int{&a, &b, &c, &4}
        fmt.Println(arr1)
        fmt.Println(arr2)
        
        // 操作数组与指针数组的区别
        //arr1[0] = 100
        //fmt.Println(a)
        //fmt.Println(arr1, &arr1[0])  // 值类型,将a,b,c,d的值拷贝过来放到数组中,修改的是数组空间内的数据,与a,b,c,d没有关系
        //fmt.Println(arr2)
        
        *arr2[0] = 1000
        fmt.Println(a)
        fmt.Println(arr1, &arr1[0])
        fmt.Println(arr2, *arr2[0])       // 通过指针数组中的内存地址,修改了a,b,c,d的值,类似于Python列表
        
        b = 2000
        fmt.Println(b)
        fmt.Println(arr1, &arr1[1])
        fmt.Println(arr2, *arr2[1])
    }
    

    总结:

    数组是值类型,将值拷贝了一份放到数内存中,二者相互独立,互不影响,修改数组后数组内存中的值改变,不会影响拷贝的源数据,源数据改变,也不会影响数组

    Go中切片是对原数组的引用,二者互相关联,修改切片元素的值后,与之关联的底层数组的也会受到影响,同理,底层数组的改变也会影响切片的值

    Python中的列表是引用类型,基于指针数组,修改可变元素后(可变类型与不可变类型),引用的源数据也会受到影响---

    • Go中切片与Python中列表的区别
      • go的切片,其成员是相同类型的,python的列表和元组则不限制类型。
      • 两种语言都有[a: b]这种切片操作,意义也类似,但是go的a、b两个参数不能是负数,python可以是负数,此时就相当于从末尾往前数。
      • 两种语言都有[a: b: c]这种切片操作,意义却是完全不同的。go的c,表示的是容量;而python的c表示的是步长
      • python的切片产生的是新的对象,对新对象的成员的操作不影响旧对象;go的切片产生的是旧对象一部分的引用,对其成员的操作会影响旧对象。
      • 底层实现的不同
        • go的切片,底层是一个三元组,一个指针,一个长度,一个容量。指针指向一块连续的内存,长度是已有成员数,容量是最大成员数。切片时,一般并不会申请新的内存,而是对原指针进行移动,然后和新的长度、容量组成一个切片类型值返回。也就是说,go的切片操作通常会和生成该切片的切片共用内存。不仅是切片,字符串、数组的切片也是一样的,通常会共用内存。当然也有异常情况,那就是切片时,提供的容量过大,此时会申请新内存并拷贝;或者对切片append超出容量,也会如此。这时,新的切片,才不会和老切片共享内存。(如果你切片/创建时提供的容量小于长度,会panic)
        • python的列表,其实是个指针数组。当然在下层也会提供一些空位之类的,但基本就是个数组。对它们切片,会创建新的数组,注意,是创建新的数组!python的列表可没有容量的概念。这其实也体现了脚本语言和编译语言的不同。虽然两个语言都有类似的切片操作;但是python主要目标是方便;go主要目标却是快速(并弥补丢弃指针运算的缺陷)。

    5.6.3 函数指针与指针函数

    // 函数指针
    // Go中函数默认就是一个指针类型,不需要*
    func main() {
        var a func()
        a = func1
        a()
    }
    func func1() {
        fmt.Println("这是func1()")
    }
    
    // 指针函数
    // 返回值为指针的函数
    func main() {
        // arr1是数组,值传递,将func1中返回的arr的值拷贝到arr1中,当func1调用结束,arr被销毁
        arr1 := func1()
        fmt.Printf("arr1的类型:%T,内存地址:%p,值:%v
    ",arr1, &arr1, arr1)
        
        // arr2是指针类型,值传递,将func2中返回的arr的内存地址保存到arr2中,arr不会随着func2的结束而销毁(和闭包一样改变了变量的生命周期?)
        arr2 := func2()
        fmt.Printf("arr2的类型:%T,内存地址:%p,值:%v
    ",arr2, &arr2, arr2)
    }
    
    // 这是普通函数
    func func1() {
        arr := [4] int{1, 2, 3, 4}
        return arr
    }
    
    // 这是指针函数
    func func2() *[4] int {
        arr := [4] int{1, 2, 3, 4}
        return &arr
    }        
    

    5.6.4 指针作为参数

    func main() {
        /*
        指针作为参数
        参数:值传递和引用传递
        
        总结:值传递,拷贝一份,不会影响原数据,但消耗内存
             引用传递通过指针操作数据,会改变原数据,但节省内存(拷贝的数据可能很大)
        */
        n := 10
        // 值传递,func1改变不会影响n,a随着func1结束而被销毁
        func1(n)
        fmt.Println(n)
    
        // 引用传递,将n的内存地址拷贝到a中,通过*a更改了n,a也会随着func2的结束而被销毁,但n已经改变
        func2(&n)
        fmt.Println(n)
    }
    
    func func1(a int) {
        a = 100
        fmt.Println(a)
    }
    
    func func1(a *int) {
        *a = 100
        fmt.Println(*a)
    
  • 相关阅读:
    Java多线程在JavaWeb中的应用
    hibernate,spring,struts的流程以及使用理由
    hibernate如何实现持久化
    ibatis与hibernate有什么区别
    关于导出pdf的例子
    BusyBox
    Android中如何查看内存
    使用PopupWindow实现Menu功能
    ScrollView原理
    eclipse 代码提示时闪退问题
  • 原文地址:https://www.cnblogs.com/zj420255586/p/12755163.html
Copyright © 2011-2022 走看看