说说 Go 中的变量(附粗制滥造面试题)
和其他语言没有区别,Go 中的数据也是两种表示方式,常量和变量,本文先说说变量吧。
为了增加文章的趣味性(多掉些头发),搜集了一些常见的面试题。部分是自己瞎编的,顺便为自己明年可能到来的面试做些准备。
先答题,题目中附有提示,但无解答。带着问题看文章效果或许更好。
面试题
1.1 如下的代码,哪些能正常编译?如果不能正常编译,如何修改?
A.
|
|
考点:定义未使用的局部变量和使用匿名变量。
B.
|
|
考点:简短模式只能定义局部变量
C.
|
|
考点:var 定义多个变量
1.2 下面这段代码逻辑是否正确?
|
|
考点:变量的作用域问题
注:取自 tonybai 老师的博客,原文地址。
1.3 下面哪一行变量简短定义存在已定义变量的赋值行为?
|
|
考点:局部变量的作用域与简单模式的退化赋值。
题目是不是都非常简单呢?如有问题,继续看正文吧。
什么是变量
变量是可以理解为使用一个名称绑定一个用来存放数据的内存块。
变量,首先是量,即为数据,而后是变,即表示内存块中的数据是可变的。与变量相对的是常量,常即恒常,表示数据不可变。常量的值是在编译期就确定了。
变量的定义
Go 中变量的定义有多种方式,先看一个变量完整的定义组成。如下:
|
|
var
是 Go 提供的用于定义变量的关键词,变量的定义语句可出现在函数和包级别中。
语句中核心是三个部分,分别是变量的名称、类型和值。与 C/C++ 不同,Go 的变量类型是在变量名称之后。
定义一个变量:
|
|
var
除了定义单个变量,还可以一次定义多个变量。
|
|
初始化
变量定义时可以指定初始值。
|
|
变量值的可选范围由变量类型决定。Go 是静态语言,变量类型是不可修改的。
|
|
如果变量定义时,没有指定初始值,将自动初始化为相应的零值(不同类型,零值不同),避免类似 C/C++ 中不可预测的行为。
在 windows 上的 “烫烫烫” 的梗,就是和变量未初始化有关。
不同类型变量的零值如下:
- 数值类型:0;
- 布尔类型:false;
- 字符串类型:空字符串;
- 接口或引用类型(slice、指针、map、chan 和 函数): nil;
- 聚合类型(数组、结构体):每个元素或字段都为对应类型的零值;
如果定义时,指定初始值,则可以省略类型,Go 编译器会自动推导变量类型。
|
|
简短定义
在函数中,变量的定义有一种简短写法,:=
。在初始化值类型明确的情况下,代替 var,实现类似动态语言的效果,懒人神器。
|
|
变量类型由编译器根据初始化值自动推导。
要注意的是,函数外的每个语句都必须以关键字开始(var, func 等),简短模式不能在函数外使用。
简短模式下,如果语句左边有多个变量,其中包含已定义变量,且必须是位于当前的作用域,则已定义变量会转化为赋值行为。
|
|
运行代码将会发现,x
的值修改了,但地址并未改变。
多变量赋值
定义变量时,已经演示了如何同时为多个变量赋初始值。动态语言通常支持这种写法,比如 Pyhon。
|
|
这种语法在简化写法的同时,还有一个比较有用的点,变量交换。
通常,交换变量的写法:
|
|
引入一个临时变量实现交换。除此之外,还有两种比较常见的交换算法,不引入临时变量。
|
|
或者
|
|
有了多变量同时赋值的特性之后,如下的写法即可完成交换。
|
|
匿名变量
Go 语言中会将定义但未使用的变量当成错误。但是有一种情况,如果 Go 的函数允许返回多个值,就要定义多个变量接收。
假设,有函数定义如下:
|
|
现在 main
函数将打印第一个返回值,第二个返回值不会使用。
|
|
编译无法通过,提示存在未使用的变量。
这时,可以使用 Go 中提供的匿名变量 _
接收无用的返回值。
|
|
匿名变量有什么特点?匿名变量可以多次使用,不占用内存空间,解决未使用变量的报错问题。
变量作用域
变量作用域和生命周期不同,生命周期表示变量执行期间的存活时间,而作用域表示变量能有效使用的范围。
除了变量有作用范围,还有诸如常量、函数、类型等都是有作用域的。
Go 的作用域可分为全局和局部,变量也就有全局变量和局部变量。但细究起来,全局和局部变量的说法也不对,Go 中内置的常量、函数、类型才能算是全局。变量的全局只能算包级别,包级别变量支持访问控制,变量名首字母大写,才能在全局可用。
局部会覆盖全局,不同的作用范围可以重新定义同名变量覆盖上一级作用域的变量。
|
|
局部变量有几种情况,分别是函数的参数与返回值,函数体内部定义变量,函数内部语法块等。
函数体内部作用域的例子。
|
|
变量作用域是一个很坑的话题,Go 中每个语法块,如 func
、if
、for
、select
、switch
等,都有一个隐式的作用域。基于它,出现了很多坑死人不偿命的面试题。
一个简单的例子。
|
|
else
中也可以使用 x
变量,if
之上有个隐式作用域。
作用域这块还有很多坑,比如与 defer 结合就会产生更多叹为观止(惨绝人寰)的现象。