什么是指针?

指针是一个变量,用于存储另一个变量的内存地址。

在上面的例子中,变量b的值是156,存储在内存地址0x1040a124。 变量a包含b的地址。 可以说现在a指向b。

声明指针

指向类型 T 的指针用 *T 表示

让我们写一些代码。

package main

import (
"fmt"
) func main() {
b := 255
var a *int = &b
fmt.Printf("Type of a is %T\n", a)
fmt.Println("address of b is", a)
}

&运算符用于获取变量的地址。 在上面的程序中,我们将b的地址赋给一个类型为* int的类型。 据说现在a指向b。 当我们在a中打印该值时,b的地址将被打印。 这个程序输出

[Running] go run "d:\GoProject\src\golang-study-line\basic-02\basic_pointer_01.go"
Type of a is *int
address of b is 0xc042054080

你可能会得到一个不同的b地址,因为b的位置可以在内存中的任何地方。执行两次得到的结果  

[Running] go run "d:\GoProject\src\golang-study-line\basic-02\basic_pointer_01.go"
Type of a is *int
address of b is 0xc042054080 [Done] exited with code=0 in 11.167 seconds [Running] go run "d:\GoProject\src\golang-study-line\basic-02\tempCodeRunnerFile.go"
Type of a is *int
address of b is 0xc04200e098 [Done] exited with code=0 in 0.954 seconds

空指针

指针的零值为 nil。

package main

import (
"fmt"
) func main() {
a := 25
var b *int
if b == nil {
fmt.Println("b is", b)
b = &a
fmt.Println("b after initialization is", b)
}
} 

b在上面的程序中最初是 nil,然后它被分配到a的地址。 这个程序输出(同样是执行两次):

[Running] go run "d:\GoProject\src\golang-study-line\basic-02\basic_pointer_02.go"
b is <nil>
b after initialization is 0xc042054080 [Running] go run "d:\GoProject\src\golang-study-line\basic-02\basic_pointer_02.go"
b is <nil>
b after initialization is 0xc042054080

如何引用一个指针

如何引用指针意味着访问指针指向的变量的值。 * a是尊重a的语法。

让我们看看这是如何在程序中起作用的。

package main  

import (
"fmt"
) func main() {
b := 255
a := &b
fmt.Println("address of b is", a)
fmt.Println("value of b is", *a)
}

在上述程序的第10行中,我们引用并打印它的值。 如预期的那样,它打印出b的值。 该程序的输出是

[Running] go run "d:\GoProject\src\golang-study-line\basic-02\basic_pointer_03.go"
address of b is 0xc042054080
value of b is 255

让我们写一个更多的程序,我们使用指针改变b中的值。

package main

import (
"fmt"
) func main() {
b := 255
a := &b
fmt.Println("address of b is", a)
fmt.Println("value of b is", *a)
*a++
fmt.Println("new value of b is", b)
}

在上面的程序中,我们将a指向的值增加1,这将b的值从a改变为b。 因此b的值变成256.程序的输出是  

[Running] go run "d:\GoProject\src\golang-study-line\basic-02\basic_pointer_04.go"
address of b is 0xc04200e098
value of b is 255
new value of b is 256

将指针传递给一个函数

package main

import (
"fmt"
) func change(val *int) {
*val = 55
} func main() {
a := 58
fmt.Println("value of a before function call is",a)
b := &a
change(b)
fmt.Println("value of a after function call is", a)
}

在上面的程序中,在第4行号。 我们将指针变量b传递给函数改变。 内部转换函数中,使用第8行中的解除引用来更改a的值。该程序输出,  

[Running] go run "d:\GoProject\src\golang-study-line\basic-02\basic_pointer_05.go"
value of a before function call is 58
value of a after function call is 55

不要将指向数组的指针作为参数传递给函数。 改用切片。
让我们假设我们想对函数内的数组进行一些修改,并且调用者应该可以看到函数内对该数组所做的更改。 这样做的一种方式是将指向数组的指针作为函数的参数。

package main

import (
"fmt"
) func modify(arr *[3]int) {
(*arr)[0] = 90
} func main() {
a := [3]int{89, 90, 91}
modify(&a)
fmt.Println(a)
}

在上面的程序中的第3行号,我们将数组a的地址传递给修改函数。 在修改函数的第8行中,我们取消引用arr并将90分配给数组的第一个元素。 这个程序输出 

[Running] go run "d:\GoProject\src\golang-study-line\basic-02\tempCodeRunnerFile.go"
[90 90 91]

a [x]是(* a)[x]的简写。 所以上述程序中的(* arr)[0]可以用arr [0]代替。

让我们用这种简写语法重写上述程序。 

package main

import (
"fmt"
) func modify(arr *[3]int) {
arr[0] = 90
} func main() {
a := [3]int{89, 90, 91}
modify(&a)
fmt.Println(a)
}

这个程序也输出

[Running] go run "d:\GoProject\src\golang-study-line\basic-02\basic_pointer_07.go"
[90 90 91]

虽然这种将指向数组的指针作为参数传递给函数并对其进行修改的方式很有效,但这并不是在Go中实现此功能的惯用方式。 我们可以使用切片。

让我们用切片重写相同的程序。  

package main

import (
"fmt"
) func modify(sls []int) {
sls[0] = 90
} func main() {
a := [3]int{89, 90, 91}
modify(a[:])
fmt.Println(a)
}

在上述程序的第13行中,我们将一个切片传递给修改函数。 切片的第一个元素在修改函数中更改为90。

这个程序输出  

[Running] go run "d:\GoProject\src\golang-study-line\basic-02\basic_pointer_08.go"
[90 90 91]

所以避免传递指向数组的指针,使用切片代替它。 这段代码更干净并且是惯用的Go

Go不支持指针算术
Go不支持C语言等其他语言中的指针运算。

package main

func main() {
b := [...]int{109, 110, 111}
p := &b
p++
}

上面的程序会抛出编译错误

[Running] go run "d:\GoProject\src\golang-study-line\basic-02\basic_pointer_09.go"
# command-line-arguments
basic-02\basic_pointer_09.go:6:6: invalid operation: p++ (non-numeric type *[3]int)

参考:

https://golangbot.com/pointers/

Golang入门教程(十五)指针的更多相关文章

  1. 无废话ExtJs 入门教程十五[员工信息表Demo:AddUser]

    无废话ExtJs 入门教程十五[员工信息表Demo:AddUser] extjs技术交流,欢迎加群(201926085) 前面我们共介绍过10种表单组件,这些组件是我们在开发过程中最经常用到的,所以一 ...

  2. RabbitMQ入门教程(十五):普通集群和镜像集群

    原文:RabbitMQ入门教程(十五):普通集群和镜像集群 版权声明:本文为博主原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明. 本文链接:https://blog.c ...

  3. SpringBoot入门教程(十五)集成Druid

    Druid是阿里巴巴开源平台上一个数据库连接池实现,它结合了C3P0.DBCP.PROXOOL等DB池的优点,同时加入了日志监控,可以很好的监控DB池连接和SQL的执行情况,可以说是针对监控而生的DB ...

  4. Photoshop入门教程(五):滤镜

    学习心得:滤镜通常用于摄影行业,是安装在相机镜头前用于过滤自然光的附加镜头,从而获得一些特殊的效果.同理,Photoshop的滤镜也是为了产生特殊的效果.Photoshop滤镜分为两类:一种是内部滤镜 ...

  5. RabbitMQ入门教程(十六):RabbitMQ与Spring集成

    原文:RabbitMQ入门教程(十六):RabbitMQ与Spring集成 版权声明:本文为博主原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明. 本文链接:https: ...

  6. WCF入门教程(五)配置文件

    WCF入门教程(五)配置文件 服务协定以及实现写好后,需要将相关服务公布出去,就需要HOST来承载,供客户端来调用. 承载服务有两种方式,一种通过配置文件,一种通过代码进行配置.上一章已经介绍了代码方 ...

  7. 无废话ExtJs 入门教程十九[API的使用]

    无废话ExtJs 入门教程十九[API的使用] extjs技术交流,欢迎加群(201926085) 首先解释什么是 API 来自百度百科的官方解释:API(Application Programmin ...

  8. Docker入门教程(五)Docker安全

    Docker入门教程(五)Docker安全 [编者的话]DockOne组织翻译了Flux7的Docker入门教程,本文是系列入门教程的第五篇,介绍了Docker的安全问题,依然是老话重谈,入门者可以通 ...

  9. 无废话ExtJs 入门教程十六[页面布局:Layout]

    无废话ExtJs 入门教程十六[页面布局:Layout] extjs技术交流,欢迎加群(201926085) 首先解释什么是布局: 来自百度词典的官方解释:◎ 布局 bùjú: [distributi ...

  10. 无废话ExtJs 入门教程十四[文本编辑器:Editor]

    无废话ExtJs 入门教程十四[文本编辑器:Editor] extjs技术交流,欢迎加群(201926085) ExtJs自带的编辑器没有图片上传的功能,大部分时候能够满足我们的需要. 但有时候这个功 ...

随机推荐

  1. C/C++ 程序库

    C/C++ 程序库 // --------------------------------------------- 来几个不常见但是很变态的库吧: bundle: 把几乎所有常见的压缩库封装成了一个 ...

  2. ssh-keygen适用场景与rsync使用id_rsa技巧

    ssh-keygen工具可以实现免密码登录服务器可参考之前的blog:http://www.cnblogs.com/Mrhuangrui/p/4565333.html写的比较粗糙 原理说明使用ssh- ...

  3. 【BZOJ1299】巧克力棒(博弈论,线性基)

    [BZOJ1299]巧克力棒(博弈论,线性基) 题面 BZOJ 题解 \(Nim\)博弈的变形形式. 显然,如果我们不考虑拿巧克力棒出来的话,这就是一个裸的\(Nim\)博弈. 但是现在可以加入巧克力 ...

  4. [hexo]如何更换主题、删除文章

    如何修改主题 hexo有很多主题,每个人可以选择自己喜欢的主题来应用,也可以自己设计主题并且上传形成公共主题供大家下载. 如果是自己设计主题的话,会稍微麻烦一些,需要自己配置很多文件,并且编写css以 ...

  5. js中的arguments用法

    //arguments对象并不是一个数组,但是访问单个参数的方式与访问数组元素的方式相同 function show(){ console.log(arguments); //arguments.pu ...

  6. [BOI2007]Mokia 摩基亚(CDQ分治)

    upd:\((x1,y1)(x2,y2)\)表示以\((x1,y1)\)为左上端点 \((x2,y2)\)为右下端点的矩形 本来以为是一道二位树状数组的模板,但是看数据范围之后就放弃了,边界既然到了2 ...

  7. [luogu3939][数颜色]

    题目链接 思路 对于每一种颜色都建立一个动态开点线段树.然后每次查询的时候就去这个颜色的线段树上查询就行了.修改之后不要忘记交换颜色. 这个题目数据有点强.抄了个比较快的读入优化才卡过去. 代码 /* ...

  8. Django 子程序

    在Web应用中,通常有一些业务功能模块是在不同的项目中都可以复用的,故在开发中通常将工程项目拆分为不同的子功能模块,各功能模块间可以保持相对的独立,在其他工程项目中需要用到某个特定功能模块时,可以将该 ...

  9. 做错的题目——this的指向

    不管延不延时,setTimeout里面的function都是孤立的,并不属于哪一个对象,所以this只会指向全局

  10. 解决jQuery ajax动态新增节点无法触发点击事件的问题

    在写ajax加载数据的时候发现,后面添加进来的demo节点元素,失去了之前的点击事件.为什么点击事件失效,我们该怎么去解决呢? 其实最简单的方法就是直接在标签中写onclick="" ...