数据类型和表达式
2019-04-07
11分钟阅读时长
当谈到Go的数据类型和表达式时,需要掌握以下这些知识:
- 基本数据类型:Go中的基本数据类型包括
bool
、string
、int
、int8
、int16
、int32
、int64
、uint
、uint8
、uint16
、uint32
、uint64
、uintptr
、byte
、rune
、float32
和float64
。其中byte
是uint8
的别名,rune
是int32
的别名。
下面是关于Go基本数据类型的总结:
类型名称 | 类型描述 | 大小(字节) | 默认值 | 范围 |
---|---|---|---|---|
bool | 布尔类型 | 1 | false | true、false |
byte | 字节类型 | 1 | 0 | 0~255 |
rune | Unicode字符类型 | 4 | 0 | -2147483648~2147483647 |
int | 整数类型 | 平台相关 | 0 | 最大值和最小值取决于平台,通常为-2147483648~2147483647 |
int8 | 8位整数类型 | 1 | 0 | -128~127 |
int16 | 16位整数类型 | 2 | 0 | -32768~32767 |
int32 | 32位整数类型 | 4 | 0 | -2147483648~2147483647 |
int64 | 64位整数类型 | 8 | 0 | -9223372036854775808~9223372036854775807 |
uint | 无符号整数类型 | 平台相关 | 0 | 最大值取决于平台,通常为0~4294967295 |
uint8 | 8位无符号整数类型 | 1 | 0 | 0~255 |
uint16 | 16位无符号整数类型 | 2 | 0 | 0~65535 |
uint32 | 32位无符号整数类型 | 4 | 0 | 0~4294967295 |
uint64 | 64位无符号整数类型 | 8 | 0 | 0~18446744073709551615 |
uintptr | 指针类型 | 平台相关 | 0 | 保存指针地址的整数值 |
需要注意的是,Go语言中支持隐式类型转换,但是不同类型之间的转换需要满足特定的规则。另外,Go还提供了一种复合类型complex
,用于表示复数。complex
由实部和虚部两个float32
或float64
类型组成,可以用于数学运算。
- 复合数据类型:Go中的复合数据类型包括数组、切片、映射、通道和结构体。
类型名称 | 类型描述 | 大小(字节) | 初始化方式 |
---|---|---|---|
数组(Array) | 在内存中连续存储多个同类型元素的集合,长度固定不变 | n * 元素大小 | var a [n]T、a := [n]T{value1, value2, ..., valuen}、a := [...]T{value1, value2, ..., valuen} |
切片(Slice) | 动态数组,由指向底层数组的指针、长度和容量三部分组成 | 24 | var s []T = make([]T, len, cap)、s := []T{value1, value2, ..., valuen}、s := make([]T, length)、s := make([]T, length, capacity) |
映射(Map) | 存储键值对的无序集合,每个键唯一对应一个值 | 平均8字节/条记录 | var m map[T]U = make(map[T]U)、m := map[T]U{key1: value1, key2: value2, ..., keyn: valuen} |
通道(Channel) | 用于协程间通信的管道,支持发送和接收数据 | 0或者1 | var ch chan T = make(chan T)、ch := make(chan T, buffer) |
结构体 (Struct) | 自定义类型,它由一系列不同类型的字段组成。结构体中的每个字段都有自己的标识符和类型。 | N/A | type person struct {name stringage int}var p personp = person{name: "Alice", age: 30} |
需要注意的是,Go 语言中除了数组和结构体外,其他数据类型都是引用类型。这意味着切片、映射等类型在传递给函数或赋值给变量时,实际上是传递了一个指向底层数据结构的指针。因此,在操作这些数据类型时需要小心避免出现副作用。
以下是一些关于Golang复合数据类型数组(Array)的一些例子
- 字符串数组
var names [3]string
names[0] = "Alice"
names[1] = "Bob"
names[2] = "Charlie"
- 布尔数组
var flags [4]bool
flags[0] = true
flags[1] = false
flags[2] = false
flags[3] = true
- 浮点数数组
var prices [5]float64
prices[0] = 1.99
prices[1] = 2.99
prices[2] = 3.99
prices[3] = 4.99
prices[4] = 5.99
- 结构体数组
type Person struct {
Name string
Age int
}
var people [2]Person
people[0] = Person{"Alice", 30}
people[1] = Person{"Bob", 25}
- 多维数组
var matrix [3][3]int
matrix[0] = [3]int{1, 2, 3}
matrix[1] = [3]int{4, 5, 6}
matrix[2] = [3]int{7, 8, 9}
以下是一些关于Golang复合数据切片(Slice)类型的一些例子
代码语言:javascript复制numbers := []int{1, 2, 3, 4, 5}
以上代码定义了一个名为 numbers
的整数类型切片,其中包含元素 1、2、3、4 和 5。可以通过下标访问切片中的元素。
还可以使用 make 函数来创建指定长度和容量的切片:
代码语言:javascript复制numbers := make([]int, 5, 10)
该语句创建了一个长度为 5,容量为 10 的整数类型切片。此时所有元素默认初始化为0。
可以使用 append()
函数将元素添加到切片末尾:
numbers = append(numbers, 6, 7, 8)
此时 numbers
切片的元素变成了 1、2、3、4、5、6、7 和 8。
还可以通过切片操作来获取子切片:
代码语言:javascript复制subNumbers := numbers[2:5]
以上代码创建了一个从索引 2 开始,到索引 5 结束(不包括索引 5)的子切片。子切片 subNumbers
包含元素 3、4 和 5。
以下是一些关于Golang复合数据映射(Map)类型的一些例子
代码语言:javascript复制ages := map[string]int{
"Alice": 30,
"Bob": 25,
"Charlie": 35,
}
以上代码定义了一个名为 ages
的映射,其中键为字符串类型,值为整数类型。它包含三个条目,分别表示 Alice、Bob 和 Charlie 的年龄。
可以使用索引操作符 []
获取映射中的值:
fmt.Println(ages["Alice"]) // 输出:30
还可以使用 make()
函数创建空映射:
scores := make(map[string]int)
该语句创建了一个名为 scores
的空映射,键为字符串类型,值为整数类型。
可以使用 delete()
函数从映射中删除条目:
delete(ages, "Bob")
此时映射中不再包含键为 Bob 的条目。
以下是一些关于Golang复合数据通道(Channel)类型的一些例子
代码语言:javascript复制package main
import "fmt"
func main() {
// 创建一个整数类型的通道
ch := make(chan int)
// 使用 go 关键字启动一个新的 goroutine
go func() {
// 向通道发送值
ch <- 42
}()
// 从通道接收值
i := <-ch
fmt.Println(i) // 输出:42
}
在这个例子中,我们创建了一个整型通道 ch
,然后启动了一个新的 goroutine,在其中向通道发送了值 42。在主 goroutine 中我们从通道 ch
中接收该值,并将其打印到控制台上。
以下是另一个使用 Golang 中通道数据类型的示例:
代码语言:javascript复制package main
import "fmt"
func main() {
// 创建一个字符串类型的通道
ch := make(chan string)
// 使用 go 关键字启动一个新的 goroutine
go func() {
// 向通道发送三个不同的字符串
ch <- "Hello"
ch <- "world"
ch <- "!"
// 关闭通道
close(ch)
}()
// 循环从通道接收值,直到通道被关闭
for msg := range ch {
fmt.Println(msg) // 输出:Hello world !
}
}
在这个例子中,我们创建了一个字符串通道 ch
,然后启动了一个新的 goroutine,在其中向通道发送了三个不同的字符串。在主 goroutine 中我们使用 range
语句循环从通道 ch
中接收每个字符串,并将它们打印到控制台上,直到通道被关闭为止。
这个例子展示了如何在多个 goroutine 之间安全地传递数据,以及如何在通道被关闭时停止接收数据。
以下是一些关于Golang复合数据结构体(Struct)类型的一些例子
以下是一些使用 Golang 结构体的例子:
代码语言:javascript复制package main
import "fmt"
// 定义一个表示人的结构体
type Person struct {
Name string
Age int
}
func main() {
// 创建一个新的 Person 对象
p1 := Person{Name: "Alice", Age: 30}
// 访问 Person 的属性
fmt.Println(p1.Name) // 输出:Alice
fmt.Println(p1.Age) // 输出:30
// 可以创建一个匿名结构体
p2 := struct {
Name string
Age int
}{
Name: "Bob",
Age: 40,
}
// 访问匿名结构体的属性
fmt.Println(p2.Name) // 输出:Bob
fmt.Println(p2.Age) // 输出:40
// 可以将结构体作为函数参数和返回值
p3 := createPerson("Charlie", 50)
fmt.Println(p3) // 输出:{Charlie 50}
}
// 创建一个新的 Person 对象并返回
func createPerson(name string, age int) Person {
return Person{
Name: name,
Age: age,
}
}
在这个示例中,我们定义了一个表示人的结构体 Person
,包含两个属性 Name
和 Age
。然后我们创建了一个新的 Person
对象 p1
,并访问了它的属性。
接着我们定义了一个匿名结构体 p2
,也有 Name
和 Age
属性,并访问了它的属性。
最后,我们定义了一个函数 createPerson
,它接收两个参数 name
和 age
,并返回一个新的 Person
对象。我们使用这个函数创建了一个新的 Person
对象 p3
,并打印它的值。
除此之外,结构体还可以用于嵌套和组合,以及实现接口等高级应用。
- 类型转换:在Go中,当需要将一个类型的值转换为另一个类型时,需要使用类型转换操作符
T(v)
,其中T
表示目标类型,v
表示要转换的值。需要注意的是,不是所有类型之间都可以进行转换,只有具有相同底层类型或者满足特定条件的类型之间才可以进行转换。
以下是一些使用 Golang 类型转换的例子:
代码语言:javascript复制package main
import "fmt"
func main() {
// 将整数类型转换为浮点数类型
i := 42
f := float64(i)
fmt.Println(f) // 输出:42.0
// 将浮点数类型转换为整数类型
f = 3.14
i = int(f)
fmt.Println(i) // 输出:3
// 将字符串类型转换为整数类型
s := "42"
i, err := strconv.Atoi(s)
if err != nil {
panic(err)
}
fmt.Println(i) // 输出:42
// 将整数类型转换为字符串类型
s = strconv.Itoa(i)
fmt.Println(s) // 输出:42
// 将接口类型转换为具体类型
var v1 interface{} = "Hello, world!"
s, ok := v1.(string)
if ok {
fmt.Println(s) // 输出:"Hello, world!"
}
// 将指针类型转换为具体类型
var v2 interface{} = &Person{Name: "Alice", Age: 30}
p, ok := v2.(*Person)
if ok {
fmt.Println(p.Name, p.Age) // 输出:"Alice 30"
}
}
// 定义一个表示人的结构体
type Person struct {
Name string
Age int
}
在这个示例中,我们首先展示了如何将整数类型转换为浮点数类型,以及如何将浮点数类型转换为整数类型。
接着我们展示了如何将字符串类型转换为整数类型,并使用 strconv
包中的 Atoi
函数实现了该操作。我们还展示了如何将整数类型转换为字符串类型,并使用 strconv
包中的 Itoa
函数实现了该操作。
然后,我们演示了如何将接口类型转换为具体类型,并使用类型断言实现了该操作。我们还展示了如何将指针类型转换为具体类型,并使用类型断言实现了该操作。
需要注意的是,在类型转换过程中可能会发生类型不匹配或溢出等问题,因此需要谨慎处理。
- 表达式:Go中的表达式由运算符和操作数组成,可以分为算术表达式、比较表达式、逻辑表达式、位运算表达式、赋值表达式等多种类型。
以下是一些使用 Golang 表达式的例子:
代码语言:javascript复制package main
import "fmt"
func main() {
// 算术表达式
a := 1 2*3/4
fmt.Println(a) // 输出:2
// 比较表达式
b := (a == 2)
fmt.Println(b) // 输出:true
// 逻辑表达式
c := !(b && true) || false
fmt.Println(c) // 输出:false
// 位运算表达式
d := 0b1100 & 0b1010
fmt.Printf("bn", d) // 输出:1000
// 赋值表达式
e := 42
e = 8
fmt.Println(e) // 输出:50
// 条件表达式
f := true
g := func() int {
if f {
return 42
} else {
return 0
}
}()
fmt.Println(g) // 输出:42
}
在这个示例中,我们首先展示了如何使用算术表达式,包括加减乘除和求余等操作。
接着我们展示了如何使用比较表达式,包括相等、不等、大于、小于等操作。我们还展示了如何使用逻辑表达式,包括与、或、非等操作。
然后我们演示了如何使用位运算表达式,包括按位与、按位或、异或等操作。需要注意的是,在 Golang 中需要使用前缀 0b
表示二进制数值。
接下来我们展示了如何使用赋值表达式,包括加、减、乘、除等操作。可以看到在这里我们使用了 =
运算符实现了加法操作。
最后我们演示了如何使用条件表达式,包括 if 语句和函数调用等操作。在这个示例中,我们定义了一个匿名函数,并使用它返回不同的值,根据变量 f
的值来决定返回什么。
- 运算符优先级:在Go中,每个运算符都有自己的优先级,当多个运算符同时出现在一个表达式中时,按照优先级从高到低依次计算。需要注意的是,优先级相同的运算符会按照从左到右的顺序依次计算。
以下是一些使用 Golang 运算符优先级的例子:
代码语言:javascript复制package main
import "fmt"
func main() {
// 后置 运算符的优先级最高
a := 1
b := a
fmt.Println(b) // 输出:1
// * 和 / 运算符的优先级高于 和 -
c := 1 2*3/4
fmt.Println(c) // 输出:2
// < 和 > 运算符的优先级低于 == 和 !=
d := (c != 2 && c < 3)
fmt.Println(d) // 输出:false
// && 运算符的优先级低于 ||
e := false || true && true
fmt.Println(e) // 输出:true
// 赋值运算符的优先级低于大部分运算符
f := 1 2
f *= 3 4
fmt.Println(f) // 输出:21
}
在这个示例中,我们首先展示了后置 运算符的优先级最高,即在其他表达式执行完毕后对变量进行自增操作。
接着我们展示了乘法和除法运算符的优先级高于加法和减法运算符。
然后我们演示了比较运算符的优先级低于相等和不等运算符。需要注意的是,在 Golang 中相等和不等运算符是 ==
和 !=
而不是 =
和 <>
。
接下来我们展示了逻辑运算符的优先级,包括 &&
和 ||
。需要注意的是,在 Golang 中逻辑运算符也是短路运算符。
最后我们演示了赋值运算符的优先级低于大部分其他运算符,包括加法和乘法运算符。可以看到在这里我们使用了 *=
运算符实现了乘法操作。
- 类型断言:在Go中,可以通过类型断言操作符
x.(T)
将一个接口类型的值转换为具体类型的值,其中x
表示要转换的值,T
表示目标类型。如果转换成功,返回转换后的值和一个布尔值true
;否则返回零值和一个布尔值false
。
以下是一些使用 Golang 类型断言的例子:
代码语言:javascript复制package main
import "fmt"
func main() {
// 将接口类型转换为具体类型
var v1 interface{} = "Hello, world!"
s, ok := v1.(string)
if ok {
fmt.Println(s) // 输出:"Hello, world!"
}
// 将指针类型转换为具体类型
var v2 interface{} = &Person{Name: "Alice", Age: 30}
p, ok := v2.(*Person)
if ok {
fmt.Println(p.Name, p.Age) // 输出:"Alice 30"
}
// 在 switch 语句中使用类型断言
var v3 interface{} = 42
switch v := v3.(type) {
case int:
fmt.Println("v is an integer:", v)
case float64:
fmt.Println("v is a float64:", v)
default:
fmt.Println("v is of a different type")
}
}
// 定义一个表示人的结构体
type Person struct {
Name string
Age int
}
在这个示例中,我们首先演示了如何将接口类型转换为具体类型,并使用类型断言实现了该操作。我们定义了一个接口类型变量 v1
,并尝试将其转换为字符串类型。
接着我们展示了如何将指针类型转换为具体类型,并使用类型断言实现了该操作。我们定义了一个接口类型变量 v2
,并尝试将其转换为 Person
结构体类型。
然后我们演示了如何在 switch 语句中使用类型断言。我们定义了一个接口类型变量 v3
,并在 switch 语句中尝试将其转换为不同的类型。
需要注意的是,在进行类型断言时,如果类型不匹配会返回 false 和默认值,因此需要谨慎处理。
- 指针:指针是一种特殊类型的变量,存储了内存地址。在Go中,使用
&
操作符获取变量的地址,使用*
操作符获取指针所指向的变量的值。指针可以用于传递函数参数、处理数据结构等方面。
以下是一些使用 Golang 指针的例子:
代码语言:javascript复制package main
import "fmt"
func main() {
// 定义一个指针变量并赋值
var p1 *int
x := 42
p1 = &x
fmt.Println(*p1) // 输出:42
// 使用 new 函数创建指针对象
p2 := new(int)
*p2 = 100
fmt.Println(*p2) // 输出:100
// 函数中使用指针参数
y := 42
increment(&y)
fmt.Println(y) // 输出:43
}
// 将指定变量的值加 1
func increment(p *int) {
*p
}
在这个示例中,我们首先展示了如何定义一个指针变量并赋值。我们通过 &
运算符获取变量 x
的地址,并将其赋值给指针变量 p1
。然后我们使用 *
运算符访问指针所指向的变量的值,并打印出来。
接着我们演示了如何使用 new
函数创建指针对象。我们使用 new
函数创建了一个新的整数类型变量,并将其地址赋值给指针变量 p2
。然后我们使用 *
运算符访问指针所指向的变量的值,并打印出来。
最后,我们演示了如何在函数中使用指针参数。我们定义了一个 increment
函数,并接收一个指针类型参数 p
。函数会将 p
指向的变量的值加 1。在调用函数时,我们传递了变量 y
的地址作为参数,并将其值加 1。最后我们打印出变量 y
的值,可以看到它已经被成功地增加了 1。