当前位置: 首页 > article >正文

【Go语言快速上手】第一部分:数据类型(数组、切片、映射)与控制语句

文章目录

  • 一、复合类型
    • Ⅰ 数组
      • 1. 语法
      • 2. 示例
      • 3. 特点
      • 4. 数组的传递
    • Ⅱ 切片
      • 1. 定义
      • 2. 语法
      • 3. 示例
      • 4. 特点
      • 5. 切片的创建
      • 6. 切片的操作
        • 切片的扩展
        • 切片的拷贝
    • Ⅲ 映射
      • 1. 定义
      • 2. 语法
      • 3. 示例
      • 4. 特点
      • 5. 映射的创建
      • 6. 映射的操作
        • 示例:插入、访问和删除
        • 判断键是否存在
        • 示例:判断键是否存在
        • 映射的遍历
    • Ⅳ 映射与数组、切片的区别
    • Ⅴ 结构体
      • 1. 结构体的定义
        • 示例:
      • 2. 创建和初始化结构体
      • 3. 结构体字段的访问
        • 访问结构体字段:
        • 通过指针访问结构体字段:
      • 4. 修改结构体字段
      • 5. 结构体作为函数参数
        • 传递结构体值:
        • 传递结构体指针:
      • 6. 结构体的嵌套
        • 示例:结构体嵌套
      • 7. 结构体与方法
        • 示例:为结构体定义方法
  • 二、控制流
    • `if` 语句
      • `if` 语句中的 `else if`
    • `for` 循环
      • 标准 `for` 循环
      • 无限循环
      • `range` 遍历
    • `switch` 语句
      • 基本语法
      • 省略 `expression`
      • 多个 `case` 合并
      • `fallthrough` 关键字

一、复合类型

Ⅰ 数组

数组是固定长度的序列,一旦定义其长度就不可更改。Go 中的数组是值类型,当我们将一个数组赋值给另一个数组时,会发生值拷贝

1. 语法

var arrayName [length]type

2. 示例

package main

import "fmt"

func main() {
    // 定义一个长度为5的整数数组
    var arr [5]int
    fmt.Println(arr) // [0 0 0 0 0]

    // 初始化数组
    arr2 := [3]int{1, 2, 3}
    fmt.Println(arr2) // [1 2 3]

    // 数组的长度是固定的,不能动态改变
    arr3 := [3]int{4, 5, 6}
    fmt.Println(len(arr3)) // 3
}

3. 特点

  1. 固定长度:数组一旦定义其长度就不能更改。
  2. 值类型:数组是值类型,赋值和传递数组会复制整个数组。
  3. 元素初始化:数组元素的默认值为零值(对于 int 类型来说是 0)。
  4. 内存连续:数组是一个内存连续的块,元素在内存中是连续存储的。

4. 数组的传递

由于 Go 中的数组是值类型,传递数组时会将整个数组拷贝一份到函数中。如果不希望这样,可以通过传递数组的指针来避免拷贝。

func modifyArray(arr [3]int) {
    arr[0] = 10
}

func main() {
    arr := [3]int{1, 2, 3}
    modifyArray(arr)
    fmt.Println(arr) // [1 2 3],数组没有改变
}

如果传递指针:

func modifyArray(arr *[3]int) {
    arr[0] = 10
}

func main() {
    arr := [3]int{1, 2, 3}
    modifyArray(&arr)
    fmt.Println(arr) // [10 2 3],数组被修改
}

Ⅱ 切片

切片是 Go 中的一个重要特性,是动态大小的数组。与数组不同,切片是引用类型,不会复制整个数据结构,而是指向底层数组的一部分。因此切片操作更加灵活高效。

1. 定义

切片是基于数组的一个引用类型,它没有固定的长度,可以动态增长。切片包含三个要素:指向底层数组的指针、切片的长度、切片的容量。

2. 语法

var sliceName []type

3. 示例

package main

import "fmt"

func main() {
    // 定义一个切片
    slice1 := []int{1, 2, 3}
    fmt.Println(slice1) // [1 2 3]

    // 使用 make 函数创建切片
    slice2 := make([]int, 5) // 创建一个长度为5的切片,默认值为0
    fmt.Println(slice2)       // [0 0 0 0 0]

    // 切片的容量
    fmt.Println(cap(slice2))  // 5
    fmt.Println(len(slice2))  // 5
}

4. 特点

  1. 动态大小:切片的长度可以动态改变,可以根据需要扩展。
  2. 引用类型:切片是引用类型,赋值和传递切片时是传递的底层数组的引用。
  3. 零值:切片的零值是 nil,并且没有分配内存空间。
  4. 可伸缩性:切片会根据需要动态扩展容量。
  5. 底层数组:切片实际上是对数组的一个视图,它是一个指向底层数组的指针,具有长度和容量。

5. 切片的创建

可以使用 make 函数创建切片:

slice := make([]int, 5)  // 创建一个长度为5的切片,默认值为零值(0)
slice := make([]int, 5, 10)  // 创建一个长度为5,容量为10的切片

6. 切片的操作

切片支持切片操作,可以从一个切片中提取子切片:

package main

import "fmt"

func main() {
    arr := []int{1, 2, 3, 4, 5}
    slice := arr[1:4] // 从 arr 中提取从索引 1 到索引 3 的部分,结果是 [2 3 4]
    fmt.Println(slice)
}

切片的操作并不会复制底层数组,只是引用了原数组的一部分。

切片的扩展

切片支持动态扩展,当切片的容量不足时,Go 会自动扩展切片的容量。扩展的规则通常是将容量翻倍。

package main

import "fmt"

func main() {
    slice := []int{1, 2, 3}
    slice = append(slice, 4)  // 添加元素到切片
    fmt.Println(slice)        // [1 2 3 4]
}
切片的拷贝

当我们想要拷贝一个切片时,可以使用 copy 函数。

package main

import "fmt"

func main() {
    slice1 := []int{1, 2, 3}
    slice2 := make([]int, len(slice1)) // 创建一个新的切片
    copy(slice2, slice1)               // 将 slice1 的内容拷贝到 slice2
    fmt.Println(slice2)                // [1 2 3]
}

Ⅲ 映射

1. 定义

映射是由键值对组成的集合,Go 中的映射是引用类型。每个键都对应一个值,且每个键在映射中是唯一的。如果使用一个已经存在的键进行赋值,旧的值将被替换。

2. 语法

var mapName map[keyType]valueType

其中,keyType 是映射的键的类型,valueType 是映射的值的类型。

3. 示例

package main

import "fmt"

func main() {
    // 创建一个映射,键为string类型,值为int类型
    var m map[string]int
    fmt.Println(m)  // 输出: nil,未初始化的映射是 nil

    // 初始化映射并添加元素
    m = make(map[string]int)
    m["age"] = 30
    m["score"] = 100

    fmt.Println(m)  // 输出: map[age:30 score:100]

    // 访问映射的值
    fmt.Println(m["age"]) // 输出: 30
    fmt.Println(m["score"]) // 输出: 100

    // 访问不存在的键
    fmt.Println(m["nonexistent"]) // 输出: 0,值的零值(int 类型的零值是 0)
}

4. 特点

  1. 无序性:映射中的键值对是无序的,在迭代映射时,元素的顺序是不确定的。
  2. 引用类型:映射是引用类型,赋值和传递映射时是传递的底层数据的引用。
  3. 键唯一:每个映射的键都是唯一的,不允许重复。如果给已有的键赋值,原来的值会被覆盖。
  4. 自动扩容:映射会根据元素的添加自动扩展。

5. 映射的创建

可以通过 make 函数来创建映射,make 函数接受两个参数:映射的键类型和映射的值类型。

// 创建一个空映射,键类型为string,值类型为int
m := make(map[string]int)

6. 映射的操作

  • 插入键值对:通过 map[key] = value 语法将键值对插入映射。
  • 访问值:通过 map[key] 语法访问映射中某个键对应的值。
  • 删除键值对:通过 delete(map, key) 函数删除某个键值对。
  • 判断键是否存在:通过 value, ok := map[key] 可以判断一个键是否存在。如果键存在,oktrue,否则为 false
示例:插入、访问和删除
package main

import "fmt"

func main() {
    m := make(map[string]int)

    // 插入元素
    m["apple"] = 5
    m["banana"] = 3
    fmt.Println(m)  // 输出: map[apple:5 banana:3]

    // 访问元素
    val, exists := m["apple"]
    if exists {
        fmt.Println("apple exists with value:", val)  // 输出: apple exists with value: 5
    }

    // 删除元素
    delete(m, "banana")
    fmt.Println(m)  // 输出: map[apple:5]
}
判断键是否存在

当访问映射时,如果键不存在,Go 会返回该值的零值(如 0""nil 等)。为了区分键是否存在,可以使用 comma ok 语法:

value, ok := m["key"]
  • 如果键存在,oktruevalue 为该键对应的值。
  • 如果键不存在,okfalsevalue 为该类型的零值。
示例:判断键是否存在
package main

import "fmt"

func main() {
    m := map[string]int{
        "apple": 5,
        "banana": 3,
    }

    // 判断 "apple" 是否存在
    val, exists := m["apple"]
    fmt.Println(val, exists) // 5 true

    // 判断 "grape" 是否存在
    val, exists = m["grape"]
    fmt.Println(val, exists) // 0 false
}
映射的遍历

Go 提供了 for range 语法来遍历映射中的键值对。

package main

import "fmt"

func main() {
    m := map[string]int{
        "apple": 5,
        "banana": 3,
    }

    // 遍历映射
    for key, value := range m {
        fmt.Println(key, value)
    }
}

输出结果可能类似于:

apple 5
banana 3

注意:映射的遍历顺序是不确定的,因为映射是无序的。


Ⅳ 映射与数组、切片的区别

  • 数组:固定长度、值类型、元素位置有序。
  • 切片:动态长度、引用类型、元素位置有序。
  • 映射:无固定顺序、引用类型、键值对唯一。

Ⅴ 结构体

1. 结构体的定义

结构体通过 type 关键字定义,通常每个字段都有一个名称和类型。定义结构体的基本语法如下:

type StructName struct {
    Field1 type1
    Field2 type2
    // 可以继续添加其他字段
}
示例:
type Person struct {
    Name    string
    Age     int
    Address string
}

这个 Person 结构体有三个字段:Name(字符串类型)、Age(整数类型)、Address(字符串类型)。每个字段都有一个名称和类型。

2. 创建和初始化结构体

在 Go 中,可以通过多种方式创建和初始化结构体。

方式 1:使用字面量初始化结构体

可以使用结构体字面量(即直接在定义时给字段赋值)来创建一个结构体对象:

package main

import "fmt"

type Person struct {
    Name    string
    Age     int
    Address string
}

func main() {
    // 使用字面量创建并初始化结构体
    p := Person{
        Name:    "Alice",
        Age:     30,
        Address: "Wonderland",
    }

    fmt.Println(p)  // 输出: {Alice 30 Wonderland}
}

方式 2:使用默认值创建结构体

如果没有指定字段值,字段将使用类型的零值进行初始化(例如 string 类型为 ""int 类型为 0)。

package main

import "fmt"

type Person struct {
    Name    string
    Age     int
    Address string
}

func main() {
    // 使用零值初始化结构体
    p := Person{}

    fmt.Println(p)  // 输出: { 0 }
}

方式 3:使用指针创建结构体

Go 中的结构体是值类型,意味着传递结构体时是将值复制。如果希望修改结构体的值,可以使用结构体指针。

package main

import "fmt"

type Person struct {
    Name    string
    Age     int
    Address string
}

func main() {
    // 创建结构体指针
    p := &Person{
        Name:    "Bob",
        Age:     25,
        Address: "Home",
    }

    fmt.Println(p)  // 输出: &{Bob 25 Home}
}

3. 结构体字段的访问

结构体的字段通过 . 运算符进行访问。可以通过结构体对象(或者结构体指针)访问字段。

访问结构体字段:
package main

import "fmt"

type Person struct {
    Name    string
    Age     int
    Address string
}

func main() {
    p := Person{Name: "John", Age: 28, Address: "New York"}

    // 访问结构体字段
    fmt.Println("Name:", p.Name)    // 输出: Name: John
    fmt.Println("Age:", p.Age)      // 输出: Age: 28
    fmt.Println("Address:", p.Address)  // 输出: Address: New York
}
通过指针访问结构体字段:

当存在结构体指针时,通过 -> 运算符访问字段(其实在 Go 中是通过 * 解引用来实现的):

package main

import "fmt"

type Person struct {
    Name    string
    Age     int
    Address string
}

func main() {
    p := &Person{Name: "Alice", Age: 30, Address: "Wonderland"}

    // 通过指针访问结构体字段
    fmt.Println("Name:", p.Name)    // 输出: Name: Alice
    fmt.Println("Age:", p.Age)      // 输出: Age: 30
    fmt.Println("Address:", p.Address)  // 输出: Address: Wonderland
}

4. 修改结构体字段

结构体的字段可以在创建后进行修改:

package main

import "fmt"

type Person struct {
    Name    string
    Age     int
    Address string
}

func main() {
    p := Person{Name: "John", Age: 28, Address: "New York"}

    // 修改结构体字段的值
    p.Name = "Tom"
    p.Age = 30
    p.Address = "California"

    fmt.Println(p)  // 输出: {Tom 30 California}
}

如果使用的是结构体指针,则可以直接修改字段值。

5. 结构体作为函数参数

结构体可以作为函数的参数进行传递。由于结构体是值类型,传递结构体时会复制整个结构体,这意味着修改副本不会影响原结构体。如果需要修改原结构体,可以传递结构体指针。

传递结构体值:
package main

import "fmt"

type Person struct {
    Name    string
    Age     int
    Address string
}

func changeName(p Person) {
    p.Name = "Changed"  // 只是修改了副本,不会改变原结构体
}

func main() {
    p := Person{Name: "John", Age: 28, Address: "New York"}
    changeName(p)

    fmt.Println(p)  // 输出: {John 28 New York} 原结构体没有改变
}
传递结构体指针:
package main

import "fmt"

type Person struct {
    Name    string
    Age     int
    Address string
}

func changeName(p *Person) {
    p.Name = "Changed"  // 通过指针修改原结构体
}

func main() {
    p := &Person{Name: "John", Age: 28, Address: "New York"}
    changeName(p)

    fmt.Println(p)  // 输出: &{Changed 28 New York} 结构体被修改
}

6. 结构体的嵌套

Go 支持结构体嵌套,可以在结构体中嵌套其他结构体。这有助于实现类似“继承”的功能,但它与传统面向对象编程中的继承不同。

示例:结构体嵌套
package main

import "fmt"

type Address struct {
    Street, City, Country string
}

type Person struct {
    Name    string
    Age     int
    Address Address  // 嵌套 Address 结构体
}

func main() {
    p := Person{
        Name: "John",
        Age:  28,
        Address: Address{
            Street: "123 Main St",
            City:   "New York",
            Country: "USA",
        },
    }

    fmt.Println(p)  // 输出: {John 28 {123 Main St New York USA}}
}

7. 结构体与方法

在 Go 中,可以为结构体定义方法(即函数),这些方法可以操作结构体的字段。方法是与特定类型(结构体)绑定的。

示例:为结构体定义方法
package main

import "fmt"

type Person struct {
    Name    string
    Age     int
}

func (p *Person) Greet() {
    fmt.Println("Hello, my name is", p.Name)
}

func main() {
    p := &Person{Name: "John", Age: 28}
    p.Greet()  // 输出: Hello, my name is John
}

在上述例子中,GreetPerson 类型的方法,使用结构体的指针来调用它。


二、控制流

if 语句

if 语句是 Go 语言中的一种条件控制结构,用于判断给定条件是否成立。如果条件为 true,则执行大括号内的代码,否则执行 else 块中的代码。

基本语法

if condition {
    // 如果 condition 为 true 执行的代码块
} else {
    // 如果 condition 为 false 执行的代码块
}

例子

package main

import "fmt"

func main() {
    age := 20
    if age > 18 {
        fmt.Println("成年人")  // 如果年龄大于18,输出"成年人"
    } else {
        fmt.Println("未成年")  // 如果年龄不大于18,输出"未成年"
    }
}

if 语句中的 else if

当有多个条件时,可以使用 else if 来进行多条件判断:

if condition1 {
    // 如果 condition1 为 true 执行的代码块
} else if condition2 {
    // 如果 condition1 为 false 且 condition2 为 true 执行的代码块
} else {
    // 如果 condition1 和 condition2 都为 false 执行的代码块
}

例子

package main

import "fmt"

func main() {
    age := 20
    if age < 13 {
        fmt.Println("儿童")
    } else if age < 18 {
        fmt.Println("青少年")
    } else {
        fmt.Println("成年人")
    }
}

for 循环

Go 语言的 for 循环有三种常见形式:标准 for 循环、无限循环、以及基于 range 的遍历。

标准 for 循环

这是最常见的循环形式,包含了初始化语句、条件判断语句以及更新语句。

语法:

for initialization; condition; post {
    // 循环体代码
}

例子

package main

import "fmt"

func main() {
    for i := 0; i < 5; i++ {
        fmt.Println(i)  // 输出 0, 1, 2, 3, 4
    }
}

在此例中,i0 开始,每次循环结束时,i 增加 1,直到 i 不满足 i < 5 这个条件时停止循环。

无限循环

Go 中可以使用 for 语句创建无限循环,语法为 for 后面没有条件判断。

例子

package main

import "fmt"

func main() {
    for {
        fmt.Println("无限循环")  // 无限输出 "无限循环"
    }
}

这个循环会一直执行,除非显式地使用 break 语句或发生其他中断操作(例如程序终止)。

range 遍历

range 关键字是 Go 中一种常用的遍历数组、切片、字符串、映射等数据结构的方式。它返回两个值:一个是索引,另一个是元素值。

语法:

for index, value := range collection {
    // 对每个元素执行的操作
}

例子

package main

import "fmt"

func main() {
    arr := []int{10, 20, 30}
    for index, value := range arr {
        fmt.Println(index, value)  // 输出数组的索引和对应的值
    }
}

此例中,indexvalue 分别表示数组中的索引和对应的值,输出会是:

0 10
1 20
2 30

switch 语句

Go 中的 switch 语句是一个多路选择结构,它根据某个表达式的值来选择匹配的 case 语句执行。与传统的 switch 语句不同,Go 的 switch 语句不需要 break 来防止贯穿,每个 case 语句默认是结束的。

基本语法

switch expression {
    case value1:
        // 如果 expression == value1,执行此处代码
    case value2:
        // 如果 expression == value2,执行此处代码
    default:
        // 如果没有匹配到任何 case,执行此处代码
}

例子

package main

import "fmt"

func main() {
    day := 2

    switch day {
    case 1:
        fmt.Println("星期一")
    case 2:
        fmt.Println("星期二")  // 输出 "星期二"
    case 3:
        fmt.Println("星期三")
    default:
        fmt.Println("无效的星期")
    }
}

省略 expression

如果没有给出 expression,则 switch 被当作 switch true 处理,这时每个 case 表达式都会被依次判断,直到匹配到 true

例子

package main

import "fmt"

func main() {
    num := 4

    switch {
    case num < 0:
        fmt.Println("负数")
    case num == 0:
        fmt.Println("零")
    case num > 0 && num < 10:
        fmt.Println("正数小于10")  // 输出 "正数小于10"
    default:
        fmt.Println("其他")
    }
}

多个 case 合并

多个 case 可以合并为一条代码块,只要它们执行相同的操作。

例子

package main

import "fmt"

func main() {
    grade := 'B'

    switch grade {
    case 'A':
        fmt.Println("优秀!")
    case 'B', 'C':  // B 和 C 合并在一起
        fmt.Println("做得不错!")
    case 'D':
        fmt.Println("通过!")
    case 'F':
        fmt.Println("下次加油!")
    default:
        fmt.Println("无效成绩")
    }
}

fallthrough 关键字

fallthrough 关键字允许在一个 case 执行后继续执行下一个 case,即使下一个 case 的条件不满足。

例子

package main

import "fmt"

func main() {
    num := 2

    switch num {
    case 1:
        fmt.Println("1")
    case 2:
        fmt.Println("2")
        fallthrough  // 会进入下一个 case
    case 3:
        fmt.Println("3")
    default:
        fmt.Println("无效数字")
    }
}

输出结果:

2
3

在这个例子中,num2,它会首先输出 2,然后由于 fallthrough,控制流继续进入 case 3 并输出 3


http://www.kler.cn/a/569799.html

相关文章:

  • 【JAVA-数据结构】枚举
  • Storm实时流式计算系统(全解)——中
  • TCP的三次握⼿中为什么是三次?为什么不是两 次、四次?
  • 数据可视化02-PCA降维
  • 【推荐项目】023-游泳俱乐部管理系统
  • 解决Java项目中Maven爆红,三方包下载不下来的问题
  • 基于单片机和C#的电压监测系统设计
  • Docker 学习(一)
  • 如何在 FastAdmin 中实现自定义主题设计
  • 比较Spring AOP和AspectJ
  • Qt QScrollArea 总结
  • iOS UIGestureRecgonizer自动化点击埋点
  • 计算机毕设JAVA——某高校宿舍管理系统(基于SpringBoot+Vue前后端分离的项目)
  • MySql面试总结(一)
  • 鸿蒙自定义组件预览
  • linux 后台执行并输出日志
  • STM32中使用PWM对舵机控制
  • 如何在Python用Plot画出一个简单的机器人模型
  • 【自学笔记】大数据基础知识点总览-持续更新
  • P8651 [蓝桥杯 2017 省 B] 日期问题--注意日期问题中2月的天数 / if是否应该连用