变量声明
变量代表可变的数据类型,也就是说,它在程序执行的过程中可能会被一次甚至多次修改。
在 Go 语言中,通过 va
声明语句来定义一个变量,定义的时候需要指定这个变量的类型,然后再为它起个名字,并且设置好变量的初始值。所以 var
声明一个变量的格式如下:
var 变量名 类型 = 表达式
定义一个变量,并且设置它的初始值:
package main
import "fmt"
func main() {
var i int = 10
fmt.Println(i)
}
上面例子中 main
函数的内容,其中 var i int = 10
就是定义一个类型为 int(整数)
、变量名为 i
的变量,它的初始值为 10
,为了运行程序,我加了一行 fmt.Println(i)
,表示打印出变量 i
的值。
通过输入 go run main.go
命令回车运行,即可看到如下结果:
打印的结果是 10
,和变量的初始值一样。
因为 Go 语言具有类型推导功能,所以也可以不去刻意地指定变量的类型,而是让 Go 语言自己推导,比如变量 i
也可以用如下的方式声明:
var i = 10
这样变量 i
的类型默认是 int
类型。
你也可以一次声明多个变量,把要声明的多个变量放到一个括号中即可,如下面的代码所示:
var (
j int= 0
k int= 1
)
同理因为类型推导,以上多个变量声明也可以用以下代码的方式书写:
var (
j = 0
k = 1
)
这样就更简洁了。
基础类型
任何一门语言都有对应的基础类型,这些基础类型和现实中的事物一一对应,比如整型对应着 1
、2
、3
、100
这些整数,浮点型对应着 1.1
、3.4
这些小数等。Go 语言也不例外,它也有自己丰富的基础类型,常用的有:整型、浮点数、布尔型和字符串等等。
整型
在 Go 语言中,整型分为:
- 有符号整型:如
int
、int8
、int16
、int32
和int64
; - 无符号整型:如
uint
、uint8
、uint16
、uint32
和uint64
;
它们的差别在于,有符号整型表示的数值可以为负数、零和正数,而无符号整型只能为零和正数。
除了有用 位(bit)
大小表示的整型外,还有 int
和 uint
这两个没有具体 bit
大小的整型,它们的大小可能是 32bit
,也可能是 64bit
,和硬件设备 CPU 有关。
在整型中,如果能确定 int
的 bit
就选择比较明确的 int
类型,因为这会让你的程序具备很好的移植性。
在 Go 语言中,还有一种字节类型 byte
,它其实等价于 uint8
类型,可以理解为 uint8
类型的别名,用于定义一个字节,所以字节 byte
类型也属于整型。
浮点数
浮点数就代表现实中的小数。Go 语言提供了两种精度的浮点数,分别是 float32
和 float64
。项目中最常用的是 float64
,因为它的精度高,浮点计算的结果相比 float32
误差会更小。
下面的代码示例定义了两个变量 f32
和 f64
,它们的类型分别为 float32
和 float64
:
package main
import "fmt"
func main() {
var f32 float32 = 2.2
var f64 float64 = 10.3456
fmt.Println("f32 is",f32,"\nf64 is",f64)
}
运行这段程序,会看到如下结果:
布尔型
一个布尔型的值只有两种:true
和 false
,它们代表现实中的 是
和 否
。它们的值会经常被用于一些判断中,比如 if
语句等。Go 语言中的布尔型使用关键字 bool
定义。
下面的代码声明了两个变量:
package main
import "fmt"
func main() {
var bf bool =false
var bt bool = true
fmt.Println("bf is",bf,"\nbt is",bt)
}
运行结果如下:
布尔值可以用于一元操作符 !
,表示逻辑非的意思,也可以用于二元操作符 &&
、||
,它们分别表示逻辑和、逻辑或。
字符串
Go 语言中的字符串可以表示为任意的数据,比如以下代码,在 Go 语言中,字符串通过类型 string
声明:
package main
import "fmt"
func main() {
var s1 string = "Hello"
var s2 string = "world"
fmt.Println("s1 is", s1, "\ns2 is", s2)
}
运行程序结果如下:
在 Go 语言中,可以通过操作符 +
把字符串连接起来,得到一个新的字符串,比如将上面的 s1
和 s2
连接起来,如下所示:
package main
import "fmt"
func main() {
var s1 string = "Hello"
var s2 string = "world"
// fmt.Println("s1 is", s1, "\ns2 is", s2)
fmt.Println("s1+s2 =", s1+s2)
}
由于 s1
表示字符串Hello
,s2
表示字符串 World
,在终端输入 go run string.go
后,就可以打印出它们连接起来的结果 HelloWorld
,结果如以下所示:
字符串也可以通过 +=
运算符操作.
零值
零值其实就是一个变量的默认值,在 Go 语言中,如果我们声明了一个变量,但是没有对其进行初始化,那么 Go 语言会自动初始化其值为对应类型的 零值
。比如数值型
的零值是 0
,布尔型
的零值是 false
,字符型
的零值是 ""
空字符串等。
通过下面的代码示例,就可以验证这些基础类型的零值:
package main
import "fmt"
func main() {
var zi int
var zf float64
var zb bool
var zs string
fmt.Println("zi = ", zi)
fmt.Println("zf = ", zf)
fmt.Println("zb = ", zb)
fmt.Println("zs = ", zs)
}
结果如以下所示:
变量
变量简短声明
有没有发现,上面示例都有一个 var
关键字,但是这样写代码很繁琐。借助类型推导,Go 语言提供了变量的简短声明 :=
,结构如下:
变量名 := 表达式
借助 Go 语言简短声明功能,变量声明就会非常简洁,比如以上示例中的变量,可以通过如下代码简短声明:
package main
import "fmt"
func main() {
i := 10
bf := false
s1 := "Hello"
fmt.Printf("i 的数据类型为:%T\n", i)
fmt.Printf("bf 的数据类型为:%T\n", bf)
fmt.Printf("s1 的数据类型为:%T\n", s1)
}
结果如以下所示:
在实际的项目实战中,如果能为声明的变量初始化,那么就选择简短声明方式,这种方式也是使用最多的。
指针
在 Go 语言中,指针对应的是变量在内存中的存储位置,也就说指针的值就是变量的内存地址。通过 &
可以获取一个变量的地址,也就是指针。
在以下的代码中,pi
就是指向变量 i
的指针。要想获得指针 pi
指向的变量值,通过 *pi
这个表达式即可。尝试运行这段程序,会看到输出结果和变量 i
的值一样。
package main
import "fmt"
func main() {
i := 100
pi := &i
fmt.Println(*pi)
}
运行结果如下:
赋值
变量的值是可以修改的,那么怎么修改呢?这就是赋值语句要做的事情。最常用也是最简单的赋值语句就是 =
,如下代码所示:
package main
import "fmt"
func main() {
i := 100
fmt.Println("i的值是:", i)
i = 20
fmt.Println("i的新值是:", i)
}
运行结果如下:
这样变量 i
就被修改了,它的新值是 20
。
常量
一门编程语言,有变量就有常量,Go 语言也不例外。在程序中,常量的值是指在编译期就确定好的,一旦确定好之后就不能被修改,这样就可以防止在运行期被恶意篡改。
常量的定义
常量的定义和变量类似,只不过它的关键字是 const
。
下面的定义了一个常量 name
,它的值是 Tom
。因为 Go 语言可以类型推导,所以在常量声明时也可以省略类型。
package main
import "fmt"
func main() {
const name = "Tom"
fmt.Println("name =", name)
}
运行结果如下:
iota
iota
是一个常量生成器,它可以用来初始化相似规则的常量,避免重复的初始化。假设我们要定义 one
、two
、three
和 four
四个常量,对应的值分别是 1
、2
、3
和 4
,如果不使用 iota
,则需要按照如下代码的方式定义:
package main
import "fmt"
const (
one = 1
two = 2
three = 3
four = 4
)
func main() {
fmt.Println(one, two, three, four)
}
运行结果如下:
以上声明都要初始化,会比较繁琐,因为这些常量是有规律的(连续的数字),所以可以使用 iota
进行声明,如下所示:
package main
import "fmt"
const(
one = iota + 1
two
three
four
)
func main() {
fmt.Println(one, two, three, four)
}
运行程序,会发现打印的值和上面初始化的一样,也是 1、2、3、4:
iota
的初始值是 0
,它的能力就是在每一个有常量声明的行后面 +1
,下面分解上面的常量:
one=(0)+1
,这时候iota
的值为0
,经过计算后,one
的值为1
。two=(0+1)+1
,这时候iota
的值会+1
,变成了1
,经过计算后,two
的值为2
。three=(0+1+1)+1
,这时候iota
的值会再+1
,变成了2
,经过计算后,three
的值为3
。four=(0+1+1+1)+1
,这时候iota
的值会继续再+1
,变成了3
,经过计算后,four
的值为4
。
如果你定义更多的常量,就依次类推,其中 ()
内的表达式,表示 iota
自身 +1
的过程。
字符串
字符串是 Go 语言中常用的类型。
字符串和数字互转
Go 语言是强类型的语言,也就是说不同类型的变量是无法相互使用和计算的,这也是为了保证 Go 程序的健壮性,所以不同类型的变量在进行赋值或者计算前,需要先进行类型转换。涉及类型转换的知识点非常多,这里先学习基础类型之间的转换,更复杂的会在后面再学习。
以字符串和数字互转这种最常见的情况为例,如下面的代码所示:
package main
import (
"fmt"
"strconv"
)
func main() {
i := 100
i2s := strconv.Itoa(i)
s2i, err := strconv.Atoi(i2s)
fmt.Printf("i2s:%T\n", i2s)
fmt.Printf("s2i:%T\n", s2i)
fmt.Printf("err:%T\n", err)
}
运行结果如下:
通过包 strconv
的 Itoa
函数可以把一个 int
类型转为 string
,Atoi
函数则用来把 string
转为 int
。
同理对于浮点数、布尔型,Go 语言提供了 strconv.ParseFloat
、strconv.ParseBool
、strconv.FormatFloat
和 strconv.FormatBool
进行互转;
对于数字类型之间,可以通过强制转换的方式,如以下代码所示:
package main
import (
"fmt"
)
func main() {
var i int = 10
// 将整型转换为浮点型
i2f := float64(i)
fmt.Printf("i2f:%T\n", i2f)
var f64 float64 = 100.0
// 将浮点型转换为整型
f2i := int(f64)
fmt.Printf("f2i:%T\n", f2i)
}
运行结果如下:
这种使用方式很简单,采用 类型(要转换的变量)
格式即可。采用强制转换的方式转换数字类型,可能会丢失一些精度,比如浮点型转为整型时,小数点部分会全部丢失。
把变量转换为相应的类型后,就可以对相同类型的变量进行各种表达式运算和赋值了。
Strings 包
基础类型,尤其是字符串,不得不提 Go SDK 为我们提供的一个标准包 strings
。它是用于处理字符串的工具包,里面有很多常用的函数,帮助我们对字符串进行操作,比如 查找字符串
、去除字符串的空格
、拆分字符串
、判断字符串是否有某个前缀或者后缀
等。掌握好它,有利于我们的高效编程。
以下代码是我写的关于 strings
包的一些例子:
package main
import (
"fmt"
"strings"
)
func main() {
s1 := "hello"
// 判断 s1 的前缀是否是 H
fmt.Println(strings.HasPrefix(s1, "H"))
// 在 s1 中查找字符串 o
fmt.Println(strings.Index(s1, "o"))
// 把 s1 全部转为大写
fmt.Println(strings.ToUpper(s1))
}
运行结果如下:
总结
今天学习了 变量
、常量的声明
、初始化
,以及 变量的简短声明
,同时介绍了常用的 基础类型
、数字
和 字符串的转换
以及 strings
工具包的使用,有了这些,就可以写出功能更强大的程序。
在基础类型中,还有一个没有介绍的基础类型——复数
,它不常用,复数是用 complex
这个内置函数创建的。
思考:如何在一个字符串中查找某个字符串是否存在?