4.变量与声明
1. var 语法
var 的 语法为
举个例子,声明一个类型为int
(整数类型),值为 1 的变量 num:
Go 声明变量本质上是在内存中开辟一块空间,那开辟多大的空间呢? int
是一个特殊的类型,在 32 位系统中占 32bit, 在 64 位系统占 64bit。 内存中以字节(byte)为基本单位,1byte = 8bit。
'var num int = 1' 在内存的占用
系统位数 | 类型 | 占用内存 | 值 | 二进制 | 高位填充 0 |
---|---|---|---|---|---|
32 位 | int | 32 bit 即 4 byte | 1 | 1 | 00000000 00000000 00000000 00000001 |
64 位 | int | 64 bit 即 8 byte | 1 | 1 | 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000001 |
我的系统是 64 位,就会开辟 8byte 的内存,并把值1
存到这个空间。然后给这块空间取个名字叫num
。
我们可以通过num
变量读出内存中的值,也可以赋值给num
改变内存中的值。num
就是这块空间的代言人,我们通过它操作内存的读和写。
- 缺省值
如果还不确定这个变量要赋什么值,可以缺省, 例如
这样,Go 会在声明时自动赋给变量零值,int
(整数)的零值就是 0
- 省略类型
如果知道这个变量要赋予什么值,可以省略类型。
Go 会把字面量1
自动推断为类型int
什么是字面量?
字面量就是源代码中的值,例如
1
,"hello"
就是字面量,每个字面量都有默认的类型,1
是int
类型,"hello"
是string
类型。
与字面量相对的是变量,例如
num
不是字面量。
- 同时声明多个变量
这在交换两个变量时非常有用,例如,交换 a,b 的值
小技巧
当我们声明一组相关的变量时,可以使用块包裹
这样可以提高代码可读性,其他关键字import
, type
,const
也适用这个规则。
2. 短声明语句
除了使用var
声明,我们还可以使用更简单的短声明语句:=
等价于
:=
语句很方便,但只能在函数内使用。
我们使用得最多的就是:=
,因为大部分声明都是在函数里。
什么是包级变量
包级变量就是在包下直接声明的变量,而不是在函数中。如上述代码的num2
, main
就是包级变量,而num3
是 main 函数内的变量。在 Go 中,函数就是值,能赋值给变量,这部分将会在函数章节讲述。
:=
也可以同时声明多个变量
3. 强类型语言
强类型就是,一个变量一旦声明,它的类型就已确定,并且不能改变。例如:
num 是int
类型,不能把string
类型的"hello"赋值给它,这会导致编译期错误。
什么是编译期错误?
-
编译期错误: 编译期间发生的错误,无法完成编译。
-
运行时错误: 程序能编译成功,但运行过程中发生错误。
4. 不允许变量声明却不使用
一个变量声明却不使用,就相当于你买了个手机却不用,如果不用,就没有必要买。同样,如果你不使用一个变量就没必要声明(创建)它。
运行该程序,出现编译期错误:
空白标识符_
当我们不想使用这个变量,可以把他赋值给空白标识符_
, _
用来忽略不需要的值。这样就不会触发声明但未使用的错误了
。 _
在for-range
循环经常用到。
5. 变量的作用域
定义三个不同的变量
运行,编译器报错:
因为变量都有自己的作用域,只有在变量的作用域内才能使用该变量。
变量作用域怎么确定呢? 规则是
- 包级变量作用域是整个包。在相同包里的任何地方都可以使用该变量
- 其他变量的声明语句在哪个大括号
{}
里,那个大括号{}
所包裹的范围就是该变量的作用域。 - Go 为所有包提供了一个全局作用域,存放内置函数,如
len()
,cap()
,make()
等。
如图, 不同的颜色对应不同的作用域。
- 变量
p
是包级变量,作用范围是整个main包
。 - 变量
s
声明在sum函数
的大括号里,其作用范围就是大括号{}
包裹的区域,即sum函数
的函数体。 - 变量
m
声明在main函数
的大括号里,其作用范围就是大括号{}
包裹的区域,即main函数
的函数体。
考你一个问题,下面代码能编译成功吗?
答案是不能。
6. 可导出与不可导出
你在一个包里定义了一个变量, 想让其他的包使用这个变量,如何做到呢?
要新建一个包,你需要新建一个文件夹,一个文件夹就是一个包,一般我们把文件夹的名字作为包名。
建立如下的项目结构:
在foo.go
声明一个包级变量Num
:
我们在main.go
中打印 foo.Num
输出
在 Go 中,可导出的变量以大写字母开头,不可导出的变量以小写字母开头。Go 中的隔离级别只有包级别:
- 可导出: 大写字母开头,对包外可见,其他包能调用这个变量。例如
foo包
里的Num
变量 - 不可导出: 小写字面开头,对包外不可见,其他包不能调用这个变量。
函数,类型也是一样的规则,(函数名就是变量)。fmt
包里的Println
函数就是大写开头,我们才能调用它。
为什么要设置包隔离级别呢?
设想一下你创建了一个包,包里有很多函数、变量和类型,你并不想把所有的东西都暴露出去,因为其他包能对你暴露的东西进行更改。你只暴露你想让其他包使用的东西。这能让你的包更安全,也让调用者没有心理负担!这是 Go 伟大的发明,简单,直接!
7. 常量
Go 的常量很简单,除了声明后不能改变,其他的都与变量相同。
使用const
关键字声明常量:
float64
是浮点数类型,就是小数的意思。
因为声明后不能改变,我们需要在声明时给其赋值。
const 也可以和 var 一样省略类型,让编译器自动推断:
常量声明后不能改变
小技巧
和其他语言不一样,常量名不需要全部大写,因为 Go 使用首字母的大小写作为其可导出的依据。
iota
iota 是一个常量生成器,用来生成一组常量自动递增的枚举值(go 不支持枚举类型 enum)。例如:
在一个 const 块中,iota 从 0 开始,每遇到一个声明语句就递增 1。如果后面的语句都与前一个相同,我们可以省略后面的赋值语句,直接写变量名,即上述代码与下述相同:
总结
- 介绍了
var
声明和短声明语句:=
,:=
只能在函数里使用。 - 变量的作用域在其所声明的大括号
{}
里。 - 可导出的变量首字母大写,不可导出的变量首字母小写。
- 常量在初始化后不允许改变。
- Go 是强类型的语言,不同类型的变量不能相互赋值。