变量和常量
在Go语言中,变量使用关键字 var
声明,常量使用关键字 const
声明。以下是变量和常量的举例说明:
变量的声明和赋值
var a int // 声明一个名为 a 的 int 类型变量,初始值为 0
var b, c int = 1, 2 // 声明两个 int 类型变量 b 和 c,并分别赋值为 1 和 2
d := "Hello, World!" // 使用 := 运算符声明一个 string 类型变量 d,并赋值为 "Hello, World!"
e, f := 3, 4 // 使用 := 运算符声明两个 int 类型变量 e 和 f,并分别赋值为 3 和 4
常量的声明和使用
const PI = 3.1415926 // 声明一个名为 PI 的常量,值为 3.1415926
const a, b = 1, "hello" // 声明两个常量 a 和 b,分别赋值为 1 和 "hello"
const (
Monday = "星期一"
Tuesday = "星期二"
Wednesday = "星期三"
Thursday = "星期四"
Friday = "星期五"
Saturday = "星期六"
Sunday = "星期日"
) // 使用 const 关键字声明一组常量,并分别赋值为相应的字符串
fmt.Println("圆周率为", PI) // 输出常量 PI 的值
注意:常量只能在声明时赋值,一旦赋值后就不能再修改。变量可以在声明时赋初值,也可以在后面再赋值。常量一般用于程序中不变的量,如数学中的圆周率、物理学中的光速等。
数据类型
数字
Go 语言中的数字类型包括整数类型和浮点数类型,下面对它们进行介绍及举例说明:
整数类型
整数类型包括有符号和无符号两种类型,每种类型都有不同的大小和取值范围。
- 有符号整数类型:
- int8:8 位有符号整数类型,取值范围为 -128 到 127。
- int16:16 位有符号整数类型,取值范围为 -32768 到 32767。
- int32:32 位有符号整数类型,取值范围为 -2147483648 到 2147483647。
- int64:64 位有符号整数类型,取值范围为 -9223372036854775808 到 9223372036854775807。
- 无符号整数类型:
- uint8:8 位无符号整数类型,取值范围为 0 到 255。
- uint16:16 位无符号整数类型,取值范围为 0 到 65535。
- uint32:32 位无符号整数类型,取值范围为 0 到 4294967295。
- uint64:64 位无符号整数类型,取值范围为 0 到 18446744073709551615。
以下是一些使用整数类型的例子:
var a int8 = 127
var b int16 = -32768
var c uint32 = 4294967295
浮点数类型
浮点数类型分为 float32 和 float64 两种类型,分别占用 4 字节和 8 字节的空间,可以表示小数点后面带有多位的数字。
以下是一些使用浮点数类型的例子:
var a float32 = 3.1415926
var b float64 = 1.23456789
注意:由于浮点数在计算机内部的存储方式是近似值,因此在比较浮点数时,应该使用一个误差范围来判断它们是否相等,而不能直接使用等于号进行比较。
[!WARNING|style:flat]
在 Go 语言中,
int
和float
的大小依赖于底层计算机的体系结构和操作系统。在大多数平台上,int
类型的大小是 32 位或 64 位,而float
类型的大小通常是 32 位或 64 位。具体来说,
int
类型在 32 位系统上是 32 位的有符号整数,范围为-2,147,483,648
到2,147,483,647
,而在 64 位系统上是 64 位的有符号整数,范围为-9,223,372,036,854,775,808
到9,223,372,036,854,775,807
。float
类型在 32 位系统上是 32 位的浮点数,而在 64 位系统上是 64 位的浮点数。需要注意的是,在不同平台和编译器下,
int
和float
的大小可能会有所不同,因此在编写程序时需要谨慎考虑跨平台的兼容性问题。为了确保代码的可移植性,建议使用明确的整数和浮点类型,如int32
、int64
、float32
和float64
。
字符串
在Go语言中,字符串类型表示文本数据。字符串是不可变的,意味着一旦创建,就不能修改其内容。在Go中,字符串类型用string
关键字来声明。
初始化
可以使用双引号""
或反引号来初始化字符串类型。双引号字符串是Go语言中的常规字符串,它支持转义序列,如\n
和\t
,反引号字符串是原始字符串字面值,可以包含多行文本和任何字符,不支持转义序列。
下面是一些初始化字符串类型的示例:
// 使用双引号初始化字符串
s1 := "Hello, world!"
fmt.Println(s1) // Output: Hello, world!
// 使用反引号初始化字符串
s2 := `This is a multi-line
string literal`
fmt.Println(s2)
// Output:
// This is a multi-line
// string literal
字符串操作
// 可以使用 + 运算符来拼接字符串
s := "Hello, " + "World!"
// 可以使用切片语法来获取字符串的一部分
fmt.Println(s[0:5]) // 输出:Hello
// 可以使用 range 关键字来遍历字符串中的每个字符
for _, c := range s {
fmt.Println(string(c))
}
内置函数
Go语言提供了很多用于处理字符串的内置函数,包括:
len()
:返回字符串的长度strconv.Atoi()
:将字符串转换为整数。strconv.ParseFloat()
:将字符串转换为浮点数。strconv.ParseBool()
:将字符串转换为布尔值。strconv.Itoa()
:将整数转换为字符串。strconv.FormatFloat()
:将浮点数转换为字符串。strconv.FormatBool()
:将布尔值转换为字符串。strings.ToLower()
:将字符串转换为小写strings.ToUpper()
:将字符串转换为大写strings.Contains(s, substr string) bool
:判断字符串s
是否包含子串substr
,返回布尔值。strings.HasPrefix(s, prefix string) bool
:判断字符串s
是否以前缀prefix
开始,返回布尔值。strings.HasSuffix(s, suffix string) bool
:判断字符串s
是否以后缀suffix
结束,返回布尔值。strings.Index(s, substr string) int
:查找字符串s
中子串substr
第一次出现的位置,返回位置索引(如果没有找到,则返回-1
)。strings.LastIndex(s, substr string) int
:查找字符串s
中子串substr
最后一次出现的位置,返回位置索引(如果没有找到,则返回-1
)。strings.Count(s, substr string) int
:计算字符串s
中子串substr
出现的次数,返回出现次数。strings.Replace(s, old, new string, n int) string
:将字符串s
中前n
个old
子串替换为new
,返回新字符串。strings.Split(s, sep string) []string
:将字符串s
按照分隔符sep
分割为多个子串,并将它们存储在一个字符串切片中,返回切片。strings.Join(a []string, sep string) string
:将一个字符串切片a
中的所有元素按照分隔符sep
连接为一个字符串,返回连接后的字符串。strings.Trim(s string, cutset string) string
:去除字符串s
两侧的cutset
中包含的字符,返回新的字符串。strings.TrimSpace(s string) string
:去除字符串s
两侧的空格字符,返回新的字符串。strings.TrimLeft(s string, cutset string) string
:去除字符串s
左侧的cutset
中包含的字符,返回新的字符串。strings.TrimRight(s string, cutset string) string
:去除字符串s
右侧的cutset
中包含的字符,返回新的字符串。
数组
在Go语言中,数组类型是一种固定长度且类型相同的数据结构。数组的长度在声明时就需要确定,且不可更改。在Go中,数组类型使用[n]T
表示,其中n
表示数组的长度,T
表示数组元素的类型。
初始化
可以使用数组字面值来初始化数组类型。数组字面值是一种简单的语法,用于创建具有固定大小的数组,其中包含预定义的元素。
下面是一些初始化数组类型的示例:
// 声明一个长度为3的整数数组
var a [3]int
fmt.Println(a) // Output: [0 0 0]
// 初始化数组的元素值
a[0] = 1
a[1] = 2
a[2] = 3
fmt.Println(a) // Output: [1 2 3]
// 使用数组字面值初始化数组
b := [3]int{1, 2, 3}
fmt.Println(b) // Output: [1 2 3]
// 使用...让编译器自动计算数组长度
c := [...]int{1, 2, 3, 4, 5}
fmt.Println(c) // Output: [1 2 3 4 5]
数组操作
在 Go 语言中,数组类型支持一些基本的操作,如遍历、修改、拷贝等。
遍历数组
可以使用for
循环遍历数组,也可以使用range
关键字来遍历数组。
a := [3]int{1, 2, 3}
// 使用for循环遍历数组
for i := 0; i < len(a); i++ {
fmt.Println(a[i])
}
// Output:
// 1
// 2
// 3
// 使用range关键字遍历数组
for i, v := range a {
fmt.Println(i, v)
}
// Output:
// 0 1
// 1 2
// 2 3
修改数组
要修改数组中的元素,只需使用下标运算符([]
)来指定要修改的元素的位置,并将新值赋给它即可。
a := [3]int{1, 2, 3}
a[1] = 4
fmt.Println(a) // Output: [1 4 3]
内置函数
在Go语言中,数组类型没有很多内置函数,只有两个内置函数可以使用:
len(array)
:返回数组的长度cap(array)
:返回数组的容量,对于数组类型,容量和长度是相等的copy(destSlice, srcSlice []T) int
:将源切片中的元素复制到目标切片中,返回实际复制的元素个数
以下是一些使用数组内置函数的示例:
a := [3]int{1, 2, 3}
fmt.Println(len(a)) // Output: 3
fmt.Println(cap(a)) // Output: 3
b := [3]int{}
copy(b[:], a[:]) // 需要将数组转换为切片才能使用copy()函数,这是因为copy()函数只能操作切片。
fmt.Println(b) // Output: [1 2 3]
切片
Go中的切片(Slice)是一个动态数组,是对数组的封装。与数组相比,切片具有更高的灵活性和便利性,可以动态地增加或减少其容量和长度。
切片本身并不存储数据,它只是一个对底层数组的引用。
初始化
在Go语言中,切片类型的定义格式为:[]T
,其中,T
表示切片中元素的类型。例如,一个由整数组成的切片的类型为[]int
。
以下是一些初始化和定义切片的示例:
// 定义一个长度为2的字符串切片,并初始化其元素的值
a := []string{"hello", "world"}
// 使用make函数创建一个长度为5、容量为10的整数切片
b := make([]int, 5, 10)
// 定义一个空的字符串切片,则该切片的长度为0
var c []string
// 声明一个整数切片,但不初始化,则该切片的长度为0
var d []int
上面创建切片时,如果指定了长度和容量,则切片的长度为指定的长度,容量为指定的容量;如果只指定了长度,则容量为长度;如果两者都没有指定,则长度和容量都为 0。
内置函数
len(slice)
:获取切片的长度。cap(slice)
:获取切片的容量。append(slice, elems...)
:向切片的末尾追加一个或多个元素,返回新的切片。copy(destSlice, srcSlice []T) int
:将源切片中的元素复制到目标切片中,返回实际复制的元素个数。
以下是一些使用切片函数的示例:
// 获取切片的长度和容量
a := []int{1, 2, 3, 4, 5}
fmt.Println(len(a)) // Output: 5
fmt.Println(cap(a)) // Output: 5
// 向切片追加元素
b := []string{"hello", "world"}
b = append(b, "!")
fmt.Println(b) // Output: [hello world !]
// 复制切片
c := []int{4, 5, 6}
d := make([]int, 3)
copy(d, c)
fmt.Println(d) // Output: [4 5 6]
切片的扩容
当切片的长度不足以容纳新的元素时,Go 语言会自动扩容切片的容量。切片的扩容机制如下:
- 当切片的容量小于 1024 时,每次扩容后容量增加一倍。
- 当切片的容量大于等于 1024 时,每次扩容后容量增加 25%。
当创建多个切片时,如果它们底层引用的是同一个数组,则它们之间会共享底层数组。以下是一个例子:
// 创建一个长度为 5,容量为 10 的切片
numbers := make([]int, 5, 10)
// 创建两个切片,它们共享同一个底层数组
slice1 := numbers[1:3]
slice2 := numbers[2:5]
// 修改切片中的元素
slice1[0] = 10
slice2[1] = 20
// 遍历切片中的所有元素,发现它们都已经被修改了
for _, v := range numbers {
fmt.Println(v)
}
在上面的例子中,我们创建了一个长度为 5,容量为 10 的切片 numbers
,然后创建了两个切片 slice1
和 slice2
,它们都共享同一个底层数组 numbers
。然后我们修改了切片 slice1
和 slice2
中的元素,最后遍历切片 numbers
中的所有元素,发现它们都已经被修改了。
需要注意的是,如果修改了其中一个切片的长度或容量,那么它们之间就不再共享同一个底层数组了。例如,如果我们将 slice1
的长度扩展到 3,那么它就不再共享底层数组了:
slice1 = append(slice1, 30)
// 修改 slice1 后,它就不再共享底层数组了
for _, v := range numbers {
fmt.Println(v)
}
[!WARNING|style:flat]
当一个切片的长度超过了其容量时,底层数组就会重新分配内存,新的底层数组会有足够的容量来容纳新的元素,然后将原来的元素复制到新的底层数组中,并将切片指向新的底层数组。因此,无论是使用数组还是使用长度为0的切片作为底层数组,在进行切片扩容操作时,都有可能失去与其他切片共享底层数组的效果。所以在使用多个切片共享底层数组时,需要特别注意切片的容量和扩容操作。
字典
在 Go 中,字典类型使用map
关键字来定义。字典是一种键值对的集合,其中每个键必须是唯一的,而每个值可以是任何类型。Map 是一种无序的数据结构,可以通过键来获取相应的值。
初始化
可以使用make()
函数来初始化一个字典,或者使用字面量初始化一个字典。
以下是定义、初始化和使用字典的示例:
// 使用 make 函数创建一个空的 Map,键的类型为 string,值的类型为 int
var scoreMap map[string]int
scoreMap = make(map[string]int)
// 创建一个同时包含多个键值对的 Map
userInfo := map[string]string{
"name": "John",
"age": "20",
"gender": "male",
}
字典操作
// 获取 Map 中某个键的值
age := userInfo["age"]
// 向 Map 中添加键值对
userInfo["email"] = "john@example.com"
// 修改 Map 中某个键的值
userInfo["age"] = "21"
// 判断 Map 中是否包含某个键
if _, ok := userInfo["email"]; ok {
fmt.Println("存在键 email,值为", userInfo["email"])
} else {
fmt.Println("不存在键 email")
}
// 遍历 Map 中所有的键值对
for k, v := range userInfo {
fmt.Println(k, v)
}
// 遍历 Map 中所有的键
for k := range userInfo {
fmt.Println(k)
}
// 遍历 Map 中所有的值
for _, v := range userInfo {
fmt.Println(v)
}
内置函数
len(map)
函数返回字典中键值对的数量delete(key)
函数用于从字典中删除一个键值对
以下是一些使用字典函数的示例:
ages := map[string]int{
"Alice": 25,
"Bob": 30,
"Charlie": 35,
}
fmt.Println(len(ages)) // 输出:3
delete(ages, "Bob") // 删除键为 "Bob" 的键值对
fmt.Println(ages) // 输出:map[Alice:25 Charlie:35]
[!TIP|style:flat]
在使用 Map 时,需要注意遍历的顺序是无序的,因为 Map 中的键是无序的。
在访问 Map 中不存在的键时,会返回该键对应值类型的零值,不会报错。
结构体
在 Go 中,结构体(Struct)是一种自定义的数据类型,可以将多个不同类型的字段组合在一起,形成一个复合类型。结构体中的每个字段可以有自己的类型和名称,可以用来表示一个实体的各个属性。
初始化
定义结构体可以使用 type
和 struct
关键字,例如:
type Person struct {
Name string
Age int
Address string
}
上面的代码定义了一个名为 Person
的结构体,它包含了三个字段:Name
(字符串类型)、Age
(整数类型)和 Address
(字符串类型)。
结构体变量的初始化可以使用多种方式,例如:
// 方式1:使用字段名初始化
p1 := Person{Name: "Alice", Age: 25}
// 方式2:按照字段定义的顺序初始化
p2 := Person{"Bob", 30}
// 方式3:使用 new 函数创建一个指向结构体的指针,然后使用解引用符初始化字段
p3 := new(Person)
p3.Name = "Charlie"
p3.Age = 35
fmt.Println(p1) // 输出:{Alice 25}
fmt.Println(p2) // 输出:{Bob 30}
fmt.Println(p3) // 输出:&{Charlie 35}
以上是结构体的初始化方式,其中第 3 种方式使用 new
函数创建一个指向结构体的指针,然后使用解引用符初始化字段。需要注意的是,在使用 new
函数创建指向结构体的指针时,会将结构体的字段初始化为零值。
结构体操作
在 Go 中,结构体字段的获取、修改和删除可以通过结构体变量的点操作符.
来实现。
结构体字段获取:使用点操作符.
加上字段名称来获取结构体中的字段的值。 例如:
type Person struct {
Name string
Age int
}
p := Person{Name: "Alice", Age: 25}
fmt.Println(p.Name) // 输出:Alice
fmt.Println(p.Age) // 输出:25
结构体字段修改:使用点操作符.
加上字段名称和新的值来修改结构体中的字段的值。 例如:
type Person struct {
Name string
Age int
}
p := Person{Name: "Alice", Age: 25}
p.Name = "Bob"
p.Age = 30
fmt.Println(p) // 输出:{Bob 30}
- 结构体字段删除:不能直接删除结构体中的字段,但可以将其设置为对应类型的零值,如 0、空字符串、nil 等。例如:
type Person struct {
Name string
Age int
}
p := Person{Name: "Alice", Age: 25}
p.Name = "" // 删除Name字段的值
fmt.Println(p) // 输出:{ 25}
格式化输出
在 Go 中,可以使用 fmt
包提供的函数进行格式化输出。常用的格式化输出函数包括:
fmt.Printf()
:按照指定的格式将数据输出到标准输出设备。fmt.Println()
:将参数格式化成字符串后,按照默认格式输出到标准输出设备。fmt.Sprintf()
:按照指定的格式将数据格式化为字符串。fmt.Errorf()
:将错误信息格式化为字符串。
格式化输出的格式由一个字符串控制,其中包含普通文本和转换说明符。转换说明符以百分号 %
开头,其后紧跟一个字符,用于表示要输出的数据类型和格式。常用的转换说明符包括:
%v
:表示任意类型的值,按照默认格式输出。%d
:表示整数类型的值,按照十进制格式输出。%f
:表示浮点数类型的值,按照默认格式输出。%s
:表示字符串类型的值,按照默认格式输出。
下面是一些具体的例子:
package main
import "fmt"
func main() {
// 格式化输出整数和字符串
age := 20
name := "Alice"
fmt.Printf("My name is %s and I'm %d years old.\n", name, age)
// 格式化输出浮点数
pi := 3.1415926
fmt.Printf("The value of pi is approximately %f.\n", pi)
fmt.Printf("The value of pi is approximately %.2f.\n", pi)
// 格式化输出任意类型的值
a := []int{1, 2, 3}
fmt.Printf("%v\n", a)
// 格式化输出到字符串
s := fmt.Sprintf("My name is %s and I'm %d years old.", name, age)
fmt.Println(s)
// 可以一次输出多个参数,每个参数之间会自动添加一个空格,并在输出完成后自动换行。
fmt.Println("My name is", name, "and I'm", age, "years old.")
}
输出结果为:
My name is Alice and I'm 20 years old.
The value of pi is approximately 3.141593.
The value of pi is approximately 3.14.
[1 2 3]
My name is Alice and I'm 20 years old.
My name is Alice and I'm 20 years old.
运算符
在 Go 中,常用的运算符包括:
算术运算符:
+
(加)、-
(减)、*
(乘)、/
(除)、%
(取余)。
a := 10
b := 3
fmt.Println(a + b) // 13
fmt.Println(a - b) // 7
fmt.Println(a * b) // 30
fmt.Println(a / b) // 3
fmt.Println(float64(a) / float64(b) // 3.3333333333333335
fmt.Println(a % b) // 1
比较运算符:
==
(等于)、!=
(不等于)、<
(小于)、>
(大于)、<=
(小于等于)、>=
(大于等于)。
a := 10
b := 3
fmt.Println(a == b) // false
fmt.Println(a != b) // true
fmt.Println(a < b) // false
fmt.Println(a > b) // true
fmt.Println(a <= b) // false
fmt.Println(a >= b) // true
逻辑运算符:
&&
(逻辑与)、||
(逻辑或)、!
(逻辑非)。
a := true
b := false
fmt.Println(a && b) // false
fmt.Println(a || b) // true
fmt.Println(!a) // false
位运算符:
&
(按位与)、|
(按位或)、^
(按位异或)、<<
(左移)、>>
(右移)。
a := 0b1010 // 10
b := 0b1100 // 12
fmt.Printf("%b\n", a&b) // 1000(按位与)
fmt.Printf("%b\n", a|b) // 1110(按位或)
fmt.Printf("%b\n", a^b) // 0110(按位异或)
fmt.Printf("%b\n", a<<1) // 10100(左移)
fmt.Printf("%b\n", b>>1) // 110(右移)
赋值运算符:
=
,+=
,-=
,*=
,/=
,%=
,&=
,|=
,^=
,<<=
,>>=
。
a := 10
a += 5 // 等价于 a = a + 5
fmt.Println(a) // 15
b := 3
b *= 4 // 等价于 b = b * 4
fmt.Println(b) // 12
其他运算符:
&
(取地址)、*
(指针取值)、<-
(发送或接收通道数据)。
a := 10
p := &a // 取地址
fmt.Println(*p) // 10(指针取值)
ch := make(chan int)
go func() {
ch <- 10 // 发送通道数据
}()
x := <- ch // 接收通道数据
fmt.Println(x) // 10