在编程中,“数据”可以有很多不同的含义。数据可以是数字、布尔值(true/false)、字符串(文本块)或其组合。下面将介绍如何通过创建和使用变量来存储“数据”以及与这些值交互的不同方式(例如将两个数字相加,或用两个字符串创建更长的字符串)。我们还将讨论数据类型, Go 用来“期望”变量具有特定属性的特定划分。通过创建变量并为其赋值,我们将了解使用不同数据类型的局限性和优势。

字面量

在 Go 中,值可以是多种含义。例如,值可以是数字(例如 109 ),也可以是括在引号中的文本(例如 “Hello world” )。这些值可以直接写入代码中,我们称之为 literals (字面量 )。它们的含义就是字面量, 我们可以使用以下运算符在 Go 中使用字面量(或命名值)执行算术运算:

  • +
  • -
  • *
  • /
  • % 取余数

例如:

fmt.Println(20 * 3) // Prints: 60
fmt.Println(55.21 / 5) // Prints: 11.042
fmt.Println(9 % 2) // Prints: 1

想象一下,上面的代码出现在 Go 程序的 main 函数体中。在此代码片段中,我们将 Go 编程语言用作计算器。我们打印出 20 乘以 3 的乘积( 60 )。接下来,我们打印出 55.21 除以 5 ( 11.042 )。最后,我们打印出 9 除以 2 后的余数( 9 除以 2 余数为 1 )。

常量

除了字面值(又称未命名值)之外,还有命名值。在 Go 中,命名一个值意味着创建一个表示该值的单词。命名值的一个例子是常量 ,它在程序运行时无法更新。另一个命名值的例子是变量, 我们使用 const 关键字来创建常量。我们立即使用字面量为常量赋值。在程序的其余部分,我们可以使用常量的名称来代替字面量。

const funFact = "Hummingbirds' wings can beat up to 200 times a second."

fmt.Println("Did you know?")
fmt.Println(funFact)

上面,我们创建了一个名为 funFact 的常量,它包含文本 “Hummingbirds’ wings can beat up to 200 times a second.” 。然后,我们可以使用我们应用的名称打印出这个值。如果一个值在整个程序中不会发生变化,这很有用,也有助于传达开发人员保持一致值的意图, 我们需要使用驼峰命名法 (camelCase) 或帕斯卡命名法 (PascalCase),将每个后续单词大写, 这是一般命名的规范

什么是数据类型?

编程语言需要处理和组织数据。这些数据以二进制数(由 0 和 1 组成的数字)的形式存储在计算机内存中。数据类型是编程语言对存储哪些信息进行指定和划分。Go 语言自带了许多基本类型,这些数据类型内置于编程语言中。你也可以创建自己的类型,将这些基本类型组合成更复杂的类型,但我们首先介绍一下 Go 中默认可以存储的数据。

Go 中数字有三种基本类别:

  • 整数( int )是整数/计数型数字。你可以用 int 来计算书架上的书数、仓库里的商品数量、网站上的人数等等…
  • 浮点数( float )可以包含小数部分。你可以使用 float 来存储距离、百分比以及其他需要除法或精度的数值
  • 复数, complex ,是一对浮点数,其中第二部分以“虚数”单位 i 标记。复数在二维空间推理时特别有用,并且还有其他用途使其与复杂的计算相关
数据类型类别用法示例值示例用途
int整数值, 计数数字11,82139,-1581, -58312,500000库存物品数量
float小数值,带小数部分的数字, 除法结果-0.075,4185.0001,-8.3,2.718平均值,无理数的表示,测量
complex虚数,带虚部值的数字3i,7 + 2i, -14 -.05i二维坐标,涉及平方根的数学计算

Go 中的基本数字类型

Go 有 15 种不同的数字类型,分为三类: int 、 float 和 complex 。这意味着在 Go 中有 15 种不同的方式描述数字。其中包括 11 种不同的整数类型、2 种不同的浮点类型和 2 种不同的复数类型。这些类型都能识别不同的有效数字集合。例如,整数不能存储 8.6132 这个数字。

除了分为这三类之外,类型还指示将使用多少位(二进制数字)来表示数据。位数越少,数据的可能值就越少,整数会强制使用严格的最小值和最大值,浮点数和复数则会降低精度。位数越少,需要保存的数据就越少,因此保存这些数据占用计算机内存也就越少。因此,虽然使用可以接受更大范围值的类型更诱人,但它可能会降低计算机的性能或导致计算机内存不足。

整数进一步分为两类: 有符号整数和无符号整数 。有符号整数可以为负数,而无符号整数只能为正数。这意味着无符号整数的最小值始终为 0。由于可以忽略负值的可能性,因此在相同位数的情况下,无符号整数的最大值远高于有符号整数。

Go 也有一个布尔类型。布尔值要么是 false ,要么是 true 。Go 只需要一位来存储布尔值: 0 表示 false , 1 表示 true 。

查看不同整数类型(以及布尔类型)的各种最小值和最大值。浮点数(和复数)没有最小值或最大值, float32 和 float64 之间的差异反映了它们使用不同的二进制位来确保值的精度。

数据类型内存使用 - 比特数最小值最大值
bool10(false)1(true)
int88-128127
int1616-32,76832,767
int3232-2,147,483,6482,147,483,647
int6464-9,223,372,036,854,775,8089,223,372,036,854,775,807
uint880255
uint1616065,535
uint323204,294,967,295
uint6464018,446,744,073,709,551,615

什么是变量?

现在我们已经了解了类型的背景,可以讨论一下变量, 变量是什么,以及我们如何创建和使用它们。变量是一个命名的值(类似于常量),但它还有一个额外的特性,那就是它可以在程序运行过程中发生变化。如果我们在程序中的不同位置使用某个值,我们可以将该值存储在变量中,以便以后轻松访问。

变量的定义由 var 关键字和两条信息组成:变量的名称以及变量中存储的数据类型。由于变量可以更新,我们甚至不需要预先赋值。以下是一些变量的定义:

var lengthOfSong uint16
var isMusicOver bool
var songRating float32

上面我们创建了三个变量:

  • 一个名为 lengthOfSong 的无符号 16 位整数。
  • 一个名为 isMusicOver 的布尔值。
  • 一个名为 songRating 的 32 位浮点数。

请注意,我们的变量名也遵循与常量相同的命名约定,使用驼峰式命名法和描述性名称。

读取 Go 错误

代码运行失败并不是什么丢人的事。编程错误和异常时有发生,学会阅读和理解它们是程序员工具箱中不可或缺的工具。当 Go 编译器抛出错误时,程序的二进制文件就无法创建,没有二进制文件,计算机就无法执行程序代码。Go 会尝试通过提供以下信息来告诉你问题所在:出错的文件名、Go 发现问题的行号以及发生错误的行的列号(从左侧算起的字符数)。一个常见的错误是,Go 语言的编译器识别出程序中存在一个未使用的已定义变量。例如:

package main

func main() {
  var numberWheels int8
}

当我们尝试运行 main.go 并在文件的第 4 行定义变量 numberWheels 时,我们在程序的其他任何地方都不会使用该变量,我们将看到以下消息:

./main.go:4:7: numberWheels declared and not used

请注意,错误消息包含文件名( main.go )、导致错误的行( 4 行)以及具体位置(换行符右侧 7 字符)。如果我们从程序中删除该变量或在其他地方使用它,Go 编译器就能安抚它,并运行我们的程序。

这看起来像是语言的一个缺点,它阻碍了你作为程序员表达自由和个性,但它的设计初衷就是为了尽早发现程序中可能存在的错误。未使用的变量浪费内存,但也可能是拼写错误或无意的遗漏。在这种情况下,Go 的强硬立场是,通过拒绝运行,直到对这个未使用的定义采取某些操作,来防止程序员疑惑为什么他们的代码没有按照他们预期的方式工作。

变量赋值

更新变量也称为赋值 。为了给变量赋值,我们可以使用赋值运算符 ( = ) 加上一个值。

var kilometersToMars int32

kilometersToMars = 62100000

在上面的例子中,我们首先使用 var 关键字声明变量,名称为 kilometersToMars ,类型为 int32 。然后,我们将 62100000 赋值给 kilometersToMars 。现在,当我们需要知道到火星的距离时,我们可以使用 kilometersToMars 来获取该值!

赋值变量的另一种方法是:

var kilometersToMars int32 = 62100000

在我们最新的例子中,我们声明了 kilometersToMars ,分配了类型 ( int32 ) 并将其初始化 (赋给变量的第一个值)为 62100000 。如果我们确切知道变量的类型,以及要将其初始化为哪个特定值,那么这种语法会很有帮助。

字符串

到目前为止,我们已经讨论了数字类型,但 Go 还提供了其他一些内置类型。其中一种特别有用的类型叫做字符串 。字符串是 Go 用于存储和处理文本的类型。在一般编程意义上,“字符串”是指任意长度的文本,其名称的选择是为了让人联想到一系列数据。

下面,我们声明了两个字符串变量:

var nameOfSong string
var nameOfArtist string

之后,我们给变量赋值

nameOfSong = "Stop Stop"
nameOfArtist = "The Julie Ruin"

可以在字符串上使用 + 运算符将它们连接起来,这称为字符串连接 。请注意, + 不会添加额外的空格或标点符号。

var description string
description = nameOfSong + " is by: " + nameOfArtist + "."
fmt.Println(description)
// Prints "Stop Stop is by: The Julie Ruin.

零值

Go 的设计者试图创建一些合理的"默认值",让我们可以根据变量的类型进行预测。所有数值变量在赋值前都为 0, 字符串变量的默认值为 "" ,也称为空字符串,因为它不包含任何字符。布尔变量的默认值为 false 。例如:

var classTime uint32
var averageGrade float32
var teacherName string
var isPassFail bool

fmt.Println(classTime) // Prints 0
fmt.Println(averageGrade) // Prints 0
fmt.Println(teacherName) // Doesn't print anything
fmt.Println(isPassFail) // Prints false

上面我们声明了四个变量:一个无符号的 32 位整数、一个 32 位浮点数、一个字符串和一个布尔值。我们不给任何变量赋值,而是将它们打印出来查看它们的默认值。这两个数字打印的结果相同,都是 0 ,这对于两种类型来说都是有效的值。空字符串打印时不显示任何内容。布尔值打印 false 。

推断类型

有一种声明变量的方法,无需明确指定其类型,即使用简短声明运算符 := 。如果我们在创建变量时就知道要存储什么值,就可以使用 := 运算符。例如:

nuclearMeltdownOccurring := true
radiumInGroundWater := 4.521
daysSinceLastWorkplaceCatastrophe := 0
externalMessage := "Everything is normal. Keep calm and carry on."

上面,我们能够定义一个 bool 、一个 float 、一个 int 和一个 string ,而无需指定类型。我们使用 := 创建一个变量,并根据提供的值推断其类型。以这种方式创建的浮点数的类型为 float64 。以这种方式创建的整数要么是 int32 ,要么是 int64 (我们将在后面讨论如何确定它们)。

Go 还提供了单独的语法来声明变量并推断其类型。我们可以将上面的代码写成:

var nuclearMeltdownOccurring = true
var radiumInGroundWater = 4.521
var daysSinceLastWorkplaceCatastrophe = 0
var externalMessage = "Everything is normal. Keep calm and carry on."

请注意,在第二个示例中,我们使用了 var 关键字和 = 运算符。在这两个示例中,我们都声明并初始化了变量同时让 Go 编译器推断所分配值的类型。

默认 int 类型

在 Go 中,还有一种更常见的定义 int 的方法。实际上,计算机的只读存储器 (ROM) 中的数据有一个默认长度。

(只读存储器)。一些较新的计算机可能拥有更强大的处理能力,可以存储/处理更大的数据块。这些计算机可能使用的是 64 位架构,但其他计算机仍然运行在 32 位架构上,并且运行良好。通过提供 int 或 uint 类型,Go 会检查计算机的架构是 32 位还是 64 位。然后,它会根据计算机本身的情况,提供 32 位 int (或 uint )或 64 位类型。

建议使用 int 除非有理由指定 int 的大小(例如知道该值将大于默认类型,或者需要优化使用的空间量)。

var timesWeWereFooled int
var foolishGamesPlayed uint

上面我们声明了两个变量,timesWeWereFooled 是 32 位或 64 位的整数。foolishGamesPlayed 是 32 位或 64 位的 foolishGamesPlayed 无符号整数。

consolationPrizes := 2

当使用 := 运算符声明变量并赋值时,其类型与声明为 int 时相同。在上面的例子中, consolationPrize 类型为 int 。

更新自身

通过将另一个数字添加到自身并保存新值来更新变量非常常见,因此 Go 提供了一个简写运算符,即 += 运算符, 比如运算 basketTotal = spinachPrice + basketTotal, 我们可以使用以下语法完成相同的操作:

spinachPrice := 1.50
basketTotal += spinachPrice
fmt.Println(basketTotal) // Prints: 2.25

我们也可以对字符串做同样的操作:

command := "Hold my "
beverage := "soda"

command += beverage
fmt.Println(command) // Prints: Hold my soda

除了 += (是的,双关语)之外,Go 还有其他算术运算,可以执行计算并更新变量自身的值:

  • -= 从变量中减去。
  • *= 将变量乘以一个因子。
  • /= 将变量除以被除数。

多变量声明

到目前为止,我们一直在声明变量一个接一个,每个变量都占一行。但 Go 实际上允许我们在一行中声明多个变量,事实上,有几种不同的语法!

让我们从不赋值的声明开始:

var part1, part2 string
part1 = "To be..."
part2 = "Not to be..."

上面,我们在同一行声明了 part1 和 part2 ,并且它们具有相同的类型。如果我们使用这种语法,则两个变量必须是相同的类型。如果我们已经知道要为变量分配什么值,我们可以使用 := 如下所示:

quote, fact := "Bears, Beets, Battlestar Galactica", true
fmt.Println(quote) // Prints: Bears, Beets, Battlestar Galactica
fmt.Println(fact) // Prints: true

在上面的例子中,我们在同一行中使用一个运算符 ( := ) 声明了 quote 和 fact 。然后,根据变量和值的顺序,为这些变量赋值。由于 quote 是第一个变量,字符串 “Bears, Beets, Battlestar Galactica” 是第一个值, quote 值为 “Bears, Beets, Battlestar Galactica” 。同样, fact 也被赋值为 true 。