变量是一个需要掌握的重要编程概念。它们是符号,代表你在程序中使用的值。
本教程将介绍一些变量基础知识和在您创建的Go程序中使用它们的最佳实践。
用技术术语来说,变量是将一个存储位置赋值给一个与符号名称或标识符相关联的值。在计算机程序中,我们使用变量名来引用存储的值。
我们可以把变量想象成一个标签,上面有一个名字,你把它和一个值联系在一起。
假设我们有一个整数1032049348
,我们希望将它存储在一个变量中,而不是不断地重复输入一个很长的数字。为了实现这一点,我们可以使用一个容易记住的名称,比如变量i
。要在变量中存储值,我们使用以下语法:
i := 1032049348
我们可以把这个变量想象成一个绑定在值上的标签。
标签上写着变量名i
,并绑定到整数值1032049348
。
短语i:= 1032049348
是一个声明和赋值语句,由以下几个部分组成:
i
):=
)1032049348
)的值int
)我们将在下一节中看到如何显式设置类型。
这些部分组成了一个语句,该语句将变量i
设置为整数1032049348
的值。
一旦我们设置一个变量等于一个值,我们就初始化或创建该变量。这样做之后,就可以使用变量而不是值了。
一旦我们将i
设置为1032049348
,我们就可以使用i
来代替整数,所以让我们把它打印出来:
package main
import "fmt"
func main() {
i := 1032049348
fmt.Println(i)
}
Output1032049348
通过使用变量,我们还可以快速轻松地进行数学运算。使用i:= 1032049348
,我们可以用下面的语法减去整数813
:
fmt.Println(i - 813)
Output1032048535
在这个例子中,Go为我们做了数学计算,从变量i
中减去813,返回和1032048535
。
说到数学,变量可以设置为与数学方程的结果相等。你也可以将两个数字相加,并将和的值存储在变量x
中:
x := 76 + 145
你可能已经注意到,这个例子看起来很像代数。就像我们在公式和方程中使用字母和其他符号来表示数字和数量一样,变量是表示数据类型值的符号名称。为了正确的Go语法,您需要确保您的变量位于任何方程的左侧。
让我们继续打印x
:
package main
import "fmt"
func main() {
x := 76 + 145
fmt.Println(x)
}
Output221
Go返回了值2212
,因为变量x
被设置为76
和145
的和。变量可以表示任何数据类型,而不仅仅是整数:
s := "Hello, World!"
f := 45.06
b := 5 > 9 // A Boolean value will return either true or false
array := [4]string{"item_1", "item_2", "item_3", "item_4"}
slice := []string{"one", "two", "three"}
m := map[string]string{"letter": "g", "number": "seven", "symbol": "&"}
如果您打印这些变量中的任何一个,Go将返回该变量等效的内容。让我们使用字符串slice
数据类型的赋值语句:
package main
import "fmt"
func main() {
slice := []string{"one", "two", "three"}
fmt.Println(slice)
}
Output[one two three
我们将[]string{"one", "two", "three"}
的切片值赋值给变量slice
,然后使用fmt.Println
函数通过调用slice
来打印出这个值。
变量的工作原理是在计算机中划出一小块内存区域,用于接收指定的值,然后将这些值与该空间相关联。
在Go中,有多种声明变量的方法,在某些情况下,有多种方法声明完全相同的变量和值。
我们可以声明一个名为i
的数据类型为int
的变量,而无需初始化。这意味着我们将声明一个空格来放置值,但不给它初始值:
var i int
这将创建一个声明为i
的变量,数据类型为int
。
可以使用equal(=
)操作符初始化这个值,如下面的例子所示:
var i int = 1
在Go中,这两种声明形式都称为长变量声明。
我们也可以使用简短的变量声明:
i := 1
在这个例子中,我们有一个名为i
的变量,以及一个int
的数据类型。当我们不指定数据类型时,Go将推断数据类型。
有了这三种声明变量的方式,Go社区采用了以下习语:
var i int
。i:= 1
。i := int64(1)
在Go中,初始化值时使用冗长的变量声明形式不被认为是习惯用法:
var i int = 1
遵循Go社区通常是如何声明变量的,这样其他人就可以无缝地阅读你的程序,这是一个很好的实践。
所有内置类型都有一个0值。任何被赋值的变量都是可用的,即使它从未被赋值。我们可以看到以下类型的0值:
package main
import "fmt"
func main() {
var a int
var b string
var c float64
var d bool
fmt.Printf("var a %T = %+v\n", a, a)
fmt.Printf("var b %T = %q\n", b, b)
fmt.Printf("var c %T = %+v\n", c, c)
fmt.Printf("var d %T = %+v\n\n", d, d)
}
Outputvar a int = 0
var b string = ""
var c float64 = 0
var d bool = false
我们在fmt.Printf
语句中使用了%T
动词。这告诉函数打印变量的data type
。
在Go中,因为所有的值都有0
值,所以我们不能像其他语言一样有undefined
值。例如,在某些语言中,[boolean
]可以是undefined
、true
或false
,这允许变量有三种
状态。在Go中,布尔值不能有超过“两种”状态。
变量的命名非常灵活,但有一些规则需要记住。
_
)组成。遵循这些规则,我们来看看有效和无效的变量名:
有效 | 无效 | 为什么无效 |
---|---|---|
userName | user-name | 不允许连字符 |
name4 | 4name | 不能以数字开头 |
user | $user | 不能使用符号 |
userName | user name | 不能超过一个单词 |
此外,在命名变量时要记住区分大小写。userName
、userName
、userName
和userName
都是完全不同的变量。最好的做法是避免在程序中使用相似的变量名,以确保你和你的合作者(现在和将来)都能正确使用变量。
虽然变量是区分大小写的,但变量的第一个字母的大小写在Go中具有特殊意义。如果变量以大写字母开头,那么该变量可以在声明它的包(或export
)之外访问。如果变量以小写字母开头,那么它只能在声明它的包中使用。
var Email string
var password string
Email
以大写字母开头,可以被其他包访问。password
以小写字母开头,并且只能在声明它的包中访问。
在Go中使用非常简洁(或简短)的变量名是很常见的。如果要在变量userName
和user
之间选择,按惯例应该选择user
。
作用域也有助于简化变量名。规则是,变量的作用域越小,变量名就越小:
names := []string{"Mary", "John", "Bob", "Anna"}
for i, n := range names {
fmt.Printf("index: %d = %q\n", i, n)
}
我们在更大的作用域中使用变量names
,因此通常会给它一个更有意义的名字,以帮助记住它在程序中的含义。然而,我们在下一行代码中立即使用i
和n
变量,然后不会再次使用它们……正因为如此,阅读代码的人不会混淆变量在哪里使用,或者它们的含义。
接下来,让我们介绍一些关于变量风格的注意事项。现在的样式是使用MixedCaps
或MixedCaps
来代替多单词名称中的下划线。
常规风格 | 非常规风格 | 为什么是非常规 |
---|---|---|
userName | user_name | 下划线不是常规的 |
i | index | i 比index 更短 |
serveHTTP | serveHttp | 首字母缩写应该大写 |
关于风格,最重要的是保持一致,并且你所工作的团队都认同这种风格。
正如“变量”一词所暗示的,我们可以随时更改Go变量。这意味着我们可以通过重新赋值将不同的值与之前赋值的变量联系起来。能够重新赋值是有用的,因为在整个程序过程中,我们可能需要接受用户生成的值到已经初始化的变量中。我们可能还需要修改之前定义的赋值。
在别人编写的大型程序中,如果不清楚哪些变量已经定义,那么很容易对变量重新赋值是很有用的。
让我们把76
的值赋给一个类型为int
的变量i
,然后给它赋一个新值42
:
package main
import "fmt"
func main() {
i := 76
fmt.Println(i)
i = 42
fmt.Println(i)
}
Output76
42
这个例子表明,我们可以先给变量i
赋值一个整数,然后再给变量i
重新赋值,这次赋值为42
。
注意:当你声明并初始化一个变量时,你可以使用:=
,但是,当你想简单地改变一个已经声明的变量的值时,你只需要使用相等操作符(=
)。
因为Go是一种typed
语言,我们不能将一种类型指定给另一种类型。例如,我们不能将值"Sammy"
赋值给int
类型的变量:
i := 72
i = "Sammy"
试图为彼此分配不同的类型将导致编译时错误:
Outputcannot use "Sammy" (type string) as type int in assignment
Go不允许我们多次使用一个变量名:
var s string
var s string
Outputs redeclared in this block
如果我们多次尝试对同一个变量名使用简短的变量声明,我们还会收到编译错误。这种情况可能是偶然发生的,所以理解错误消息的含义是很有帮助的:
i := 5
i := 10
Outputno new variables on left side of :=
与变量声明类似,考虑变量的命名可以提高程序的可读性,对你和其他人来说都是如此。
Go还允许我们在同一行中为多个变量分配多个值。这些值都可以是不同的数据类型:
j, k, l := "shark", 2.05, 15
fmt.Println(j)
fmt.Println(k)
fmt.Println(l)
Outputshark
2.05
15
在这个例子中,变量j
被赋值给字符串"shark"
,变量k
被赋值给浮点数2.05
,变量l
被赋值给整数15
。
这种在一行中将多个变量赋值给多个值的方法可以减少代码的行数。然而,重要的是不要为了更少的代码行而牺牲可读性。
当在程序中使用变量时,记住变量作用域是很重要的。变量的作用域指的是在给定程序的代码中可以访问变量的特定位置。也就是说,并不是所有变量都可以在程序的所有部分中访问,有些变量是全局变量,有些是局部变量。
全局变量存在于函数之外。局部变量存在于函数中。
让我们看一下全局变量和局部变量的实际作用:
package main
import "fmt"
var g = "global"
func printLocal() {
l := "local"
fmt.Println(l)
}
func main() {
printLocal()
fmt.Println(g)
}
Outputlocal
global
这里我们使用var g = "global"
在函数外部创建一个全局变量。然后我们定义函数printLocal()
。在函数内部,一个名为l
的局部变量被赋值,然后被打印出来。程序以调用printLocal()
结束,然后打印全局变量g
。
因为g
是一个全局变量,我们可以在printLocal()
中引用它。下面来修改前面的程序:
package main
import "fmt"
var g = "global"
func printLocal() {
l := "local"
fmt.Println(l)
fmt.Println(g)
}
func main() {
printLocal()
fmt.Println(g)
}
Outputlocal
global
global
我们首先声明一个全局变量g
, var g = "global"
。在main
函数中,我们调用函数printLocal
,它声明了一个局部变量l
并打印出来,fmt.Println(l)
。然后,printLocal
打印出全局变量g
, fmt.Println(g)
。即使g
没有在printLocal
中定义,它仍然可以被访问,因为它是在全局作用域中声明的。最后,main
函数也打印出g
。
现在尝试在函数外部调用局部变量:
package main
import "fmt"
var g = "global"
func printLocal() {
l := "local"
fmt.Println(l)
}
func main() {
fmt.Println(l)
}
Outputundefined: l
我们不能在赋值局部变量的函数之外使用它。如果你尝试这样做,你会在编译时收到一个undefined
错误。
让我们看另一个例子,我们对全局变量和局部变量使用相同的变量名:
package main
import "fmt"
var num1 = 5
func printNumbers() {
num1 := 10
num2 := 7
fmt.Println(num1)
fmt.Println(num2)
}
func main() {
printNumbers()
fmt.Println(num1)
}
Output10
7
5
在这个程序中,我们声明了两次num1
变量。首先,我们在全局作用域中声明num1
, var num1 = 5
,然后在printNumbers
函数的局部作用域中声明num1:= 10
。当我们在main
程序中打印num1
时,我们看到5
的值被打印出来了。这是因为main
只能看到全局变量的声明。然而,当我们从printNumbers
函数打印出num1
时,它看到了本地声明,并将打印出10
的值。即使printNumbers
创建了一个名为num1
的新变量,并将值10
赋给它,它也不会影响num1
的全局实例的值5
。
使用变量时,你还需要考虑程序的哪些部分需要访问这些变量;相应地采用全局或局部变量。在Go程序中,您会发现局部变量通常更常见。
常量类似于变量,只是一旦声明就不能修改。常量用于定义在程序中会多次使用但不能更改的值。
例如,如果我们想声明购物车系统的税率,可以使用一个常量,然后在程序的不同区域计算税率。在未来的某个时候,如果税率改变了,我们只需要改变程序中的一个地方。如果我们使用了一个变量,就有可能在程序的某个地方不小心改变它的值,从而导致错误的计算。
要声明常量,可以使用以下语法:
const shark = "Sammy"
fmt.Println(shark)
OutputSammy
如果我们试图在声明常量后修改它,会得到编译时错误:
Outputcannot assign to shark
常量可以是untyped
的。这在处理整数类型的数据时很有用。如果常量是untyped
,它会被显式转换,而typed
常量则不会。让我们看看如何使用常量:
package main
import "fmt"
const (
year = 365
leapYear = int32(366)
)
func main() {
hours := 24
minutes := int32(60)
fmt.Println(hours * year)
fmt.Println(minutes * year)
fmt.Println(minutes * leapYear)
}
Output8760
21900
21960
如果声明常量时指定了类型,它就会是这个类型。在这里,当我们声明常量leapYear
时,我们将它定义为数据类型int32
。因此它是一个typed
常量,这意味着它只能操作int32
数据类型。我们声明的year
常量没有类型,所以它被认为是无类型
。因此,你可以将它用于任何整数数据类型。
当定义hours
时,它推断它的类型是int
,因为我们没有显式地给它一个类型hours:= 24
。当我们声明minutes
时,我们显式地声明它为int32
, minutes:= int32(60)
。
现在,让我们来看看每个计算以及它为什么可以工作:
hours * year
在这个例子中,hours
是一个int
类型,而years
是未类型。当程序编译时,它显式地将years
转换为int
,这允许乘法操作成功。
minutes * year
在这个例子中,minutes
是int32
,而year
是未类型。当程序编译时,它显式地将years
转换为int32
,这允许乘法操作成功。
minutes * leapYear
在这个例子中,minutes
是一个int32
,而leapYear
是一个int32
类型的常量。这一次编译器什么都不用做,因为两个变量的类型已经相同了。
如果我们尝试将两个typed
且不兼容的类型相乘,程序将无法编译:
fmt.Println(hours * leapYear)
Outputinvalid operation: hours * leapYear (mismatched types int and int32)
在这个例子中,hours
被推断为int
类型,而leapYear
被明确声明为int32
类型。因为Go是一种类型语言,int
和int32
不兼容数学运算。要将它们相乘,你需要将1转换为int32
或int
在本教程中,我们回顾了Go中变量的一些常见用例。变量是编程的重要组成部分,它作为符号,代表程序中使用的数据类型的值。