专栏首页学院君的专栏Go 语言基础入门教程 —— 数据类型篇:指针的概念和基本使用

Go 语言基础入门教程 —— 数据类型篇:指针的概念和基本使用

指针概述

我们知道,变量的本质对一块内存空间的命名,可以通过引用变量名来使用这块内存空间存储的值,而指针的含义则指向存储这些变量值的内存地址。和 PHP、Java 不同,Go 语言支持指针,如果一个变量是指针类型的,那么就可以用这个变量来存储指针类型的值:

a := 100
var ptr *int
ptr = &a
fmt.Println(ptr)
fmt.Println(*ptr)

如上,ptr 就是一个指针类型,表示指向存储 int 类型值的指针。我们可以通过 *ptr 获取指针指向内存地址存储的变量值(我们通常将这种引用称作「间接引用」),ptr 本身是一个内存地址值(通过 &a 可以获取变量 a 所在的内存地址),所以上述代码打印结果是:

0xc0000a2000
100

你可能记得 PHP 中也有类似通过 & 进行引用传值的用法(数组传值需要,对象传值不需要,因为对象本身是引用类型),其实这种用法的本质也是指针,只不过 PHP 的语言级别屏蔽了指针的概念而已。

Go 语言之所以引入指针类型,主要基于两点考虑,一个是为程序员提供操作变量对应内存数据结构的能力;另一个是为了提高程序的性能(指针可以直接指向某个变量值的内存地址,可以极大节省内存空间,操作效率也更高),这在系统编程、操作系统或者网络应用中是不容忽视的因素。

指针在 Go 语言中有两个使用场景:

  • 指针类型
  • 数组切片

作为类型指针时,允许对这个指针类型的数据进行修改指向其它内存地址,传递数据时如果使用指针则无须拷贝数据从而节省内存空间,此外和 C 语言中的指针不同,Go 语言中的类型指针不能进行偏移和运算,因此更为安全。

数组切片我们前面已经介绍过,由指向起始元素的原始指针、元素数量和容量组成,所以切片与数组不同,是引用类型,而非值类型。

指针的基本使用

下面我们以一些简单的示例代码来演示 Go 语言中指针的基本使用。

指针类型的声明和初始化

指针变量在传值时之所以可以节省内存空间,是因为指针指向的内存地址的大小是固定的,在 32 位机器上占 4 个字节,在 64 位机器上占 8 个字节,这与指针指向内存地址存储的值类型无关。

关于指针类型的声明我们在开头已经演示过,这里我们再回头看下这段代码:

var ptr *int
fmt.Println(ptr)

a := 100
ptr = &a
fmt.Println(ptr)
fmt.Println(*ptr)

当指针被声明后,没有指向任何变量内存地址时,它的零值是 nil,然后我们可以通过在给定变量前加上取地址符 & 获取变量对应的内存地址将其赋值给声明的指针类型,这样,就是对指针的初始化了,然后我们可以通过在指针类型前加上间接引用符 * 获取指针指向内存空间存储的变量值。当然,我们也可以通过 := 对指针进行初始化:

a := 100
ptr := &a
fmt.Printf("%p\n", ptr)
fmt.Printf("%d\n", *ptr)

底层会自动判断指针的类型,在格式化输出时,可以通过 %p 来标识指针类型。

通过指针传值

我们再来看一个通过指针传值的示例,通过指针传值就类似于 PHP 中通过引用传值,这样做的好处是节省内存空间,此外还可以在调用函数中实现对变量值的修改,因为直接修改的是指针指向内存地址上存储的变量值,而不是值拷贝。

为了体现出区别,我们先看不使用指针的值拷贝示例:

func swap(a, b int)  {
    a, b = b, a
    fmt.Println(a, b)
}

func main() {
    a := 1
    b := 2
    swap(a, b)
    fmt.Println(a, b)
}

上述代码的打印结果是:

2 1
1 2

下面我们通过指针传值来重构上述代码:

func swap(a, b *int)  {
    *a, *b = *b, *a
    fmt.Println(*a, *b)
}

func main() {
    a := 1
    b := 2
    swap(&a, &b)
    fmt.Println(a, b)
}

上述代码的打印结果是:

2 1
2 1

因为这次,我们是通过指针传值的(&a&b 都是指针,只不过我们没有显示声明而已),直接会对内存地址存储变量值进行交换操作,而主函数中的 ab 变量仅仅是对应内存存储空间的别名而已,所以调用完 swap 函数后,它们所对应的内存空间存储值已经交换过来了。

本文分享自微信公众号 - 学院君的后花园(geekacademy)

原文出处及转载信息见文内详细说明,如有侵权,请联系 yunjia_community@tencent.com 删除。

原始发表时间:2019-06-25

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • 【干货】PHP7强悍性能背后,zval的变化!

    PHP7已经发布, 如承诺, 我也要开始这个系列的文章的编写, 主要想通过文章让大家理解到PHP7的巨大性能提升背后到底我们做了什么, 今天我想先和大家聊聊zv...

    猿哥
  • 10年开发经验的PHPer老司机经验之谈

    在PHP的coding之路上不经意间已走过了10余载,期间有幸结识了一些高手,谈及成功的经验,总结下来,主要有以下几点:

    猿哥
  • JAVA通过URL链接获取视频文件信息(无需下载文件)

    最近项目碰到一个大坑:APP上需要在获取视频列表时就获取视频的时长,但早期上传的时候数据库都没有保存这个数据,所以前段时间添加一个时长字段,在上传时手动输入视频...

    ZhangXianSheng
  • Fast Web Scraping With ReactPHP

    Have you ever needed to grab some data from a site that doesn’t provide a public...

    猿哥
  • 从PHP 5到PHP 7性能全评测(含未发布的JIT版PHP 8对比)

    导读:PHP 是 Web 开发最常用的语言,每个大版本的更新都带来不少新特性和性能提升。特别是 PHP 7.0 的发布,带来 PHP 性能飞跃。本文作者对各个 ...

    猿哥
  • 【鸟哥】PHP7强悍性能背后,zval的变化!

    PHP7已经发布, 如承诺, 我也要开始这个系列的文章的编写, 主要想通过文章让大家理解到PHP7的巨大性能提升背后到底我们做了什么, 今天我想先和大家聊聊zv...

    猿哥
  • maven版SpringBoot的Hello World

    1)新建maven项目,package方式为jar. 用archetype quick start.参考我视频目录下的SpringbootMaven项目。

    马克java社区
  • PHP命名空间

    首先, 为什么要用到命名空间呢? 当然是为了解决重名的问题了, 其实命名空间和Java中的包有着异曲同工之妙, Java中 a包下的Test类, 和b包下的Te...

    烟草的香味
  • 关于LNMP+Frp内网穿透环境下一个PHP程序运行超时的问题

    一直想在内网穿透的环境下运行WordPress,但是由于WordPress的一个插件一直运行超时错误的问题未解决而作罢,这次有空调试一下,看看能否解决:

    猿哥
  • PHP的垃圾回收机制以及大概实现

    垃圾回收,简称gc。顾名思义,就是废物重利用的意思。再说这个之前先接触一下内存泄露,大概意思就是申请了一块地儿拉了会儿屎,拉完后不收拾,那么那块儿地就算是糟...

    猿哥

扫码关注云+社区

领取腾讯云代金券