声明、定义、初始化,这三者的关系和区别
-
声明(Declaration):
- 声明是告诉编译器变量的名称和类型,但不分配内存或赋值。
- 在Go中,可以使用 var 关键字来声明变量。
例如:
1
var x int
-
定义(Definition)
- 定义包括声明,并且为变量分配内存空间。
- 在Go中,声明和定义通常是同时进行的。
1
var x int // 这既是声明也是定义
-
初始化(Initialization):
- 初始化是在定义变量的同时为其赋予一个初始值。
- 在Go中,可以在声明时直接初始化,也可以使用短变量声明来同时完成声明、定义和初始化。
1 2
var x int = 5 // 声明、定义并初始化 y := 10 // 短变量声明,同时完成声明、定义和初始化
关系和区别:
- 在Go中,声明和定义通常是同时发生的。当你声明一个变量时,Go会自动为其分配内存。
- 初始化是可选的。你可以先声明和定义一个变量,稍后再初始化它。
- 使用短变量声明(:=)可以一步完成声明、定义和初始化。
- 在某些其他语言中(如C),声明和定义可能是分开的,特别是在处理函数原型或外部变量时。
命名
Go 语言中的函数名、变量名、常量名、类型名、语句符号和包名,必须遵循一个简单的命名规则:由数字、字母、下划线组成,且必须以字母或下划线开头。
Go语言中类似if和switch的关键字有25个;关键字不能用于自定义名字,只能在特定语法结构中使用。
|
|
此外,还有大约30多个预定义的名字,比如int和true等,主要对应内建的常量、类型和函数。
|
|
如果一个名字在函数内部定义,那么它只能在函数内部有效。如果在函数外部定义,那么将在当前包内的所有文件中都可以访问。
名字的开头字母的大小写决定了名字在包外的可见性。如果一个名字是大写字母开头的(函数外部定义的包级名字),那么它是可以被外部的包访问。
名字的长度没有逻辑限制,但是Go语言的风格是尽量使用短小的名字,对于局部变量尤其是这样;你会经常看到i之类的短名字,而不是冗长的theLoopIndex命名。通常来说,如果一个名字的作用域比较大,生命周期也比较长,那么用长的名字将会更有意义。
在习惯上,Go语言程序员推荐使用 驼峰式 命名,当名字由几个单词组成时优先使用大小写分隔,而不是优先用下划线分隔。
声明
Go 语言属于静态编译型语言:使用变量之前需要先进行变量的声明,声明后必须被使用。
Go 语言常见的变量声明形式:
|
|
💡在变量声明的形式上应该尽量保持项目范围内一致
Go 语言可以分为两类变量:
- 包级别变量:在 package 级别可见。如果是导出变量,则该包级变量也可以被视为全局变量。
- 局部变量:函数或方法体内部声明的变量,仅在函数或方法体内部可见。
包级变量的声明
包级变量只能使用带 var 关键字的变量声明形式。
-
声明的同时显式初始化
1
var variableName = InitExpression
Go编译器会自动根据等号右侧的InitExpression表达式求值的类型确定左侧所声明变量的类型。
如果InitExpression采用的是不带有类型信息的常量表达式,比如下面的语句
1 2
var a = 255 var f = 3.14
则包级变量会被设置为常量表达式的默认类型:a 为 int,b 为 float64。
如果InitExpression采用的是不带有类型信息的常量表达式,比如下面的语句:
1 2 3 4 5 6 7
// 第一种 var a int32 = 18 var f float = 3.14 // 第二种 var a = int32(18) var f = float(3.14)
从声明一致性的角度出发,Go语言官方更推荐后者,这样就统一了接受默认类型和显式指定类型两种声明形式。尤其是在将这些变量放在一个var块中声明时,我们更青睐这样的形式:
1 2 3 4
var ( a = 18 f = float32(3.14) )
而不是下面这种看起来不一致的声明形式:
1 2 3 4
var ( a = 18 f float32 = 3.14 )
-
声明但延迟初始化
对于声明时并不显式初始化的包级变量,我们使用最基本的声明形式:
1 2
var a int32 var f float64
Go 语言会让这些变量拥有初始的零值。
-
声明聚类和就近原则
声明聚类的意思是:将同一类的变量声明放在一个var块中,将不同类的声明放在不同的var块中;或者将延迟初始化的变量声明放在一个var块,而将声明并显式初始化的变量放在另一个var块中。
就近原则指的是:变量声明尽可能在靠近第一次使用变量的位置声明该变量。但是如果一个包级变量被内部多次使用,那么还是放在头部比较合适。
局部变量的声明
与包级变量相比,局部变量多了一个短变量声明的形式。这也是局部变量采用最多的一种方式。
-
对于延迟初始化的局部变量声明,采用带有var关键字的声明形式
-
对于声明且显式初始化的局部变量,建议使用短变量声明形式
1 2 3
a := 17 f := 3.14 s := "hello, gopher!"
对于不接受默认类型的变量,依然可以使用短变量声明形式,只是在“:=”右侧要进行显式转型:
1 2 3
a := int32(17) f := float32(3.14) s := []byte("hello, gopher!")
常量
Go 语言中常量有关的声明通过 const 来进行。
例如:
|
|
上面这段代码通过const声明了一组常量,这种对常量的声明方式仅仅是Go标准库中的少数个例,绝大多数情况下,Go常量在声明时并不显式指定类型,也就是说使用的是无类型常量(untyped constant)。比如:
|
|
所有常量表达式的求值计算都可以在编译期而不是在运行期完成,这样既可以减少运行时的工作,也能方便编译器进行编译优化。当操作数是常量时,在编译时也能发现一些运行时的错误,例如整数除零、字符串索引越界等。
iota
iota
是go语言的常量计数器,只能在常量的表达式中使用。
iota
在const关键字出现时将被重置为0。const中每新增一行常量声明将使iota
计数一次(iota可理解为const语句块中的行索引)。 使用iota能简化定义,在定义枚举时很有用。
例:
|
|
使用_
跳过某些值
|
|
iota
声明中间插队
|
|
定义数量级 (这里的<<
表示左移操作,1<<10
表示将1的二进制表示向左移10位,也就是由1
变成了10000000000
,也就是十进制的1024。同理2<<2
表示将2的二进制表示向左移2位,也就是由10
变成了1000
,也就是十进制的8。)
|
|
多个iota
定义在一行
|
|
类型零值
当通过声明或调用new为变量分配存储空间,或者通过复合文字字面量或调用make创建新值,且不提供显式初始化时,Go会为变量或值提供默认值。
Go语言中的每个原生类型都有其默认值,这个默认值就是这个类型的零值。
- 整型:
0
- 浮点类型:
0.0
- 布尔类型:
false
- 字符串类型:
""
- 指针、interface、slice、channel、map、function:
nil
- Go的零值初始是递归的,即数组、结构体等类型的零值初始化就是对其组成元素逐一进行零值初始化。