抱歉,您的浏览器无法访问本站
本页面需要浏览器支持(启用)JavaScript
了解详情 >

踮起脚尖 另一种高度

变量和常量

在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 语言中,intfloat 的大小依赖于底层计算机的体系结构和操作系统。在大多数平台上,int 类型的大小是 32 位或 64 位,而 float 类型的大小通常是 32 位或 64 位。

具体来说,int 类型在 32 位系统上是 32 位的有符号整数,范围为 -2,147,483,6482,147,483,647,而在 64 位系统上是 64 位的有符号整数,范围为 -9,223,372,036,854,775,8089,223,372,036,854,775,807float 类型在 32 位系统上是 32 位的浮点数,而在 64 位系统上是 64 位的浮点数。

需要注意的是,在不同平台和编译器下,intfloat 的大小可能会有所不同,因此在编写程序时需要谨慎考虑跨平台的兼容性问题。为了确保代码的可移植性,建议使用明确的整数和浮点类型,如 int32int64float32float64

字符串

在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 中前 nold 子串替换为 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,然后创建了两个切片 slice1slice2,它们都共享同一个底层数组 numbers。然后我们修改了切片 slice1slice2 中的元素,最后遍历切片 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)是一种自定义的数据类型,可以将多个不同类型的字段组合在一起,形成一个复合类型。结构体中的每个字段可以有自己的类型和名称,可以用来表示一个实体的各个属性。

初始化

定义结构体可以使用 typestruct 关键字,例如:

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

评论