一个有关Golang变量作用域的坑

临近下班前编写和调试一段Golang代码,但运行结果始终与期望不符,怪异的很,下班前依旧无果。代码Demo如下:

//testpointer.go
package main

import (
        "fmt"
)

var p *int

func foo() (*int, error) {
        var i int = 5
        return &i, nil
}

func bar() {
        //use p
        fmt.Println(*p)
}

func main() {
        p, err := foo()
        if err != nil {
                fmt.Println(err)
                return
        }
        bar()
        fmt.Println(*p)
}

这段代码原意是定义一个包内全局变量p,用foo()的返回值对p进行初始化,在bar中使用p。预期结果:bar()和main()中均输出5。但编译执行后的结果却是:

$go run testpointer.go
panic: runtime error: invalid memory address or nil pointer dereference
[signal 0xb code=0x1 addr=0x0 pc=0x20d1]

goroutine 1 [running]:
main.bar()
    /Users/tony/Test/Go/testpointer.go:17 +0xd1
main.main()
    /Users/tony/Test/Go/testpointer.go:26 +0x11c

goroutine 2 [runnable]:
runtime.forcegchelper()
    /usr/local/go/src/runtime/proc.go:90
runtime.goexit()
    /usr/local/go/src/runtime/asm_amd64.s:2232 +0×1

goroutine 3 [runnable]:
runtime.bgsweep()
    /usr/local/go/src/runtime/mgc0.go:82
runtime.goexit()
    /usr/local/go/src/runtime/asm_amd64.s:2232 +0×1

goroutine 4 [runnable]:
runtime.runfinq()
    /usr/local/go/src/runtime/malloc.go:712
runtime.goexit()
    /usr/local/go/src/runtime/asm_amd64.s:2232 +0×1
exit status 2

晚饭后,继续调试这段代码。怎么还crash了!代码看似半点问题都没有,难道是Go编译器的问题,我用的可是最新的1.4,切换回1.3.3,问题依旧啊。看来还是代码的问题,但问题在哪里呢?加上些打印语句再看看:

func bar() {
        //use p
        fmt.Printf("%p, %T\n", p, p) //output:
0x14dc80, 0×0, *int
        fmt.Println(*p) //Crash!!!
}

func main() {
        fmt.Printf("%p, %T\n", p, p) //output: 0x14dc80, 0×0, *int
        p, err := foo()
        if err != nil {
                fmt.Println(err)
                return
        }
        fmt.Printf("%p, %T\n", p, p) //output: 0x2081c6020, 0x20818a258, *int
        bar()
        fmt.Println(*p)
}

通过打印输出,发现从foo函数中返回的p(0x2081c6020)与全局变量的p(0x14dc80)居然不是一个地址,也就是说不是一个变量。而且 从bar()中的调试输出来看,全局变量p在foo函数返回时并未被赋值为foo中变量i的地址,而依然是一个nil值,从而导致程序Crash。

好了,废话不说了,该是揭晓真相的时候了。问题就在于":="。在main这个作用域中,我们使用了

p, err := foo()

最初的理解是golang会定义新变量err,p为初始定义的那个全局变量。但实际情况是,对于使用:=定义的变量,如果新变量p与那个同名已定义变量 (这里就是那个全局变量p)不在一个作用域中时,那么golang会新定义这个变量p,遮盖住全局变量p,这就是导致这个问题的真凶。

我们将main函数改为:

func main() {
        var err error
        p, err = foo()
        if err != nil {
                fmt.Println(err)
                return
        }
        bar()
}

则执行结果就完全符合预期了。

2014小结

2014年的最后一个工作日,这里写下有关2014年的一份小结。

年终总结本无固定格式,但写了若干年后,便有了自己的格式。但今年不打算遵循这个格式了,跳出自己的舒适区,随意写写。

2014年12月底,随着亚航QZ8501航班的最后一掉,航空史上都为数不多的灾难年终于画上了句号,留给人们的是久久的惊恐不安,留给遇难者 家属们的是无法释怀的悲伤。2014年12月31日15点,随着A股上证指数最后一个交易日收涨68.86点,稳稳站上3200点,让广大股民们 看到了2015年牛市持续赚钱的希望。不知为何,这个世界几乎总是同时上演着冰与火两种剧本。

短信与微信(包括其他X信)的博弈亦是如此。

短信,这一红极一时的让移动运营商赚得盆满钵满的廉价沟通工具如今却早已成明日黄花。不妨打开手机,翻看一下你的手机通信录,短信列表中是不是除 了验证码(登录、支付业务),就是各种营销垃圾广告,或者是移动运营商自有的客服信息呢。我相信我的情况应该可以代表广大群众了。

随着微信今年推出“企业号”,微信几乎完成了对短信的业务合围:

点对点短信 vs. 联系人、朋友圈、群
SP短信    vs. 订阅号、服务号
行业短信  vs. 服务号、企业号 (营销、售后、内部OA、CRM等)

今年年初招商银行信用卡将300以内的消费提醒短信取消,改为微信提醒,其实就是一个看高微信,看空短信的行为。只是考虑到到达率(用户未开网络时),没 有将大额消费全部转到微信上,而是短信和微信都做提醒。一旦无线网络接入、资费门槛下降、网络速度提升、终端实时在线不再是问题,达到率也将 不是问题时,微信会对短信发起最后的总攻。

这么对比其实也不公平,因为短信和微信本不是一个重量级的对手。从出生的那天起,微信就被赋予了崇高的使命,非短信可比。微信试图连接一切,做统 一入口,建立庞大生态圈;而短信仅仅是一个通道工具罢了。

面对移动短信市场的衰败,移动运营商也在挣扎,也在试图翻盘,或至少平起平坐,但就我了解到的移动运营商产品开发与运营的风格,想和互联网巨头T 掰手腕,下场必输无疑。中国移动年初也蛮拼的,喊出了"RCS(融合通信)"与微信抢手机社交入口,但这都到了2014最后一天了,RCS依旧不见踪 影。

短信免费或退出历史舞台就像周鸿祎在其书《周鸿祎自述:我的互联网方法论》中说的那样是“趋势”,不可违!

我们就是为中国移动短信业务提供服务端软件和方案的。短信若是没了(或变成鸡肋),我们干啥?冰冷的现实摆在大家面前,领导跟我 们说:“转型”。

2014年,至少我们依旧在转型中。老板们把“转型”依旧约束在“移动运营商”这棵大树下面,这让我们转的不那么纯粹,有些拖泥带水,可持续盈利 的业务方向并不明显。从目前来看,今年收入依旧靠传统业务渠道获得。

虽说要“转型”,但领导今年给我的任务却是做好守门员,守住现有市场份额,保证产品线上无事。这并非如我所愿,在一个业务线耕耘多年,业务和技术 能力均到达了天花板,对我个人来说,这不是一个很好的发展规划。但考虑到下面的技术负责人、员工在技术和业务火候儿还欠缺那么一些,我答应了留 守,但会投入部分精力做个人技术转型储备。

业务的转型需要技术做支撑,局限于传统后台服务系统的我们需要张开怀抱,拥抱那些“流行”的新玩意儿。我首先试水!从2014年我的博客中你也许 可以看得出来我试水过的技术,我在尝试跳出自己的各种舒适区,向一些近两年兴起的、将来比较有前途的技术方向靠拢,学习移动互联网的思维和潮流。

上半年曾尝试过终端产品开发的技术,还为此购入若干数码装备,但试过后才发现这仍然不是我的主菜,就和10年前Windows GUI程序开发不是我的菜一样。但这个过程并非没有收获,未来任何业务不与终端开发打交道是不可能的,这个接触过程让我了解到了终端开发的重点和难点,于 是总结经验,整理教训。

正当准备调整方向、重新上路之际,家里出现重大变故,耗费了我整整1个多月的时间,一切几乎都停滞了,直到10月份我才渐渐重新进入状态。

在公司内部技术社区看到公司CTO的一篇文章,讲述移动互联网正在由消费者驱动向企业驱动转变(来自麦肯锡报告),结合微信推出企业号、用友软件的转型来 看(今天听说用友软件更名为“用友网络”了,决心向互联网转型),这个趋势也是我比较认同的,这个方向以及相关技术也是我在正在涉猎以及即将涉猎的。不过 关于企业互联网服务以及平台,自己的相关业务经验、技术和积累还是甚少,征途必然坎坷,自己还需“拼”一下!关于微信这个平台,这个入口,它是腾讯未来战 略的核心,靠着腾讯这棵大树,至少未来几年发展应该还是不错的。

公司的大BOSS这两年一直提倡“创业者的精神",学会在逆境中成长,在困境中成功。但作为在短信这个行业内浸淫了十多年的部门,我们不免产生一些惰性, 更愿意躺在现有的温床上“享受生活”,立足于现有的平台做舒服的事情。经历过2014年的严峻形势,现在的我们应该清醒的认识到这样的舒服生活,温床和平 台都可能将远离我们。如果我们再不主动站起来,我们将再无力站起了。

2014年在个人发展方面做出了“妥协”,2015我打算轻装前行,这对我、对团队成员的成长都是有好处的。年底给领导发总结时,已经和领导书面提出退出 当前业务线的想法。虽然目前还没有收到回复,不过无论怎样,我都坚定了决心,自己作为这个产品线的负责人,已经起不到领路的作用了,是时候退出了。

2015,给自己的关键字是“创业”。《精益创业》一书中作者似乎有这样一句话:“你不一定非要在车库里折腾才算是创业”,在企业内部也可以“创业”,为创造某种新产品或新服务为目的而组建的一个团队或组织内的人都是“创业者”。

以往年份的小结,我总会总结一些数据,比如blog文章、读过多少本书等等。但今年这些数据就不统计了,自己对自己的考核指标"KPI"有所调整,以前哪些指标已经不算数了,列出也就无意义了。

2014这一年,LP给了我很大压力!我能理解,她期望我能取得更大的成功。这让我“亚历山大”啊,这回可是真的。

要说新年的愿望是什么?希望2015年年末时能为自己2015年的所作所为,所取得的进步和成果点个赞

如发现本站页面被黑,比如:挂载广告、挖矿等恶意代码,请朋友们及时联系我。十分感谢! Go语言第一课 Go语言精进之路1 Go语言精进之路2 Go语言编程指南
商务合作请联系bigwhite.cn AT aliyun.com

欢迎使用邮件订阅我的博客

输入邮箱订阅本站,只要有新文章发布,就会第一时间发送邮件通知你哦!

这里是 Tony Bai的个人Blog,欢迎访问、订阅和留言! 订阅Feed请点击上面图片

如果您觉得这里的文章对您有帮助,请扫描上方二维码进行捐赠 ,加油后的Tony Bai将会为您呈现更多精彩的文章,谢谢!

如果您希望通过微信捐赠,请用微信客户端扫描下方赞赏码:

如果您希望通过比特币或以太币捐赠,可以扫描下方二维码:

比特币:

以太币:

如果您喜欢通过微信浏览本站内容,可以扫描下方二维码,订阅本站官方微信订阅号“iamtonybai”;点击二维码,可直达本人官方微博主页^_^:
本站Powered by Digital Ocean VPS。
选择Digital Ocean VPS主机,即可获得10美元现金充值,可 免费使用两个月哟! 著名主机提供商Linode 10$优惠码:linode10,在 这里注册即可免费获 得。阿里云推荐码: 1WFZ0V立享9折!


View Tony Bai's profile on LinkedIn
DigitalOcean Referral Badge

文章

评论

  • 正在加载...

分类

标签

归档



View My Stats