Tony Bai · Go 语言第一课
Tony Bai
资深架构师,tonybai.com 博主
21492 人已学习
新⼈⾸单¥59
登录后,你可以任选4讲全文学习
课程目录
已完结/共 59 讲
开篇词 (1讲)
结束语 (1讲)
Tony Bai · Go 语言第一课
15
15
1.0x
00:00/00:00
登录|注册

10|变量声明:静态语言有别于动态语言的重要特征

统一项目范围内的变量声明形式
考虑多方面因素
声明聚类
分支控制时使用短变量声明形式
延迟初始化
就近原则
声明聚类
延迟初始化
适用于分支控制
最简化的变量声明形式
显式类型转型
Go编译器自动推导类型
标准范式
变量声明块
初值
类型
变量名
var关键字
动态语言在运行时确定变量边界
静态语言需明确变量边界
思考题
小结
局部变量的声明形式
包级变量的声明形式
短变量声明
省略类型信息的声明
Go语言的变量声明方法
静态语言 vs 动态语言
变量声明

该思维导图由 AI 生成,仅供参考

你好,我是 Tony Bai。
今天我们将深入 Go 语法细节,学习静态语言有别于动态语言的一个重要特征:变量声明。那么变量声明究竟解决的是什么问题呢?我们先从变量这个概念说起。
在编程语言中,为了方便操作内存特定位置的数据,我们用一个特定的名字与位于特定位置的内存块绑定在一起,这个名字被称为变量
但这并不代表我们可以通过变量随意引用或修改内存,变量所绑定的内存区域是要有一个明确的边界的也就是说,通过这样一个变量,我们究竟可以操作 4 个字节内存还是 8 个字节内存,又或是 256 个字节内存,编程语言的编译器或解释器需要明确地知道。
那么,编程语言的编译器或解释器是如何知道一个变量所能引用的内存区域边界呢?
其实,动态语言和静态语言有不同的处理方式。动态语言(比如 Python、Ruby 等)的解释器可以在运行时通过对变量赋值的分析,自动确定变量的边界。并且在动态语言中,一个变量可以在运行时被赋予大小不同的边界。
而静态编程语言在这方面的“体验略差”。静态类型语言编译器必须明确知道一个变量的边界才允许使用这个变量,但静态语言编译器又没能力自动提供这个信息,这个边界信息必须由这门语言的使用者提供,于是就有了“变量声明”。通过变量声明,语言使用者可以显式告知编译器一个变量的边界信息。在具体实现层面呢,这个边界信息由变量的类型属性赋予。
确认放弃笔记?
放弃后所记笔记将不保留。
新功能上线,你的历史笔记已初始化为私密笔记,是否一键批量公开?
批量公开的笔记不会为你同步至部落
公开
同步至部落
取消
完成
0/2000
荧光笔
直线
曲线
笔记
复制
AI
  • 深入了解
  • 翻译
    • 英语
    • 中文简体
    • 中文繁体
    • 法语
    • 德语
    • 日语
    • 韩语
    • 俄语
    • 西班牙语
    • 阿拉伯语
  • 解释
  • 总结

Go语言变量声明形式的总结: Go语言中的变量声明形式包括通用的声明形式、省略类型信息的声明和短变量声明。对于包级变量,可以使用省略类型信息的“语法糖”格式进行声明,也可以采用通用的变量声明形式进行延迟初始化。推荐将相似类型的变量声明放在一个var变量声明块中,以提升代码可读性。对于局部变量,常用的声明形式是短变量声明形式,尤其在分支控制语句中使用广泛。文章还强调了就近原则,建议在靠近第一次使用变量的位置声明变量。总的来说,本文通过对Go语言中静态语言变量声明的重要特点和多种变量声明形式的使用方法进行详细介绍,帮助读者快速了解Go语言中变量声明的特点和最佳实践。 在这一讲中,我们学习了多种Go变量声明的方法,还学习了不同类型Go变量可以采用的变量声明形式和惯用法,以及一些变量声明的最佳实践原则。 具体来说,Go语言提供了一种通用变量声明形式以及两种变量声明“语法糖”形式,而且Go包级变量和局部变量会根据具体情况选择不同的变量声明形式。良好的变量声明实践需要我们考虑多方面因素,包括明确要声明的变量是包级变量还是局部变量、是否要延迟初始化、是否接受默认类型、是否是分支控制变量并结合聚类和就近原则等。 总的来说,本文通过对Go语言中静态语言变量声明的重要特点和多种变量声明形式的使用方法进行详细介绍,帮助读者快速了解Go语言中变量声明的特点和最佳实践。

仅可试看部分内容,如需阅读全部内容,请付费购买文章所属专栏
《Tony Bai · Go 语言第一课》
新⼈⾸单¥59
立即购买
登录 后留言

全部留言(44)

  • 最新
  • 精选
  • 珅珅君
    https://blog.go-zh.org/gos-declaration-syntax 其实官方有给出解释,原因简单来说就是和C相比,在当参数是指针的复杂情况下,这种声明格式会相对好理解一点,链接里面有详细解释

    作者回复: 正解。

    2021-11-03
    3
    36
  • jc9090kkk
    我认为类型放在变量名的后面是为了提高代码可读性的,我个人认为golang的设计者在设计go语言的时候大概率参考了c语言的语法,针对存在大量变量需要声明的场景下,go的变量类型放在变量名的后面可读性会高很多,不容易引起类型定义的混乱。 比如: c: int (*fp)(int a, int b); int (*fp)(int (*ff)(int x, int y), int b) go: f func(func(int,int) int, int) int f func(func(int,int) int, int) func(int, int) int 相比之下:go的清晰易懂!而c的层层嵌套,难以直接看出是什么类型的指针 另外的一点我认为也是大多编程语言开发者的习惯,语法定义后紧接着就是变量名应该是很多编程者的开发习惯,如果var后面跟着int类型这样的语法会让人很别扭,至少对我来讲是这样。

    作者回复: ✅。答案非常细致。刚开始学go时,也不适应,后来适应后,就像你说的,在一些复杂函数/方法的声明中,类型在后面的方式更容易理解。

    2021-11-03
    26
  • hhzzer
    想问一下老师,为啥go产生了这么多种变量声明方式?依照一个问题只用一种方法解决的原则,如果你重新设计,会选择保留哪一种申明方式呢?

    作者回复: 1. 保留这么多变量声明方式的原因,Go语言设计者们并未在公开场合说明过。这个我也无不能不负责任的揣测^_^。 2. rob pike只是说过,如果有机会从头设计go,他不会再提供这么多变量声明方式了。但没说要保留哪一种。如果是我设计,我可能会保留var a int以及短变量声明。

    2021-11-03
    10
  • 修文
    就近原则介绍的例子中提到,ErrNoCookie 只被用在了 Cookie 方法中。有一个疑问是,为什么 ErrNoCookie 不定义在方法中,而是定义在方法外呢?

    作者回复: 后面会降到,go的错误处理是基于值比较的。之所以将ErrNoCookie放在包级而不是函数/方法内部,是因为需要将ErrNoCookie暴露给包的使用者。这样包的使用者才能通过ErrNoCookie与方法返回的错误值进行比较以判断是否是这个错误。

    2021-11-10
    3
    6
  • TonyGao
    就是既想要静态语言的严格,又想取动态语言的灵活的折中方案。

    作者回复: 选Go就对了:)

    2021-11-03
    6
  • Calvin
    学习总结笔记: 1、静态类型语言编译器必须明确知道一个变量的边界,一般通过“变量声明”,由变量的类型属性显式地告知; 2、var a int = 10,Go 声明变量的 4 个部分:var 表明这是一个“变量”,a 是“变量名”,int 是变量的类型,10 是变量的初始化值; 1) var a int // 声明变量 a,不指定初始化值(编译器会赋予其类型零值,int 默认零值是 0) 2) var ( a int = 128 s string = "hello" ) // 变量声明块 3) var a, b, c int = 5, 6, 7 // 在一行变量声明中同时声明多个变量(相同类型) 4) var a, b, c = 3, 'b', "ccc" // 在一行变量声明中同时声明多个变量(不同类型) 5) var ( a, b, c int = 1, 2, 3 d, e, f rune = 'D', 'E', 'F' ) // 变量声明块,在一行变量声明中同时声明多个变量 6) var a = 123 // 编译器根据右侧变量初始化值自动推导出变量的类型为 int(整型值的默认类型) 7) var b = int32(123) // 不接受默认推导类型 int,显式地为变量指定类型为 int32 8) c := 123 // 短变量声明(省掉 var 及类型信息,使用专用的“:=”) 9) d, e, f := 123, 'A', "bcd" // 短变量声明,在一行变量声明中同时声明多个变量 3、Go 变量可以分为两类: 1) 包级变量:包级别可见的变量,如果是导出变量(大写字母开头的),它也可被视为“全局变量”; 2) 局部变量:仅在函数或方法体内可见。 4、包级变量 - 只能使用带有 var 关键字的变量声明形式,不能使用短变量声明形式,但在形式细节上可以有一定灵活度: 1) 声明并同时显式初始化:var EOF = errors.New("EOF"),注意“声明一致性”原则,即如既有 var a = 123 这种使用默认类型的形式,又有 var b int32 = 321 或 var b = int32(321) 这种显式指定类型的形式,Go 更推荐使用后者; 2) 声明但延迟初始化:var a int32。 5、提升代码可读性: 1) 声明聚类:将同一类的变量声明放在一个 var 变量声明块中,不同类的声明放在不同的声明块中,如: var ( netGo bool netCgo bool ) // 声明但延迟初始化 var ( aLongTimeAgo = time.Unix(1, 0) noDeadline = time.Time{} noCancel = (chan struct{})(nil) ) // 声明并同时显式初始化 2) 就近原则:尽可能在靠近第一次使用变量的位置声明这个变量,实际上也是对变量的作用域最小化的一种实现手段,如果一个包级变量在包内部被多处使用,则还是放在源文件头部比较适合。 6、局部变量 - 和包级变量相比,多了一种短变量声明形式,这是局部变量特有的一种变量声明形式,也是局部变量采用最多的一种声明形式: 1) 对于延迟初始化的局部变量声明,我们采用通用的变量声明形式:var err error 2) 对于声明且显式初始化的局部变量,建议使用短变量声明形式:a := 123,f := float32(3.14) 3) 尽量在分支控制时使用短变量声明形式,这种融合的使用方式也体现出“就近”原则,让变量的作用域最小化; 4) 如果在声明局部变量时遇到了适合聚类的应用场景,应该毫不犹豫地使用 var 声明块来声明多于一个的局部变量: func (r *Resolver) resolveAddrList(ctx context.Context, op, network, addr string, hint Addr) (addrList, error) { ... ... var ( tcp *TCPAddr udp *UDPAddr ip *IPAddr wildcard bool ) ... ... } 7、Go 语言变量声明中,类型是放在变量名的后面的,这样做的好处:https://go.dev/blog/declaration-syntax

    作者回复: 赞👍

    2021-11-07
    2
    4
  • CLMOOK🐾
    老师好,按我的理解,显式指定变量类型,在编译器的执行效率上,是比不指定类型,或进行显式转换类型要高的,因为省略了编译器根据值去判断类型,或做类型转换的开销,但由于go程序都是编译后运行二进制文件,所以编译过程的开销就没那么重要或者说可以忽略,然后可以更多的考虑使用便利性和代码可读性。

    作者回复: 👍

    2022-09-27归属地:北京
    3
  • Vfeelit
    a, e := a() 然后 b, e := b() 这里的 e 算不算声明两次?

    作者回复: 同一代码块内先后放置上面两个语句,e不算声明两次。你可以输出e的地址证明这点。两个e的地址是相同的。

    2022-01-09
    3
    3
  • liaomars
    变量名放在前面应该是考虑代码的可读性才这样设计的吧

    作者回复: 大约是如此。可以看看官方解释:https://go.dev/blog/declaration-syntax

    2021-11-03
    3
  • 罗杰
    C 和 C++ 都是类型在前,变量名在后,记得非常清楚 CCMouse 老师在视频课上提到这才是正确的方法,但到现在其实我还没太明白原因。不过在 Rust 中采用了和 Go 相同的方式。所以希望老师解答一下原因

    作者回复: 其实官方早有正解,https://go.dev/blog/declaration-syntax 。我个人以前是C程序员,在读懂那些复杂函数的原型时,颇为费力。换做go之后,再未被这类问题困扰过:)

    2021-11-03
    3
收起评论
显示
设置
留言
44
收藏
沉浸
阅读
分享
手机端
快捷键
回顶部