第三式开篇语有些负面, 所以这里就不贴了。有兴趣的自己可以去看看 https://andy-zhangtao.gitbooks.io/golang/content/ 。怒发冲冠,意气之作。看完就完了,别当真。把下面的东西当真就行。

不看内容,只看标题还以为这是一个小说呢。 如果哪天心血来潮,没准会写一篇小说。但自从参加朋友婚姻之后,就受到了打击。 同样进入30岁,有的同学已经年入上百万,前呼后拥。 而自己除了会写点"水货"代码,别无他长。 感慨良多,还是感觉自己不是一块能依靠写代码发家致富的料。 所以会分出一部精力,来考虑如何实现技术变现。 但这个系列仍然会写完,不会虎头蛇尾。因此本节起名:分水岭。(这里为cnblogs的乁卬杨同学发一封表扬信,这位同学大胆留言说第三式写的有些负面,我重看了一下第三式,的确有些负面。 所以汲取教训,写好才是王道,我的文章我说了算!再次谢谢乁卬杨同学)

如果没有C/C++语言经历的同学,估计很少会使用过指针,这个名词应该是在职业生涯当中经常会听到,但很少会使用到的。 尤其什么时候用*,什么时候用&,只靠死记硬背很难活学活用。而在Golang当中,指针的使用变得有些简单。像在前几节讲解函数参数之时,提到Golang参数默认是值传递,但有的时候通过指针可以达到引用传递的作用。而这也只是指针在Golang当中一个使用场景,本节将会具体聊聊Golang中如何使用指针。

正如前面所述,在Golang当中每个变量都是一个内存位置,每个内存位置都有它定义的地址,可以使用&运算符访问它,它表示内存中的地址。例如下面的例子:

var a int = 10
fmt.Printf("%x\n", &a )

这样通过&a,就输出了a的内存地址。每次输出的地址,有可能相同也有可能不相同。这取决于当时的内存使用状态。 如果内存资源不紧张,Golang的GC就不会回收这块内存,输出的就是同一块地址。 如果内存紧张,GC就会回收垃圾内存,然后运行时重新分配一块内存,那时就输出一块新地址。

在大学或者其他教程中,应该会提到过什么是指针。 为了一俗到底,这里再提一遍:指针是一个变量,保存的是内存地址。与任何变量或常量一样,必须先声明一个指针(变量),然后才能使用它来存储任何内存地址。通过下面语法可以声明指针变量:

var name *type

type指的是数据类型,name则是指针名称。*不能丢,表示这是一个指针类型的变量。如果没有*,就变成一个普通数据变量了。比如声明一个int类型,是这样:

var i int

而声明一个int类型的指针,则是:

var i *int

因为指针保存的是内存地址,所以其长度都是相同的,是一个表示内存地址的长十六进制数字。不同数据类型指针之间的唯一区别是指针指向的内存所保存的数据类型。举个例子:每家家门钥匙长得都一样(是同一批防盗门),但每家里面的户型就千奇百怪了。 这里面,钥匙就是指针,而户型就是数据类型。 我们可以说A钥匙指的是二居室,而B钥匙指的是四居室。

声明完指针类型之后,就可以使用指针了。因此,下面来看如何使用指针。

使用指针三板斧:

  1. 声明一个指针。
  2. 将真正数据的内存地址赋给这个指针
  3. 取出指针保存的内存地址,然后通过这个地址取出数据。

指针的使用,基本就是这三板斧。而赋值,取值操作就依靠*和&来操作。对照着例子来看:

package main

import "fmt"

func main() {
var a int = 20
var ip *int ip = &a fmt.Printf("Address of a variable: %x\n", &a ) fmt.Printf("Address stored in ip variable: %x\n", ip ) fmt.Printf("Value of *ip variable: %d\n", *ip )
}
var a int = 20

声明了一个int类型的普通变量,把20搁到里面去。

然后第一斧(声明指针) var ip *int。 声明了一个指针,准备用来保存int类型的变量。(内存地址长度都一样,但Golang是强类型语言,所以必须告诉编译器你准备保存什么类型的数据,因此声明成*int)

然后砍上第二斧,将a的地址给了ip。通过&a取出a的内存地址,然后ip = &a就把地址给了ip。

最后三板斧,取出地址里面的数据,也就是最后一个Printf里面的*ip。 这里面,绕了个弯。 如果直接用ip,那其实是将ip保存的数据打印出来(想想如果i=1, 直接输出i,是多少?)。这个地址本身没有用,我们需要的是这个地址所对应的数据(好比小偷不关注钥匙,关注的是钥匙能打开那扇门)。所以通过*ip,就直接取出ip所保存地址里面的数据了。

如果绕了,就把自个想象成小偷(意淫一下,别真去)。给你一个钥匙,你不心动。而如果告诉你这把钥匙能开那扇门,这个时候才最心动。所以ip保存的是钥匙,而*ip就是告诉你是那扇门。

既然说到钥匙了,就接着往下说。 配钥匙师傅那里都挂着一串空白钥匙,这个专业术语就是空指针。空指针也是一个指针变量类型,只不过没有保存任何地址。想想空白钥匙,那扇门都打不开。

当刚开始第一板斧的时候(var ip *int),其实就已经是空指针了。此时默认值就是0.在绝大多数的操作系统中,0都是一个很神奇的数字,介于天堂和地狱之间。 所以操作系统不让用户直接访问0地址(其实就是操作系统不放心用户,担心用户瞎捣乱)。在Golang当中,特意为空指针起了个名字,叫做nil。例如下面:

if(ptr != nil){ ... 不是空指针 }
if(ptr == nil){ ... 是空指针 }

如果你能熟练背出指针三板斧,那么你就掌握50%了。 如果不能.... 重新GOTO line1,然后Loop。下面开始剩余50%的指针大法。

指针在数组中的应用

莫紧张,不要看到指针就感觉是相亲。 指针比妹子容易对付多了。妹子的世界是太极拳,虚虚实实,拳无定式。对付妹子,就跟圣斗士一样,同样的招式不能用两遍(是不是车田正美也受到了妹子的启发)。而Golang大法,招招式式,都写的很清楚。先来看普通数组如何使用:

   a := []int{10,100,200}
var i int for i = 0; i < MAX; i++ {
fmt.Printf("Value of a[%d] = %d\n", i, a[i] )
}

初始化三个值,然后依次取出。 改造一下,把指针糅合进去。

   const MAX int = 3

   a := []int{10,100,200}
var i int
var ptr [MAX]*int; for i = 0; i < MAX; i++ {
ptr[i] = &a[i] /* assign the address of integer. */
}
for i = 0; i < MAX; i++ {
fmt.Printf("Value of a[%d] = %d\n", i,*ptr[i] )
}

指针的数组使用起来,就和其它数组的使用一模一样。以前怎么用,现在还怎么用。唯一的变化就是,以前的数组保存的是数,而现在的指针数据保存的是地址。 除此之外,毫无区别。 两个例子一对比就够了,没啥说的。

指针的指针

这种场景不得不说。 你可以不去使用,但万一看到了,不能不知道。说实话,使用这个大招的,十有八九是从C++转过来的。不能说不好,个人习惯吧。 我是不喜欢用,毕竟要考虑代码的可读性。(学乖了,为了不找骂,我个人不喜欢而已。言多必失!)

指针的指针,术语叫做指针链。一听链,就表明至少有两个指针。 暂且称之为A针,B针。A针按三板斧的规则,保存的是一个数据的地址。而B针保存的是A针的地址。 绕?看下面:

B --> A --> Data

假设Data所在的地址是123,那么A保存的数据就是123. 同时A自己的地址是abc,那么B保存的数据就是abc。 如果还有C,那就保存B的地址,你高兴,后面可以跟着一群。

那么此时此刻,问题来了。 我知道A可以声明成 var a *int。那么B呢? *int? 好像不对吧。

大口吸气,冷静一下。 想想指针的数据类型。 int是数据类型,*int是指针类型。那么一个保存指针类型的指针,也就是叠加一个*int呗。因此B就变成了:

var b **int

如果还有C呢,自个儿写代码试试。指针链就这么点需要注意的,如果还不明白,手敲以下代码跑一下:

package main

import "fmt"

func main() {
var a int
var ptr *int
var pptr **int a = 3000
ptr = &a pptr = &ptr fmt.Printf("Value of a = %d\n", a )
fmt.Printf("Value available at *ptr = %d\n", *ptr )
fmt.Printf("Value available at **pptr = %d\n", **pptr)
}

坚持住,已经99%了。看完下面,进度条就跑到100%了(绝不像X雷,卡你99%)。

指针在函数参数中的使用

这里只是为了凑齐那1%,其实内容已经在:二式<维密>一节,提到过了。只要看准参数是要值传递还是引用传递,就没问题。

最后忍不住,来点扯淡的话。 追求知识无可厚非,但现实社会中,金钱是成功与否的唯一标准。所以在学习工作过程中,还是要时时刻刻考虑如何依靠知识来变现。没有人看的书,等于厕纸。无法变现的知识,等于白学。如果你有好的想法,或者闲得无聊欢迎发邮件给我(ztao8607@gmail.com),没准火花就迸发于扯淡之间。

分水岭 golang入坑系列的更多相关文章

  1. 入坑第二式 golang入坑系列

    史前必读: 这是入坑系列的第二式,如果错过了第一式,可以去gitbook( https://andy-zhangtao.gitbooks.io/golang/content/ )点个回放,看个重播.因 ...

  2. 初生牛犊不怕虎 golang入坑系列

    读前必读,下面所有内容都是来自这里. 放到这里的目的,就是为了比对一下,哪里的读者多.平心而论,同样的Markdown,博客园排版真心X看,怎么瞅怎么X看.(X := '难' || X :='耐' | ...

  3. 维多利亚的秘密 golang入坑系列

    原文在gitbook,字字原创,版权没有,转载随意. 在写本文的前一天,2017维密在上海开始了. 为了纪念屌丝界的盛世,特为本节起名维多利亚的秘密.现在的社会,要想出名只有抓眼球.所以写份技术文章, ...

  4. 准备冲锋 golang入坑系列

    史前摘要: 本来想写读前必读,但连续几篇博文都写读前必读,感觉就没有了新意. 所以换成史前摘要,反正是一个意思. 此摘要的目的仍然是提醒点击而来的同学,本系列最新文章在这里.放到博客园的目的是为了方便 ...

  5. 坐忘峰 golang入坑系列

    读前必读: 本文写于20日,首发于gitbook. 迟到的是日期,没变的是内容. 点击进入 https://andy-zhangtao.gitbooks.io/golang/content/ 可以看到 ...

  6. 崩溃 golang入坑系列

    早上(11.30)收到邮件,Vultr东京机房网络故障.当时搭建SS时,考虑到了机房故障.所以特意分出了日本和香港两条线路.但千算万算,忘记数据库还在东京机房中. 现在网络故障,SS服务器无法读取数据 ...

  7. 乐呵乐呵得了 golang入坑系列

    开场就有料,今天返回去看了看以前的文章,轻松指数有点下降趋势.一琢磨,这不是我的风格呀.一反思,合着是这段时间,脑子里杂七杂八的杂事有点多,事情一多,就忘了快乐.古话说得好:愁也一天,乐也一天,只要还 ...

  8. 来自朝鲜的问候 golang入坑系列

    鸿渐于陆 本想着写满十八式,但按照目前的进度来看,是很难凑够十八式了.所以还是那句话,量力而行,适可而止.能写多少就写多少,我没法保证看完这本golang脱口秀,一定能成为golang大拿.但入了门, ...

  9. SEO是件贼有意思的事情 golang入坑系列

    这两天迷上了SEO.真心看不起百度的竞价排名,但作为一个商业网站,赚钱是一件无可厚非的事情.只做活雷锋,没有大金主是做不长的.做完功课后,发现百度和google的SEO策略又不相同,几乎是无法通用.百 ...

随机推荐

  1. 人工智能 tensorflow框架-->简介及安装01

    简介:Tensorflow是google于2015年11月开源的第二代机器学习框架. Tensorflow名字理解:图形边中流动的数据叫张量(Tensor),因此叫Tensorflow 既 张量流动 ...

  2. 新博客,新开始-从Chrome浏览器奔溃说起

    新博客,新开始 今天是2015-04-09,昨天新开的博客,今天在这写上一段,算是立个标记,好留以后拿来回溯吧. 不知道是谁跟我说的,坚持写博客是个好习惯,也能帮助自己总结经验,提高技术.也许大概可能 ...

  3. 解决Ubuntu中phpmyadmin对数据上传上限2M

    本文部分参考自:http://www.myhack58.com/Article/sort099/sort0102/2011/29396.htm 原文有少量错误或者过时的(相对于ubuntu15来说)内 ...

  4. ARM总线方面知识

    AMBA简介 随着深亚微米工艺技术日益成熟,集成电路芯片的规模越来越大.数字IC从基于时序驱动的设计方法,发展到基于IP复用的设计方法,并在SOC设计中得到了广泛应用.在基于IP复用的SoC设计中,片 ...

  5. LeetCode 594. Longest Harmonious Subsequence (最长的协调子序列)

    We define a harmonious array is an array where the difference between its maximum value and its mini ...

  6. LeetCode 152. Maximum Product Subarray (最大乘积子数组)

    Find the contiguous subarray within an array (containing at least one number) which has the largest ...

  7. 为Lua5.3编写C模块简单示例

    为Lua5.3编写C模块简单示例 一.编译安装Lua5.3 MSVC 命令行安装脚本: @echo off md bin md lib md include cd src cl /c /nologo ...

  8. c++ 类覆盖方法中的协变返回类型

    c++ 类覆盖方法中的协变返回类型 在C++中,只要原来的返回类型是指向类的指针或引用,新的返回类型是指向派生类的指针或引用,覆盖的方法就可以改变返回类型.这样的类型称为协变返回类型(Covarian ...

  9. sql语句练习题及答案

    表结构 创建表数据    SET NAMES utf8;    SET FOREIGN_KEY_CHECKS = 0;    -- ----------------------------    -- ...

  10. mongodb集群【】

    参考 http://www.jianshu.com/p/2825a66d6aed http://www.cnblogs.com/huangxincheng/archive/2012/03/07/238 ...