如何在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返回了值2212,因为变量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中,有多种声明变量的方法,在某些情况下,有多种方法声明完全相同的变量和值。

我们可以声明一个名为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]可以是undefinedtruefalse,这允许变量有三种状态。在Go中,布尔值不能有超过“两种”状态。

命名变量:规则和风格

变量的命名非常灵活,但有一些规则需要记住。

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

遵循这些规则,我们来看看有效和无效的变量名:

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

此外,在命名变量时要记住区分大小写。userNameuserNameuserNameuserName都是完全不同的变量。最好的做法是避免在程序中使用相似的变量名,以确保你和你的合作者(现在和将来)都能正确使用变量。

虽然变量是区分大小写的,但变量的第一个字母的大小写在Go中具有特殊意义。如果变量以大写字母开头,那么该变量可以在声明它的包(或export )之外访问。如果变量以小写字母开头,那么它只能在声明它的包中使用。

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 iindex更短
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

我们首先声明一个全局变量gvar g = "global"。在main函数中,我们调用函数printLocal,它声明了一个局部变量l并打印出来,fmt.Println(l)。然后,printLocal打印出全局变量gfmt.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变量。首先,我们在全局作用域中声明num1var 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时,我们显式地声明它为int32minutes:= int32(60)

现在,让我们来看看每个计算以及它为什么可以工作:

hours * year

在这个例子中,hours是一个int类型,而years未类型。当程序编译时,它显式地将years转换为int,这允许乘法操作成功。

minutes * year

在这个例子中,minutesint32,而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是一种类型语言,intint32不兼容数学运算。要将它们相乘,你需要将1转换为int32int

总结

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

你可能感兴趣的:(Go,golang,算法,开发语言)