一. 指针

先来看一段代码

    var a int =
var pa *int
pa = &a
*pa =
fmt.Println(a)

这里定义了一个int类型的变量a, 有定义了一个指针类型的变量pa, 让pa指向了a的地址. 然后修改了pa的值, 我们看看打印出来的a是什么:

结果:  

下面开始描述指针

1. 指针也是一种变量, 指针变量的定义方式, 和普通变量的定义方式一样

2. go语言中的指针不能进行运算. 在c中,指针是可以运算的. 比如 &pa+2, 这在go中是不可以的. 还是刚才的例子,我们对指针进行运算, 编译就过不去

3. go语言只有值传递, 没有引用传递. 如何实现go语言的引用传递呢, 配合指针.

如果你想要传递一个int类型的数值,如果是直接传递过去. 那么就是值拷贝. 如果想要引用传递, 那么就要把指针传递过去.

思考: go语言只有值传递. 比如,定义一个方法, 然后调用它:

func test(a int) {
a ++
}
func main() {
a := 5
test (a)
fmt.Println("a: ", a)
} 结果: a: 5

  这里main方法调用test,将a拷贝了一份传给test. 这是值传递. 那么怎么才能让值跟着发生变化呢? 我们可以配合地址使用.

func test(a *int) {
*a ++
} func main() {
a := 5
test (&a)
fmt.Println("a: ", a)
} 结果: a: 6

  这是一个配合地址使用的值传递. 他的传递是将a的地址复制了一份,传给了test. 如下图:

他们最终指向的都是5. 所以, 一旦test方法修改ile值, main方法中的也会随之修改

那么, 自定义类型是值传递还是引用传递呢?

答案: 不一定. 如果你将一个对象Cache传递到一个函数里. 他是对这个Cache的值copy么? 不一定. 要看这个函数里面的内部结构: 看下面的例子.

例子1:

type Cache struct {
aa int
bb int
cc string
} func change(c Cache) {
c.cc = "world"
fmt.Println("in change, c", c)
} func main() {
// 初始化一个Cache对象
var c = Cache{aa:1, bb:100, cc:"hello"}
// 修改对象的值. 这里传递过去的是一个值拷贝
change(c)
fmt.Println(c)
}

这个例子, go语言只有值拷贝, 这是对对象的值拷贝的过程. 修改了函数体里面对象的属性, 对函数外没有影响,

结果:

in change, c {1 100 world}
{1 100 hello}

例子2:

type Cache struct {
aa int
bb int
cc string
} func change(c *Cache) {
c.cc = "world"
fmt.Println("in change, c", c)
} func main() {
// 初始化一个Cache对象
var c = Cache{aa:1, bb:100, cc:"hello"}
// 修改对象的值. 这里传递过去的是一个值拷贝
change(&c)
fmt.Println(c)
}

这个例子和例1不一样的地方是, 函数的参数是一个指针. go语言只有值拷贝, 这里是将c的地址拷贝了一份传给了change. 这就达到了引用传递的效果, 修改函数体里面的值,外面也受影响.

返回结果:

in change, c &{1 100 world}
{1 100 world}

例子3:

type Cache struct {
aa int
bb int
cc *string // 结构体里面有一个指针对象
} func change(c Cache) {
*c.cc = "world"
fmt.Println("in change, c", c.aa, c.bb, *c.cc)
} func main() {
// 初始化一个Cache对象
cc := "hello"
var c = Cache{aa:1, bb:100, cc:&cc}
// 修改对象的值. 这里传递过去的是一个值拷贝
change(c)
fmt.Println(c.aa, c.bb, *c.cc)
}

这个例子和前两个例子不同的地方是: 结构体Cache中cc是一个地址. 他不是一个变量了. 那么这个时候. change函数修改了cc的值,会怎么样呢?

结果:

in change, c   world
world

是的, cc是一个地址, 当对象c拷贝一份到cc里面的时候, Cache中的地址变量cc 直接复制一份到change. 本身cc就是地址, 所以函数内改变了cc所在地址的值, 那么函数外也会改变

这就说明了, 结构体传递到函数里面, 到底是值传递还是引用传递呢? 和结构体内部的结构有关系.

3. 用go语言实现交换两个变量的值.

分析1:

func change(a int ,b int) {
a, b = b, a
fmt.Println("in change, a:", a, ", b:", b)
} func main() {
// 初始化一个Cache对象
a, b := 3, 4
change(a, b)
fmt.Println(a, b)
}

上述方法返回值:

in change, a: 4 , b: 3
3 4

错误的原因在于, int类型是值传递, 修改内容的值, 对外部没影响. 所以返回的还是3, 4

分析2: 地址传递

func change(a *int ,b *int) {
fmt.Println(fmt.Println("[change---1], a:", a, ", b:", b))
a, b = b, a
fmt.Println("[change--2], a:", a, ", b:", b)
} func main() {
// 初始化一个Cache对象
a, b := 3, 4
fmt.Println("[main --- 1]", &a, &b)
change(&a, &b)
fmt.Println("[main --- 2]", a, b)
}

先来看输出结果:

[main --- 1] 0xc000096008 0xc000096010
[change---1], a: 0xc000096008 , b: 0xc000096010
48 <nil>
[change--2], a: 0xc000096010 , b: 0xc000096008
[main --- 2] 3 4

出乎意料, 原本以为, main函数最后的输出会是4, 3. 我们发现, 结果并不是. 也就是说, 虽然传的是指针过去, 但是是对指针的一个copy,  这一点更说明了, 所有的变量都是值拷贝. 包括指针变量. 也是一个值拷贝

分析3: 改变地址的值

func change(a *int ,b *int) {
fmt.Println(fmt.Println("[change---1], a:", a, ", b:", b))
*a, *b = *b, *a // 把地址的值改变了
fmt.Println("[change--2], a:", a, ", b:", b)
} func main() {
// 初始化一个Cache对象
a, b := 3, 4
fmt.Println("[main --- 1]", &a, &b)
change(&a, &b)
fmt.Println("[main --- 2]", a, b)
}

和三个不同之处, 是在change函数内, 修改了指针类型的变量的值. 输出结果是:

[main --- ] 0xc00001c0f8 0xc00001c100
[change---], a: 0xc00001c0f8 , b: 0xc00001c100
<nil>
[change--], a: 0xc00001c0f8 , b: 0xc00001c100
[main --- ]

我们看到, 最后的输出结果却是是4, 3

分析4: 还有一种更简单的交换两个值的方式

func change(a *int ,b *int) (int, int){
return *b, *a
} func main() {
// 初始化一个Cache对象
a, b := 3, 4
fmt.Println("[main --- 1]", &a, &b)
a,b = change(&a, &b)
fmt.Println("[main --- 2]", a, b)
}

输出:

[main --- ] 0xc00001c0f8 0xc00001c100
[main --- ]

分析5: 最简单的方式

func change(a int ,b int) (int, int){
return b, a
} func main() {
// 初始化一个Cache对象
a, b := 3, 4
fmt.Println("[main --- 1]", &a, &b)
a,b = change(a, b)
fmt.Println("[main --- 2]", a, b)
}

结果:

[main --- ] 0xc000096008 0xc000096010
[main --- ]

从这里我们得出以下结论:

1. 指针类型的变量, 和普通变量一样, 是值传递.

2. 指针类型的变量, 要想修改变量的值, 需要使用指针的指针来改变. 其实,在指针里面, 是指针的指针就是值了. 那么, 我们的原则是, 不管他是什么, 只有修改的是指针, 那么就达到了引用传递的效果.

指针对于我们来说, 方便好多, 但是也会产生很对疑问. 比如分析4和分析5, 他们为什么得到的结果是一只呢? 需要看一下他的内部到底是怎么交换的.

aaa

go基础系列 第二章 go指针的更多相关文章

  1. sql系列(基础)-第二章 限制和排序数据

    更好的看↑代码点击VIEW PLAN 第二章 约束和排序数据 1. 在 emp 表中选择工资介于 1500 到 2500 的员工的信息: 注意:使用 between 下边界 and 上边界时.条件包括 ...

  2. Java语言程序设计(基础篇)第二章

    第二章 基本程序设计 2.2 编写简单的程序 1.变量名尽量选择描述性的名字(descriptive name). 2.实数(即带小数点的数字)在计算机中使用一种浮点的方法来表示.因此,实数也称为浮点 ...

  3. jQuery系列 第二章 jQuery框架使用准备

    第二章 jQuery框架使用准备 2.1 jQuery框架和JavaScript加载模式对比 jQuery框架的加载模式 <script> window.onload = function ...

  4. ActiveMQ 快速入门教程系列 第二章 发布-订阅者模式实现

    第二章我们会介绍怎样实现一个发布者对多个订阅者的消息传递 Topic和queue的最大区别在于topic是以广播的形式,通知所有在线监听的客户端有新的消息,没有监听的客户端将收不到消息:而queue则 ...

  5. 从零开始的程序逆向之路基础篇 第二章——用OllyDbg(OD)分析一个简单的软件

    作者:Crazyman_Army 原文来自:https://bbs.ichunqiu.com/thread-43469-1-1.html 0x00知识回顾 (由于笔者省事,没开XP虚拟机,而且没关闭A ...

  6. python基础教程-第二章-列表和元组

    本章将引入一个新的概念,:数据结构.数据结构是通过某种方式(例如对元素进行编号)组织在 一起的数据元素的集合,这些数据元素可以是数字或者字符,甚至可以是其他数据结构.在python中,最基本的数据结构 ...

  7. WPF从入门到放弃系列第二章 XAML

    本文是作者学习WPF从入门到放弃过程中的一些总结,主要内容都是对学习过程中拜读的文章的整理归纳. 参考资料 XAML 概述 (WPF):https://msdn.microsoft.com/zh-cn ...

  8. go基础系列 第一章 go基础语法

    0.前言 1. go定义变量的几种方式 2. go内建变量类型 3. 常量的定义 4. go枚举 5. go的if语句 零. go语言的换行 go语言对换行很有讲究, 如果想换行,必须有一个逗号, 否 ...

  9. C++ Primer 第二章 引用 指针 const限定符

    1.引用: 为对象起了另外一个名字,引用类型引用另外一种类型,通过将声明符写成&d的形式来定义引用类型,其中d也就是声明的变量名(声明符就是变量名). PS:1.通过图片中编译所提示的报错信息 ...

随机推荐

  1. 如何在没有代理的情况下编译 tidb server

    这里主要介绍 tidb server 的编译, ti kv 和 ti pd 的编译不在本文范围内: go 语言 1.11 版本之后支持 go.mod,  依赖包在 go.mod 里生成, 如果 go. ...

  2. Python2.7学习

    网上很多代码都不适用于python3版本,所以还是转回版本2来学习了 install 安装模块特别简单 E:\01_SOFT\Python27\python  -m easy_install sunb ...

  3. ros平台下python脚本控制机械臂运动

    在使用moveit_setup_assistant生成机械臂的配置文件后可以使用roslaunch demo.launch启动demo,在rviz中可以通过拖动机械臂进行运动学正逆解/轨迹规划等仿真运 ...

  4. 下载安装tomcat 部署本地项目

    原文地址:https://blog.csdn.net/weixin_40396459/article/details/81706543 下载地址:http://tomcat.apache.org 点击 ...

  5. luogu_P4767 [IOI2000]邮局

    传送门 Description 高速公路旁边有一些村庄.高速公路表示为整数轴,每个村庄的位置用单个整数坐标标识.没有两个在同样地方的村庄.两个位置之间的距离是其整数坐标差的绝对值. 邮局将建在一些,但 ...

  6. spoj Longest Common Substring (多串求最大公共子序列)

    题目链接: https://vjudge.net/problem/SPOJ-LCS 题意: 最多10行字符串 求最大公共子序列 数据范围: $1\leq |S| \leq100000$ 分析: 让他们 ...

  7. Ansible 模式

    一.Ansible 命令 1.Ansible 命令执行的方式有两种:Ad-Hoc.Ansible-playbooks,这两种方式没有本质的区别,Ad-Hoc用于临时执行命令:Ansible-playb ...

  8. GO windows下编译luajit

    1 GO嵌入luajit需要用到cgo,使用cgo需要安装gcc,在windows上下载MinGW-W64安装上配置好环境变量就可以 2 gcc编译luajit,生成.a文件. 把LuaJIT-2.0 ...

  9. java读取excel文件数据导入mysql数据库

    这是我来公司的第二周的一个小学习任务,下面是实现过程: 1.建立maven工程(方便管理jar包) 在pom.xml导入 jxl,mysql-connector 依赖 可以在maven仓库搜索 2.建 ...

  10. 火狐调试工具-DevTools

    狐调试工具 - DevTools 咱们做写js 代码的时候,遇到的一个最大的问题就是调试问题,很多开发者在写 js 代码的时候,经常都非常痛苦.但是我们如果掌握好相应的调试工具,那么就可以比较游刃有余 ...