zoukankan      html  css  js  c++  java
  • go语言的指针类型

    一、指针与引用的相关概念

    什么是指针?

      指针,全称为指针变量,是用来存储内存地址的一种变量。程序中,一般通过指针来访问其指向的内存地址中的内容(数据)。

     

    什么是引用?

      引用,是C++中提出来的一种新的使用变量的方式,即,给实际变量起个别名,通过这个别名来引用实际的变量。标准C中不支持变量的引用。

     

    指针与引用的区别?

      1.指针是实实在在的变量,有自己的内存存储空间,它可以指向任何有效的变量。

      2.引用是一种形式、方法,定义的引用变量,实际上是原实际变量的另一个名称(别名),引用变量本身没有自己的实际存储空间,操作引用变量,就是在操作实际变量。

     

    go语言的指针与c语言指针,以及java,python等引用类型的语言的区别与联系:

      1.Java,Python,Javascript 等引用类型的语言

      2.Golang 的指针是单独的类型,而不是 C 语言中的 int 类型,而且也不能对指针做整数运算。

      3.从这一点看,Golang 的指针基本就是一种引用。

     

    对引用类型语言来说,参数传递时,什么时候传值,什么时候传引用?

      1.在大部分引用型语言里,参数为基本类型时,传进去的大都是值,也就是另外复制了一份参数到当前的函数调用栈。

      2.参数为高级类型时,传进去的基本都是引用。这个主要是因为虚拟机的内存管理导致的。

     

    传值和传引用的区别?

      1.内存管理中的内存区域一般包括 heap 和 stack, stack 主要用来存储当前调用栈用到的简单类型数据:string,boolean,int,float 等。

      2.这些类型的内存占用小,容易回收,基本上它们的值和指针占用的空间差不多,因此可以直接复制,GC也比较容易做针对性的优化。

      3.复杂的高级类型占用的内存往往相对较大,存储在 heap 中,GC 回收频率相对较低,代价也较大,因此传引用/指针可以避免进行成本较高的复制操作,并且节省内存,提高程序运行效率。

    而在 Golang 中,具体到高级类型 struct,slice,map,也各有不同。实际上,只有 struct 的使用有点复杂,slice,map,chan 都可以直接使用,不用考虑是值还是指针。

    二、go语言指针介绍

    go的原生数据类型分类:

      1.基本类型:string, bool, int, float等           使用值传递

      2.高级类型:sturuct, array/slice, map, chan, func。     使用引用传递

     

    go的指针定义:

        a.一个指针的值是另一个变量的地址。

      b.一个指针对应变量在内存中的存储位置。

      c.并不是每一个值都会有一个内存地址。

      d.灭一个变量必然有对应的内存地址。     

        e.用过指针我们可以直接读或者更新对应变量的值,而不需要知道该变量的名字。

     

    go语言中,什么时候使用指针?(指针使用的场景):

      1.需要改变参数的值

      2.避免复制操作

      3.节省内存

     

    go语言的值传递和“引用传递”

      1.go语言中的传递方式只有值传递

      2.所谓的“引用传递”是指将要传递的较大的值,复制他的内存地址然后将内存地址通过值传递传给对象。对象拿到内存地址的值就可以直接访问了。这个过程看起来很像“引用传递”

     

    指针(pointer)在go 语言中拆分为两个核心概念:

      1.类型指针,允许对这个指针类型的数据进行修改。传递数据使用指针,而无需拷贝数据。类型指针不能进行偏移量和运算。

      2.切片,由指向起始元素的原始指针、元素数量和容量组成。

     

    go语言指针的优势:

      1.Go 语言的指针类型变量拥有指针的高效访问

      2.不会发生指针偏移,从而避免非法修改关键性数据问题。

      3.垃圾回收也比较容易对不会发生偏移的指针进行检索和回收。

      4.切片比原始指针具备更强大的特性,更为安全。

      5.切片发生越界时,运行时会报出宕机,并打出堆栈,而原始指针只会崩溃。

     

    指针类型和指针地址:

      1.每个变量在运行时都拥有一个地址,这个地址代表变量在内存中的位置

      2.Go 语言中使用 & 作符放在变量前面对变量进行“取地址”操作。

    ptr := &v    // v的类型为T

      v 代表被取地址的变量

      变量prt 接收 被取地址的 v 

      ptr 的类型就为*T,称做 T 的指针类型。*代表指针。

    package main
    import (
        "fmt"
    )
    func main() {
        //声明整型 cat 变量。
        var cat int = 1
        //声明字符串 str 变量。
        var str string = "banana"
       //使用 fmt.Printf 的动词%p输出 cat 和 str 变量取地址后的指针值,指针值带有0x的十六进制前缀。
        fmt.Printf("%p %p", &cat, &str)
    }
    
    
    
    //运行结果
    //0xc042052088 0xc0420461b0
    指针的实际用法

    变量、指针、地址,三者的关系?

      1.每个变量都拥有地址

      2.指针的值就是地址

      

    如何使用指针获取指针指向的值?

      在对普通变量使用&操作符取地址获得这个变量的指针后,可以对指针使用*操作,也就是指针取值

    package main
    import (
        "fmt"
    )
    func main() {
        // 准备一个字符串类型
        var house = "Malibu Point 10880, 90265"
        // 对字符串取地址, 将指针保存到 ptr 中,ptr类型为*string
        ptr := &house
        // 打印ptr的类型  类型为 *string。
        fmt.Printf("ptr type: %T
    ", ptr)
        // 打印ptr的指针地址   每次运行都会发生变化。
        fmt.Printf("address: %p
    ", ptr)
        // 对 ptr 指针变量进行取值操作   value 变量类型为 string。
        value := *ptr
        // 取值后的类型
        fmt.Printf("value type: %T
    ", value)
        // 指针取值后就是指向变量的值
        fmt.Printf("value: %s
    ", value)
    }
    
    
    //运行结果
    //ptr type: *string
    //address: 0xc0420401b0
    //value type: string
    //value: Malibu Point 10880, 90265
    指针获取指针

      取地址操作符&和取值操作符*是一对互补操作符

      &取出地址,*根据地址取出地址指向的值。

    变量、指针地址、指针变量、取地址、取值的相互关系和特性:

      1.对变量进行取地址(&)操作,可以获得这个变量的指针变量。

      2.指针变量的值是指针地址。

      3.对指针变量进行取值(*)操作,可以获得指针变量指向的原变量的值。

    指针的零值和相等测试

      1.任何类型的指针的零值都是nil。

      2.如果 p != nil 为真,那么p就指向某个有效变量  (p为指针)

      3.指针只有指向同一个变量或者全部为nil时才相等。

    var x,y int
    var z  *int
    var w  *string
    fmt.Println(&x ==&x, &x ==&y,&x ==nil , z == nil ,  w ==nil)
    
    //true false false true true
    指针的相等测试

    如何使用指针修改值?

    package main
    import "fmt"
    // 定义一个交换函数,参数为 a、b,类型都为 *int,都是指针类型。
    func swap(a, b *int) {
        // 将 a 指针取值,把值(int类型)赋给 t 变量,t 此时也是 int 类型。
        t := *a
        // 取 b 指针值,赋给 a 变量指向的变量。注意,此时*a的意思不是取 a 指针的值,而是“a指向的变量”。
        *a = *b
        // 将 t 的值赋给 b 指向的变量。
        *b = t
    }
    func main() {
    // 准备 x、y 两个变量,赋值 1 和 2,类型为 int。
        x, y := 1, 2
        // 取出 x 和 y 的地址作为参数传给 swap() 函数进行调用。
        swap(&x, &y)
        // 交换完毕时,输出 x 和 y 的值。
        fmt.Println(x, y)
    }
    
    
    //运行结果
    //2 1
    使用指针修改值

      1.*操作符作为右值时,意义是取指针的值

      2.作为左值时,也就是放在赋值操作符的左边时,表示 a 指向的变量。

      归纳:

        1.*操作符的根本意义就是操作指针指向的变量。

        2.当操作在右值时,就是取指向变量的值;

        3.当操作在左值时,就是将值设置给指向的变量。

      每次我们对一个变量取地址,或者是复制指针,我们都是为原变量创建新的别名。例如a就是变量p的别名。

      指针特别有价值的地方在于我们可以不用名字而访问一个变量的。  

    package main
    import "fmt"
    func swap(a, b *int) {
        b, a = a, b
    }
    func main() {
        x, y := 1, 2
        swap(&x, &y)
        fmt.Println(x, y)
    }
    
    
    //运行结果
    //1  2
    swap() 函数中交换指针值

      1.上面代码中的 swap() 函数交换的是 a 和 b 的地址

      2.在交换完毕后,a 和 b 的变量值确实被交换。

      3.但和 a、b 关联的两个变量并没有实际关联。

      4.这就像写有两座房子的卡片放在桌上一字摊开,交换两座房子的卡片后并不会对两座房子有任何影响。

    创建指针的另一种方法--new()函数

    package main
    
    import "fmt"
    
    
    func main() {
        //申请一个指针str,类型为*string
        str := new(string)
            //指针str指向字符串“ninja”
        *str = "ninja"
            //打印str指向的地址空间
        fmt.Println(*str)
            //打印指针str的内存地址
        fmt.Printf("%p
    ",str)
        fmt.Printf("%d
    ",str)
            //打印指针str的类型
        fmt.Printf("%T
    ",str)
    
    }
    
    
    //运行结果
    //ninja
    //0xc00000e1e0
    //824633778656
    //*string
    new()方法创建指针
  • 相关阅读:
    mac os programming
    Rejecting Good Engineers?
    Do Undergrads in MIT Struggle to Obtain Good Grades?
    Go to industry?
    LaTex Tricks
    Convert jupyter notebooks to python files
    How to get gradients with respect to the inputs in pytorch
    Uninstall cuda 9.1 and install cuda 8.0
    How to edit codes on the server which runs jupyter notebook using your pc's bwroser
    Leetcode No.94 Binary Tree Inorder Traversal二叉树中序遍历(c++实现)
  • 原文地址:https://www.cnblogs.com/ppzhang/p/10552787.html
Copyright © 2011-2022 走看看