如何在Go中使用变量和常量

来自菜鸟教程
跳转至:导航、​搜索

变量是一个需要掌握的重要编程概念。 它们是代表您在程序中使用的值的符号。

本教程将介绍一些变量基础知识和在您创建的 Go 程序中使用它们的最佳实践。

理解变量

用技术术语来说,变量将存储位置分配给与符号名称或标识符相关的值。 我们使用变量名来引用计算机程序中存储的值。

我们可以将变量视为带有名称的标签,您可以将其绑定到值上。

假设我们有一个整数 1032049348,我们希望将它存储在一个变量中,而不是一遍又一遍地不断地重新输入长数字。 为此,我们可以使用一个易于记忆的名称,例如变量 i。 要将值存储在变量中,我们使用以下语法:

i := 1032049348

我们可以把这个变量想象成一个与值绑定的标签。

标签上写有变量名 i,并与整数值 1032049348 绑定。

短语 i := 1032049348 是一个声明和赋值语句,由几个部分组成:

  • 变量名(i
  • 短变量声明赋值 (:=)
  • 与变量名绑定的值 (1032049348)
  • Go 推断的数据类型 (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 返回值 221 因为变量 x 被设置为等于 76145 的总和。

变量可以表示任何数据类型,而不仅仅是整数:

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 中,有多种声明变量的方法,在某些情况下,声明完全相同的变量和值的方法不止一种。

我们可以在没有初始化的情况下声明一个数据类型为 int 的名为 i 的变量。 这意味着我们将声明一个空间来放置一个值,但不给它一个初始值:

var i int

这将创建一个声明为数据类型 inti 的变量。

我们可以使用等于 (=) 运算符来初始化该值,如下例所示:

var i int = 1

在 Go 中,这两种声明形式都称为 长变量声明

我们也可以使用短变量声明

i := 1

在这种情况下,我们有一个名为 i 的变量,以及一个 int 的数据类型。 当我们不指定数据类型时,Go 会推断数据类型。

对于声明变量的三种方式,Go 社区采用了以下习惯用法:

  • 仅在不初始化变量时使用长格式 var i int
  • 在声明和初始化时使用缩写形式 i := 1
  • 如果您不希望 Go 推断您的数据类型,但仍想使用短变量声明,则可以使用以下语法将值包装为所需类型:
i := int64(1)

在我们初始化值时,使用长变量声明形式在 Go 中不被认为是惯用的:

var i int = 1

遵循 Go 社区通常如何声明变量以便其他人可以无缝读取您的程序是一种很好的做法。

零值

所有内置类型的值都为零。 任何分配的变量都是可用的,即使它从未分配过值。 我们可以看到以下类型的零值:

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 中,因为所有值都有一个 zero 值,我们不能像其他一些语言那样有 undefined 值。 例如,某些语言中的 boolean 可能是 undefinedtruefalse,这允许 three 状态多变的。 在 Go 中,布尔值不能超过 two 个状态。

命名变量:规则和样式

变量的命名相当灵活,但有一些规则需要牢记:

  • 变量名只能是一个单词(如没有空格)。
  • 变量名只能由字母、数字和下划线组成(_)。
  • 变量名不能以数字开头。

按照这些规则,让我们看看有效和无效的变量名:

有效的 无效的 为什么无效
userName user-name 不允许使用连字符
name4 4name 不能以数字开头
user $user 不能使用符号
userName user name 不能超过一个字

此外,在命名变量时请记住它们区分大小写。 这些名称 userNameUSERNAMEUserNameuSERnAME 都是完全不同的变量。 最好避免在程序中使用相似的变量名称,以确保您和您的合作者(当前和未来)都能保持变量的正确性。

虽然变量区分大小写,但变量首字母的大小写在 Go 中具有特殊含义。 如果变量以大写字母开头,则该变量可以在声明它的包之外访问(或 exported)。 如果变量以小写字母开头,则它仅在声明它的包中可用。

var Email string
var password string

Email 以大写字母开头,可以被其他包访问。 password 以小写字母开头,并且只能在声明它的包内访问。

在 Go 中使用非常简洁(或短)的变量名是很常见的。 考虑到对变量使用 userNameuser 之间的选择,选择 user 是惯用的。

范围在变量名称的简洁性方面也发挥了作用。 规则是变量存在的范围越小,变量名越小:

names := []string{"Mary", "John", "Bob", "Anna"}
for i, n := range names {
    fmt.Printf("index: %d = %q\n", i, n)
}

我们在更大的范围内使用变量 names,因此通常给它一个更有意义的名称以帮助记住它在程序中的含义。 但是,我们在下一行代码中立即使用 in 变量,然后不再使用它们……因此,它不会混淆阅读代码的人在哪里变量被使用,或者它们的含义。

接下来,让我们介绍一些关于可变样式的注意事项。 样式是使用 MixedCapsmixedCaps 而不是下划线用于多词名称。

传统风格 非传统风格 为什么非传统
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 变量。 首先,我们在全局范围 var num1 = 5 中声明 num1,然后在 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 常量没有类型,所以它被认为是 untyped。 因此,您可以将它与任何整数数据类型一起使用。

当定义 hours 时,它 推断 它属于 int 类型,因为我们没有明确地给它一个类型 hours := 24。 当我们声明 minutes 时,我们明确地将其声明为 int32minutes := int32(60)

现在让我们来看看每个计算及其工作原理:

hours * year

在这种情况下,hoursintyears无类型 。 当程序编译时,它显式地将 years 转换为 int,这使得乘法运算成功。

minutes * year

在这种情况下,minutesint32year无类型 。 当程序编译时,它显式地将 years 转换为 int32,这使得乘法运算成功。

minutes * leapYear

在这种情况下,minutesint32leapYearint32类型化 常数。 这次编译器无需执行任何操作,因为这两个变量已经属于同一类型。

如果我们尝试将 typed 且不兼容的两种类型相乘,程序将无法编译:

fmt.Println(hours * leapYear)
Outputinvalid operation: hours * leapYear (mismatched types int and int32)

在这种情况下,hours 被推断为 intleapYear 被显式声明为 int32。 因为 Go 是一种类型化语言,所以 intint32 不兼容数学运算。 要将它们相乘,您需要 将 1 转换为 int32 或 int

结论

在本教程中,我们回顾了 Go 中变量的一些常见用例。 变量是编程的重要组成部分,充当代表我们在程序中使用的数据类型的值的符号。