Golang 方法 Golang

package main

import "fmt"

// 方法

// 方法 是一种与特定类型关联的函数。
// 它与函数的不同之处在于,方法绑定到某个类型上,可以通过该类型的实例调用。
// Go 中的函数和方法在语法上非常相似,区别主要在于方法有一个接收者(Receiver),它指定了该方法属于哪个类型

//基本定义
//在 Go 中,方法是与类型关联的函数。方法的定义与函数类似,但方法定义需要指定一个 接收者,接收者是方法的“主人”,即该方法操作的类型实例
// func (receiver variable) MethodName(parameters) returnType {
//  // 方法体
// }

// receiver:接收者,表示方法绑定的类型。它类似于其他语言中的 this 或 self,但在 Go 中,接收者是显式的,在方法定义时直接指定。
// MethodName:方法名。
// parameters:参数列表,和普通函数一样。
// returnType:返回类型(可以没有)。

// 定义一个结构体类型
type Rectangle struct {
    Width, Height int
}

// 定义一个方法,计算矩形的面积
func (r Rectangle) Area() int {
    return r.Width * r.Height
}

// 引用接收者方法
func (r *Rectangle) SetWidth(width int) {
    r.Width = width  // 修改原对象
}

func main(){
    tr := Rectangle{
        Width:5,
        Height:10,
    }
    // 调用结构体的Area方法
    fmt.Println(tr.Area())

    // 值接收者 vs 引用接收者

    // 值接收者
    // 值接收者意味着方法接收的是该类型的副本(拷贝)。这种方式会复制接收者对象,因此对于大型对象,可能会带来性能损失。
    // 用值接收者定义的方法不能修改接收者的值。

    // 例如下面这种方法,无法修改原始Rectangle的Width,因为接收者是值类型
    // func (r Rectangle) SetWidth(width int) {
    //  r.Width = width
    // }

    // 引用接收者
    //引用接收者意味着方法接收的是该类型的指针,可以修改接收者的值。通常情况下,使用引用接收者可以提高性能,避免不必要的拷贝,尤其是当接收者是大型结构体时。
    //使用指针接收者的方法可以修改接收者实例的字段。

    // 这样,SetWidth 方法就能修改原始的 Rectangle 实例,因为接收者是指针类型。
    // func (r *Rectangle) SetWidth(width int) {
    //  r.Width = width
    // }

    //引用接收方法,可以修改tr的值
    tr.SetWidth(30)
    fmt.Println(tr)

    // 方法的传递方式
    // Go 方法有两个主要传递方式:值传递 和 指针传递。在方法定义时,选择值接收者还是指针接收者,决定了传递方式。
    // 值接收者:每次调用方法时,都会传递该类型的副本(拷贝),适用于方法不修改接收者的情况。
    // 指针接收者:每次调用方法时,都会传递该类型的指针,适用于方法可能修改接收者的情况,且能避免拷贝开销。

    // 方法与接口
    // 在 Go 中,方法是与接口的实现紧密相关的。一个类型如果实现了接口中的所有方法,则该类型就实现了该接口,而不需要显式声明。Go 的接口是隐式实现的

    // 示例
    // // 定义一个接口
    // type Shape interface {
    //  Area() int
    // }

    // // 定义一个结构体
    // type Rectangle struct {
    //  Width, Height int
    // }

    // // 实现 Shape 接口的 Area 方法
    // func (r Rectangle) Area() int {
    //  return r.Width * r.Height
    // }

    // func main() {
    //  var s Shape
    //  rect := Rectangle{Width: 5, Height: 4}

    //  s = rect // rect 实现了 Shape 接口
    //  fmt.Println("Area of Shape:", s.Area())  // 输出: Area of Shape: 20
    // }

    // 方法与匿名字段(嵌入式结构体)
    // Go 允许通过匿名字段(embedding)实现方法的继承。通过将一个结构体嵌入到另一个结构体中,嵌入的结构体的方法可以被外部结构体调用

    //示例
    // 定义一个基础结构体
    // type Shape struct {
    //  Width, Height int
    // }

    // func (s Shape) Area() int {
    //  return s.Width * s.Height
    // }

    // // 定义一个继承 Shape 的结构体
    // type Rectangle struct {
    //  Shape // 匿名字段,继承了Shape的方法
    // }

    // func main() {
    //  rect := Rectangle{Shape: Shape{Width: 5, Height: 4}}
    //  fmt.Println("Area of Rectangle:", rect.Area())  // 输出: Area of Rectangle: 20
    // }
    // 在这个例子中,Rectangle 嵌入了 Shape 结构体,从而继承了 Shape 的 Area 方法。这样可以避免重复实现相同的方法

    // 方法的组合与封装
    //方法也可以作为组合的一部分来封装其他类型的行为。这种方式通常用于实现更复杂的类型,封装细节,暴露公共接口

    // 示例
    // 定义一个基础类型
    // type Circle struct {
    //  Radius int
    // }

    // func (c Circle) Area() int {
    //  return 3 * c.Radius * c.Radius
    // }

    // // 定义一个更复杂的类型,组合了 Circle
    // type ColoredCircle struct {
    //  Circle // 组合 Circle
    //  Color  string
    // }

    // func main() {
    //  coloredCircle := ColoredCircle{
    //      Circle: Circle{Radius: 5},
    //      Color:  "Red",
    //  }
    //  fmt.Println("Area of Colored Circle:", coloredCircle.Area()) // 输出: Area of Colored Circle: 75
    //  fmt.Println("Color:", coloredCircle.Color)  // 输出: Color: Red
    // }

    // 方法与 Goroutine
    // Go 中的并发编程依赖于 goroutine 和通道,方法也可以用来在 goroutine 中执行某些操作。

    // type Counter struct {
    //  Count int
    // }

    // // 方法:计数
    // func (c *Counter) Increment() {
    //  c.Count++
    // }

    // // 启动多个 goroutine
    // func main() {
    //  counter := Counter{Count: 0}
    //  for i := 0; i < 5; i++ {
    //      go func() {
    //          counter.Increment()
    //      }()
    //  }

    //  // 等待 goroutines 完成
    //  time.Sleep(time.Second)
    //  fmt.Println("Counter value:", counter.Count) // 输出:Counter value: 5
    // }

    //总结
    // Go 中的方法与函数非常相似,但有一个关键区别:方法与特定类型(例如结构体)绑定。理解 Go 方法的以下特性对于编写高效且易于维护的 Go 代码至关重要
    // 接收者:方法需要一个接收者,接收者决定了方法属于哪个类型。
    // 值接收者与指针接收者:方法可以使用值接收者或指针接收者,选择哪种接收者取决于是否需要修改接收者的数据。
    // 方法与接口:Go 中的方法与接口息息相关,类型实现接口不需要显式声明,只要方法匹配即可。
    // 嵌入式结构体:通过嵌入其他结构体,可以轻松实现代码复用和组合。
    // 方法与并发:方法可以结合 goroutine 和通道来实现并发编程。

}

杨佳乐 发布于  2025-1-10 14:12 

Golang 函数 Golang

package main

import "fmt"

// 函数
func main(){

    // Go 语言的函数(Function)是构建程序的基本单元之一。函数封装了某些行为或操作,并且通过参数传递数据,最终通过返回值将结果返回给调用者。
    // Go 的函数具备简洁和灵活的特性,例如支持多返回值、匿名函数、闭包等

    // 定义函数基本语法
    // func FunctionName(parameterList) returnType {
    //  // 函数体
    //  return returnValue
    // }

    num1 := 10
    num2 := 20

    //无返回值函数
    tfunc(num1,num2)

    //单个返回值函数
    fmt.Println(add(num1,num2))

    //多返回值函数
    q,r := divide(num1,num2)
    fmt.Println("q,r",q,r)

    // 命名返回值
    // 如果函数的返回值具名,可以在函数声明中指定返回值的变量名,这样可以省略 return 中的变量名,直接返回值。
    // func divideTo(a, b int) (quotient, remainder int) {
    //  quotient = a / b
    //  remainder = a % b
    //  return
    // }

    // 参数传递
    //Go 中的参数传递默认是 值传递,即函数内对参数的修改不会影响外部变量。也就是说,函数接收到的是参数的副本,修改副本不会影响原始数据

    numa := 5
    modifyValue(numa) // 给参数 + 1
    fmt.Println("numa",numa) // 返回5 不会改变

    //引用传递 (要使用&传递内存地址)
    modifyPointer(&numa) // 参数 * 2
    fmt.Println("numa",numa) // 返回10 会修改numa数据

    //变长参数
    //Go 函数支持变长参数(variadic function),允许传递任意数量的参数。变长参数在函数定义中使用 ... 来表示,变长参数会被当作一个切片处理
    // 变长参数函数
    // func sum(nums ...int) int {
    //  total := 0
    //  for _, num := range nums {
    //      total += num
    //  }
    //  return total
    // }
    // fmt.Println(sum(1, 2, 3)) 
    fmt.Println(addsnum(1,2,3,4,5))

    // 匿名函数
    // 匿名函数(Lambdas)是没有名字的函数,通常用于传递给其他函数或者作为闭包使用。
    func(a, b int) {
        fmt.Println(a + b)
    }(5, 7) // 输出 12

    // 匿名函数可以赋值给变量,并且可以像普通函数一样调用
    oneaddfunc := func(a,b int) int {
        return a + b
    }
    fmt.Println(oneaddfunc(1,2)) // 输出 3

    // 闭包(Closure)
    // 闭包是一个函数,它“记住”并且可以访问它被创建时的作用域中的变量,即使外部函数已经返回。Go 的匿名函数通常是闭包
    // func adder() func(int) int {
    //  sum := 0
    //  return func(x int) int {
    //      sum += x
    //      return sum
    //  }
    // }
    // 函数 adder 返回了一个闭包,闭包可以访问 sum 变量并且修改它。
    // 即使 adder 函数已经返回,闭包仍然记住了 sum 的值。

    add := adder() // 返回一个闭包
    fmt.Println(add(5)) // 返回 5
    fmt.Println(add(10)) // 返回 15

    add2 := adder()
    fmt.Println(add2(5)) // 返回 5
    fmt.Println(add2(10)) // 返回 15

    // 递归函数
    // 递归是指函数直接或间接调用自身。Go 支持递归,递归的基本形式和其他编程语言一样
    // 递归函数,计算阶乘
    // func factorial(n int) int {
    //  if n == 0 {
    //      return 1
    //  }
    //  return n * factorial(n-1)
    // }
    fmt.Println(factorial(10))

    // defer 关键字
    // Go 语言中的 defer 语句用于确保在函数执行结束时(无论是正常返回还是异常终止)执行某些操作。defer 语句会被推迟到函数执行结束时才执行。

    deferfunc1 := func(){
        defer fmt.Println("最后执行这里")
        fmt.Println("优先执行这里")
    }

    deferfunc1()

    // 多个defer语句
    // 如果函数中有多个 defer 语句,它们会按 后进先出(LIFO) 的顺序执行。

    deferfunc2 := func(){
        defer fmt.Println("1")
        defer fmt.Println("2")
        defer fmt.Println("3")
    }

    deferfunc2()
    // 1
    // 2
    // 3

    // 总结
    // 多返回值:函数可以返回多个值,常用于返回多个计算结果。
    // 参数传递:Go 的函数采用值传递,可以通过传递指针实现引用传递。
    // 变长参数:可以传递任意数量的参数,参数在函数内部以切片形式处理。
    // 匿名函数:可以定义没有名字的函数,通常用于回调或闭包。
    // 闭包:函数可以捕获并记住外部变量的状态,即使外部函数已经返回。
    // 递归:函数可以调用自身,以解决一些递归问题。
    // defer 语句:用于延迟执行某些操作,通常用于资源清理工作。

}
// func:关键字,表示定义函数。
// FunctionName:函数的名称。
// parameterList:参数列表,多个参数用逗号分隔。
// returnType:返回类型,如果函数没有返回值,返回类型是 void。
// return:返回的值,返回值的类型要与声明的返回类型匹配。

// 一个简单的加法函数
func add(a int,b int) int {
    return a + b
}

//一个没有返回值的函数
func tfunc(a int,b int) {
    fmt.Println("a,b",a,b)
}

//多个返回值函数
func divide(a,b int)(int,int){
    quotient := a / b
    remainder := a % b
    return quotient, remainder
}

//值传递函数
func modifyValue(x int) {
    x += 1
}

// 引用传递(指针传递)
func modifyPointer(x *int) {
    *x = *x * 2
}

// 变长函数
// nums ...int 表示一个变长参数,它是一个切片
// 在函数体内,nums 可以像普通切片一样使用
func addsnum(nums ...int) int {
    total := 0

    for _,num := range nums{
        total += num
    }

    return total
}

// 闭包函数
func adder() func(int) int {
    num := 0
    return func(x int) int {
        num += x
        return num
    }
}

//递归函数
func factorial(n int) int {
    if n == 0 {
        return 1
    }
    return n * factorial(n-1)
}

杨佳乐 发布于  2025-1-10 11:24 

Golang 控制结构 Golang

package main

import "fmt"

// 控制结构
func main(){

    // if 语句
    // Go 中的 if 语句用于执行条件判断,语法与大多数编程语言类似,但 Go 的 if 语句没有括号,条件表达式直接放在 if 后面

    // if condition {
    //  // 条件为真时执行的代码
    // }
    x := 10

    if x == 10 {
        fmt.Println("x = 10")
    }

    // if else 语句
    // if condition {
    //  // 条件为真时执行的代码
    // } else {
    //  // 条件为假时执行的代码
    // }
    if x > 10 {
        fmt.Println("x > 10")
    } else {
        fmt.Println("x <= 10")
    }

    // if-else if-else 语句
    // 可以使用多个 else if 来处理多条件判断。
    if x == 1 {
        fmt.Println("x = 1")
    } else if x == 2 {
        fmt.Println("x = 2")
    } else if x == 3 {
        fmt.Println("x = 3")
    } else {
        fmt.Println("x != 1 && x != 2 && x != 3")
    }

    // if条件语句中的短声明
    // Go 的 if 语句支持在条件中声明和初始化变量,这在条件语句中很常见

    if testa := 10; testa > 0 {
        fmt.Println("testa > 0",testa)
    }
    //这里的 testa 只在 if 语句块内有效。
    //fmt.Println("testa",testa) // 此处使用testa将会报错

    // for循环
    // Go 中唯一的循环结构是 for。它可以用来实现各种类型的循环(类似于 C 语言的 for、while、do-while)

    // for initialization; condition; post {
    //  // 循环体
    // }
    // initialization:在循环开始时执行的代码,通常用于初始化循环变量。
    // condition:每次循环前检查的条件,如果为真则执行循环体。
    // post:每次循环结束时执行的代码,通常用于更新循环变量。
    for ia := 0;ia < 10; ia++ {
        fmt.Println("ia:",ia)
    }

    // 模拟while循环
    ib := 0
    for ib < 5 {
        fmt.Println("ib:",ib)
        ib++
    }

    // 无限循环
    // Go 的 for 语句也可以作为无限循环使用,只需要省略条件部分
    // for {
    //  //无限循环
    //  fmt.Println("无限循环")
    // }

    // switch语句
    // 是另一种控制结构,用于在多个条件中选择一个执行。Go 中的 switch 更加简洁和灵活,支持自动“fallthrough”行为,也支持条件判断和多条件匹配。
    // switch value {
    // case condition1:
    //  // 执行代码
    // case condition2:
    //  // 执行代码
    // default:
    //  // 如果没有匹配的条件,执行 default 中的代码
    // }

    day := 4
    switch day {
    case 1:
        fmt.Println("day = 1")
    case 2:
        fmt.Println("day = 2")
    case 3:
        fmt.Println("day = 3")
    default:
        fmt.Println("day != 1 && day !=2 && day != 3")
    }

    // 没有条件的switch语句
    // 如果没有 value,switch 会自动判断每个 case 的条件,直到遇到第一个满足的条件为止。
    switch {
    case day < 10:
        fmt.Println("day is less than 10")
    case day == 10:
        fmt.Println("day is equal to 10")
    default:
        fmt.Println("day is greater than 10")
    }

    // fallthrough 关键字
    // 在 Go 中,switch 不会自动执行下一个 case,除非显式使用 fallthrough 关键字
    x = 1
    switch x {
    case 1:
        fmt.Println("One") // 会输出
        fallthrough
    case 2:
        fmt.Println("Two") // 会输出
        fallthrough
    case 3:
        fmt.Println("Three") // 会输出
    case 4:
        fmt.Println("Four") // 不会输出
    }

    var nx interface{}
    nx = 1
    // switch语句中的类型匹配
    // Go 支持在 switch 中进行类型匹配,可以根据类型来分支
    switch v := nx.(type) { // 类型断言
    case int:
        fmt.Println("nx is of type int:", v)
    case string:
        fmt.Println("nx is of type string:", v)
    default:
        fmt.Println("Unknown type")
    }

    // select 语句
    // select 语句用于处理多个通道的操作,它是 Go 中用于并发编程的一个关键控制结构。select 会等待多个通道中的操作完成,并选择一个进行处理

    // select {
    // case <-chan1:
    //  // chan1 收到数据时执行
    // case <-chan2:
    //  // chan2 收到数据时执行
    // default:
    //  // 如果没有通道准备好,则执行 default
    // }

    // package main

    // import "fmt"

    // func main() {
    //  ch1 := make(chan string)
    //  ch2 := make(chan string)

    //  go func() {
    //      ch1 <- "Hello from ch1"
    //  }()
    //  go func() {
    //      ch2 <- "Hello from ch2"
    //  }()

    //  select {
    //  case msg1 := <-ch1:
    //      fmt.Println(msg1)
    //  case msg2 := <-ch2:
    //      fmt.Println(msg2)
    //  }
    // }

    // select 语句中的default关键字
    // 如果所有的通道都没有准备好,default 分支将会执行

    //总结
    // if 语句:用于条件判断,支持 else 和 else if。
    // for 循环:唯一的循环结构,支持多种形式的循环,包括传统的 for 循环、while 循环和无限循环。
    // switch 语句:用于选择多个条件之一,支持多条件匹配、类型断言和 fallthrough。
    // select 语句:用于并发编程,处理多个通道的读取和写入操作。

}

杨佳乐 发布于  2025-1-10 10:36 

Golang 运算符 Golang

package main

import "fmt"

// 运算符
func main(){

    // 算术运算符 用于进行基本的数学计算

    // +    加法  a + b
    // -    减法  a - b
    // *    乘法  a * b
    // /    除法  a / b
    // %    取余(模运算) a % b

    a := 10
    b := 3

    fmt.Println("a + b",a + b)
    fmt.Println("a - b",a - b)
    fmt.Println("a * b",a * b)
    fmt.Println("a / b",a / b)
    fmt.Println("a `%` b",a % b)

    // 关系运算符 用于比较两个值,并返回一个布尔值(true 或 false)

    // ==   等于  a == b
    // !=   不等于 a != b
    // <    小于  a < b
    // >    大于  a > b
    // <=   小于或等于   a <= b
    // >=   大于或等于   a >= b

    fmt.Println("a == b",a == b) // false
    fmt.Println("a != b",a != b) // true
    fmt.Println("a < b",a < b) // false
    fmt.Println("a > b",a > b) // true
    fmt.Println("a <= b",a <= b) // false
    fmt.Println("a >= b",a >= b) // true

    // 逻辑运算符 用于处理布尔值的逻辑运算

    // &&   逻辑与(AND)    a && b
    // !    逻辑非(NOT)    !a
    // ||   逻辑或        a || b

    tt := true
    tf := false

    fmt.Println("tt && tf",tt && tf) // false
    fmt.Println("tt || tf",tt || tf) // true
    fmt.Println("!tt",!tt) // false 当tt 不是false的时候,返回false , tt = false的时候返回true

    // 赋值运算符 用于将右侧的值赋给左侧的变量
    // 运算符  描述  示例
    // =    赋值  a = b
    // +=   加并赋值    a += b
    // -=   减并赋值    a -= b
    // *=   乘并赋值    a *= b
    // /=   除并赋值    a /= b
    // %=   取余并赋值   a %= b
    // &=   按位与并赋值  a &= b
    // |=   按位或    a |= b
    // ^=   按位异或并赋值 a ^= b
    // <<=  左移并赋值   a <<= b
    // >>=  右移并赋值   a >>= b

    c := 10
    d := 5

    c += d // c = c + d
    fmt.Println("c",c)

    c -= d // c = c - d
    fmt.Println("c",c)

    c *= d // c = c * d
    fmt.Println("c",c)

    c /= d // c = c / d
    fmt.Println("c",c)

    c |= d // c = c | d
    fmt.Println("c",c)

    // 自增自减运算
    c++ // c = c + 1
    c-- // c = c - 1
    fmt.Println("c",c)

    // 位运算符
    // &    按位与(AND)    a & b
    // |    按位或(OR)    a | b
    // ^    按位异或(XOR)   a ^ b
    // &^   清空对应位   a &^ b
    // <<   左移  a << b
    // >>   右移  a >> b

}

杨佳乐 发布于  2025-1-10 09:55 

Golang 数据类型 Golang

package main

import "fmt"

// 数据类型
func main(){

    // 基本数据类型

    // 布尔类型 bool (布尔类型用于表示真值,只有两个取值:true 或 false)
    bool_true := true
    bool_false := false

    fmt.Println(bool_true,bool_false) // true,false

    // 整数类型(int, int8, int16, int32, int64))
    // Go 提供了多种整数类型,int 是根据操作系统架构来选择大小的,一般是 32 位或 64 位。其他整数类型的位数分别是 8 位、16 位、32 位和 64 位。
    // int 和 uint:根据平台选择相关大小(32或64)
    // int8、int16、int32、int64 带符号整数
    // uint8、uint16、uint32、uint64 无符号整数

    // 注:int、uint根据系统自动选择32或者64,如果当前操作系统位32位架构,想使用64位int,可手动指定类型,比如 var int_64 int64 = 1000000
    var int_a int = 10
    var int_b int64 = 10000000000
    var int_c uint = 20

    fmt.Println(int_a,int_b,int_c)

    // 浮点数类型(float32、float64)

    var f1 float32 = 3.14
    var f2 float64 = 3.141592653589793

    fmt.Println(f1,f2)

    // 字符串类型(string)
    // Go 中的字符串是由一系列字节组成的,使用 UTF-8 编码。字符串是不可变的,一旦创建就不能更改。
    // 创建不能更改意思为不能修改字符串的内容,可以重新赋值字符串变量
    var s1 string = "Hello Go"
    // s1[0] = 'H'  // 错误: 不能修改字符串中的字符

    s1 = "Hello" // 合法的

    fmt.Println(s1)

    // 字节类型(byte)
    // byte 是 uint8 的别名,用于表示字节类型,通常用于处理原始数据(如文件、网络协议等)。
    // byte 的范围是 0 - 255
    var b_a byte = 'A'
    var b_b byte = 30
    fmt.Println(b_a,b_b) // b_a 输出 65

    // rune类型
    // rune 是 int32 的别名,表示一个 Unicode 字符。它用于表示单个字符,特别是在处理 Unicode 字符集时。
    // rune 的范围是 -2147483648 - 2147483647
    var r_a rune = 'A'
    fmt.Println(r_a) // 输出65

    // 复合数据类型
    // Go 提供了多种复合数据类型,包括数组、切片、映射、结构体和指针等。

    // 数组(Array)
    // 数组是固定大小的元素序列,元素的类型相同。Go 中的数组长度是数组类型的一部分,因此数组的大小是不可变的。

    // 显式指定大小
    var arr_a [5]int
    arr_a[0] = 100
    arr_a[1] = 200
    // arr_a[5] = 100 // 错误,数组下标从0开始,当前变量最大下标为4,给5设置值则会报错
    fmt.Println(arr_a)

    // 使用字面量初始化
    arr_b := [3]int{100,200,300} //arr_b := [4]int{100,200,300} 也是合法声明,最后一个下标默认则为0
    fmt.Println(arr_b)

    // 自动推导数组大小
    arr_c := [...]int{1,2,3,4,5,6}
    fmt.Println(arr_c)

    // 访问和修改数组元素
    fmt.Println("访问数组:",arr_a[0])
    arr_a[0] = 300 //修改数组

    // 遍历数组
    for arr_a_i := 0;arr_a_i < len(arr_a);arr_a_i++ {
        fmt.Println(arr_a[arr_a_i])
    }

    // 获取数组长度
    fmt.Println("获取数组长度(len()):",len(arr_a))

    // 多维数组
    arr_dw := [2][3]int{
        {1, 2, 3},
        {4, 5, 6},
    }

    fmt.Println(arr_dw[0][1]) // 输出 2

    // 遍历多维数组
    for arr_dw_i := 0;arr_dw_i < len(arr_dw);arr_dw_i++ {
        fmt.Println(arr_dw[arr_dw_i])
        for arr_dw_i_c := 0;arr_dw_i_c < len(arr_dw[arr_dw_i]);arr_dw_i_c++ {
            fmt.Println(arr_dw[arr_dw_i][arr_dw_i_c])
        }
    }

    // 切片(Slice)
    // 切片是动态数组,可以动态增长,长度和容量都可以变化。切片是 Go 中常用的复合数据类型。
    // 它是基于数组的动态视图。
    // 切片在 Go 编程中非常常用,因为它提供了比数组更灵活和高效的操作方式。
    // 与数组不同,切片是引用类型,因此切片的操作会影响原始数据(即底层数组)。

    // 切片没有固定大小,因此它是动态的。切片有三个主要属性:
    // 1.指针 指向底层数组的起始位置
    // 2.长度 切片中元素的个数
    // 3.容量 从切片的起始位置到数组的末尾的元素个数

    // 可以通过字面量、make 函数或从数组中切片来创建切片

    // 通过字面量创建切片
    slice_a := []int{1,2,3,4,5} // 创建一个切片,包含5个元素
    fmt.Println(slice_a)

    // 使用make函数创建切片
    // make函数有三个参数:make([]T, len, cap),其中 len 是切片的长度,cap 是切片的容量。cap 是可选的,如果省略,默认 cap 等于 len。
    slice_b := make([]int,5)// 创建一个长度为5的切片,初始值为0
    fmt.Println(slice_b)

    // 从数组创建切片
    slice_arr := [...]int{1,2,3,4,5}
    slice_c := slice_arr[1:4] // 获取数组 arr 从下标 1 到 3 的切片
    fmt.Println(slice_c) // 输出: [2 3 4]

    // 切片的基本操作

    //访问切片元素 通过索引访问切片元素,和数组的访问方式一样
    slice_new_a := []int{1,2,3,4,5,6}
    fmt.Println(slice_new_a[0],slice_new_a[2])

    //修改切片元素 切片是引用类型,因此修改切片元素会直接影响底层数组
    fmt.Println(slice_arr);
    slice_c[0] = 100
    fmt.Println(slice_arr,slice_c) // 由于slice_c 是从slice_arr 中创建,所以更改slice_c 会同时修改slice_arr中数组内容

    //获取切片的长度和容量
    //使用len()和cap()函数分别获取切片的长度和容量
    slice_d := make([]int,5)
    fmt.Println("长度:",len(slice_d),"容量:",cap(slice_d))

    //切片的扩展和重新切片
    //使用 append 扩展切片
    //append 函数用于向切片末尾添加元素。如果切片的容量足够,它会在原地修改切片;如果容量不够,append 会创建一个新的底层数组。
    slice_d = append(slice_d,4,5,6) //向切片添加元素
    fmt.Println(slice_d)

    //重新切片
    //可以通过切片操作符来从一个切片创建新的切片(即重新切片),这不会复制数据,而是创建一个新的切片指向原数组的一部分
    oldslice := []int{1,2,3,4,5}
    newslice := oldslice[1:3] //// 创建一个新的切片,包含元素 [2,3]
    fmt.Println(oldslice,newslice)

    //切片容量增长规则
    //Go 中的切片会根据需要自动增长。当切片的容量不够时,它会创建一个新的底层数组,通常将容量翻倍。
    slice_nn := []int{1, 2, 3}
    slice_nn = append(slice_nn, 4)  // 新的底层数组会被分配
    fmt.Println(cap(slice_nn))   // 输出大于 3 的值,容量通常是 8(根据实现不同可能会有所不同)

    //数组是值类型 
    //切片是引用类型赋值或传递切片时不会复制底层数据,而是共享同一个底层数组

    // 切片非常适合用作函数参数,可以传递多个值,而无需显式创建数组。例如下面:
    // func sum(nums []int) int {
    //  total := 0
    //  for _, num := range nums {
    //      total += num
    //  }
    //  return total
    // }

    // numbers := []int{1, 2, 3, 4}
    // fmt.Println(sum(numbers))  // 输出: 10

    //由于切片共享底层数组,它们是内存高效的。在大多数情况下,切片的传递是按引用进行的,而不是复制整个数组。这避免了内存的重复分配,提高了性能。

    //切片复制
    //可以使用 copy 函数来复制切片的内容。copy 函数将源切片的数据复制到目标切片,返回复制的元素个数。 
    src := []int{1, 2, 3}
    dst := make([]int, len(src))
    copy(dst, src)  // 将 src 的内容复制到 dst
    fmt.Println(dst) // 输出: [1 2 3]

    // 总结
    // 切片是对数组的动态视图,支持灵活的操作。
    // 切片的长度是动态变化的,而容量在大多数情况下会自动扩展。
    // 切片是引用类型,多个切片可以共享同一个底层数组。
    // 使用 append 可以动态增加切片的元素,而 copy 函数用于复制切片的内容。
    // 切片非常适用于动态数据结构(如队列、栈等)的实现。

    // 映射Map
    // 映射是一种键值对的数据结构。Go 中的 map 是无序的。
    // Map提供了基于键值对的存储方式。它允许根据键快速查找对应的值

    // 定义和初始化
    m_a := map[string]int{
        "a":1,
        "b":2,
        "c":3,
    }
    fmt.Println(m_a)

    // 通过make函数创建map
    m_b := make(map[string]int) //创建一个空的 map,键是 string 类型,值是 int 类型
    m_b["a"] = 1
    m_b["b"] = 2
    m_b["c"] = 3
    fmt.Println(m_b)

    // 访问和操作Map元素
    // 访问元素
    // 可以通过键来访问 map 中的值。如果键存在,则返回对应的值;如果键不存在,则返回值类型的零值。
    fmt.Println(m_b["a"],m_b["ccc"])
    // 判断某个键是否存在 可以通过多重赋值来判断某个键是否存在。如果键存在,第二个值会是 true,否则是 false。
    m_b_a_value, exists := m_b["a"]
    if exists {
        fmt.Println("键 'a' 存在,值为:", m_b_a_value)  // 输出: 键 'a' 存在,值为: 1
    } else {
        fmt.Println("键 'a' 不存在") // exists = false
    }

    //删除元素
    //使用 delete 函数删除 map 中的某个键值对。如果键不存在,delete 不会引发错误。
    delete(m_b,"a")
    delete(m_b,"ccc") // ccc不存在,不会报错
    fmt.Println(m_b)

    // 遍历map 可以使用for 和 range 来遍历map中的键值对
    for m_b_key,m_b_value := range m_b{
        // 由于map是无序的,所以输出顺序不确定
        fmt.Println(m_b_key,m_b_value)
    }

    //Map的零值
    //map 是引用类型,零值是 nil。一个 nil 的 map 不可以用于存储键值对,尝试插入元素会引发运行时错误。你需要使用 make 来初始化 map
    var m_nil map[string]int  // 未初始化,m 是 nil
    fmt.Println(m_nil == nil)  // 输出: true
    // 以下代码会导致运行时错误: panic: assignment to entry in nil map
    // m["a"] = 1

    // 使用make 初始化
    m_nil = make(map[string]int)
    m_nil["a"] = 1 // 可以正常运行
    fmt.Println(m_nil)

    // map的容量和扩展
    // map 的容量会根据元素的数量动态增长。可以使用 len() 函数获取 map 中元素的数量,但不能直接修改 map 的容量。map 的容量增长是自动的,无需手动调整
    fmt.Println(len(m_nil))

    //应用场景
    // 计数 map 是一个很好的工具,常用于统计某些元素的出现次数,例如字符频率统计:
    // s := "hello world"
    // freq := make(map[rune]int)

    // for _, c := range s {
    //  freq[c]++
    // }

    // fmt.Println(freq)
    // // 输出: map[ 1 d:1 e:1 h:1 l:3 o:2 r:1 w:1]

    //去重 利用 map 的键唯一性,可以轻松去重数组或切片中的元素
    // arr := []int{1, 2, 2, 3, 4, 4, 5}
    // unique := make(map[int]bool)

    // for _, num := range arr {
    //  unique[num] = true
    // }

    // for key := range unique {
    //  fmt.Println(key)  // 输出: 1 2 3 4 5
    // }

    // Map注意事项
    // 1.无序性:map 是无序的,遍历 map 时,元素的顺序是不确定的。
    // 2.不能直接比较 map:map 不能直接进行比较操作(例如,map1 == map2)。可以通过遍历 map 来比较它们的内容。
    // 3.并发访问问题:Go 的 map 不是线程安全的。如果多个 goroutine 同时读写同一个 map,需要使用同步机制(如 sync.Mutex 或 sync.RWMutex)来保护 map 的并发访问。
    // // 示例: 使用互斥锁同步访问 map
    // import "sync"

    // var m = make(map[string]int)
    // var mu sync.Mutex

    // func safeWrite(key string, value int) {
    //  mu.Lock()         // 锁住
    //  m[key] = value    // 写入 map
    //  mu.Unlock()       // 解锁
    // }

    // func safeRead(key string) int {
    //  mu.Lock()         // 锁住
    //  value := m[key]   // 读取 map
    //  mu.Unlock()       // 解锁
    //  return value
    // }

    // 性能: map 采用哈希表(hash table)实现,支持常数时间复杂度的查找、插入和删除操作。具体的时间复杂度依赖于哈希函数和负载因子。
    // 内存: map 会根据需要动态扩展其大小,当元素数量达到一定阈值时,Go 会重新调整 map 的底层存储结构,通常通过哈希扩容来提高性能。
    // 总结
    // 1.map 是基于哈希表实现的键值对集合,提供了高效的插入、查找和删除操作。
    // 2.map 的键是唯一的,值可以是任意类型。
    // 3.map 是引用类型,零值为 nil,需要使用 make 或字面量初始化。
    // 4.遍历 map 是无序的,顺序不可预期。
    // 5.在多 goroutine 并发访问 map 时,需要使用锁(如 sync.Mutex)来确保安全。

    // 结构体(Struct)
    // 是一种聚合类型,用于将多个不同类型的数据组合成一个单一的数据结构。结构体通常用来表示一些具有不同属性(字段)的对象或实体。
    // 结构体的字段可以是任意类型(包括基本类型、数组、切片、映射、其他结构体等),它是 Go 中实现面向对象编程的一种方式。

    // 结构体的定义和创建
    // 定义结构体 结构体定义通过 type 关键字完成,使用 struct 关键字来声明结构体的类型。
    type Person struct {
        Name string
        Age int
    }
    // Person是结构体的类型名,结构体包含两个字段,Name字段 string类型,Age字段 int类型

    // 创建结构体实例
    // 通过字面量初始化结构体
    person_1 := Person{
        Name: "张三",
        Age: 30,
    }

    fmt.Println(person_1) // 输出: {张三 30}

    // 使用结构体指针(&)
    //通过 & 可以创建结构体的指针,这样可以避免复制结构体,特别是在结构体很大时,传递指针更加高效。
    person_2 := &Person{
        Name: "李四",
        Age: 30,
    }

    fmt.Println(*person_2)
    // 结构体的指针也可以像结构体实例一样访问字段,Go 会自动解引用。
    fmt.Println(person_2.Name,person_1.Name)

    // 结构体的字段
    // 结构体的字段可以是任何类型,包括基本类型、数组、切片、映射、其他结构体等。字段的名字通常使用首字母大写(如 Name、Age),表示字段是 可导出的,即可以在其他包中访问。
    // 结构体字段的访问,可以通过 (.) 操作符访问结构体字段
    fmt.Println(person_1.Age)

    // 结构体指针
    // 结构体是值类型。如果你直接传递结构体给函数,函数会接收到结构体的副本。为了避免复制结构体,可以传递结构体的指针,这样函数可以修改原始结构体。

    //结构体指针和结构体实例的区别
    //结构体实例:直接存储数据。
    //结构体指针:存储结构体的内存地址,通过指针可以间接访问数据。

    // 结构体方法
    // Go 语言支持通过结构体定义 方法。方法就是与某个类型(如结构体)相关联的函数。

    dog1 := Dog{
        Name:"dog1",
        Age:1,
    }

    dog1.Greet() //值传递,不可修改dog1 数据
    dog1.Hello() //指针传递,可以修改dog1 数据

    fmt.Println(dog1)

    // 匿名字段(嵌套结构体)
    //Go 支持匿名字段,即没有字段名的结构体字段。如果结构体类型是匿名的,那么该类型的字段可以直接通过类型名来访问。匿名字段通常用于嵌套结构体。

    type Address struct{
        Street, City string
    }

    type Nperson struct{
        Name string
        Age int
        Address // 匿名字段
    }

    np1 := Nperson{
        Name:"张三",
        Age:20,
        Address: Address{Street:"sss",City:"cityvalue"},
    }

    fmt.Println(np1,np1.Name,np1.Address.City)

    // 结构体比较
    // Go 中的结构体可以通过 == 和 != 运算符进行比较(前提是结构体的字段类型支持比较)。比较时,两个结构体的所有字段必须相等。

    ad1 := Address{
        Street:"aa",
        City:"bb",
    }
    ad2 := Address{
        Street:"aa",
        City:"bb",
    }
    ad3 := Address{
        Street:"aaa",
        City:"bb",
    }

    fmt.Println(ad1 == ad2) // true
    fmt.Println(ad1 == ad3) // false

    // 结构体与数组/切片的区别
    // 数组:固定大小,存储一组相同类型的数据。
    // 切片:动态大小,支持更多灵活的操作。
    // 结构体:可以包含不同类型的字段,适用于表示具有不同属性的实体。

    // 总结
    // 结构体(struct) 是将不同类型的数据聚合在一起的一种类型,通常用于表示某种具有多个属性的实体。
    // 结构体字段可以是任意类型,可以包含其他结构体、切片、映射等。
    // 可以通过值类型或指针类型的接收者为结构体定义方法,支持面向对象的编程风格。
    // Go 中的结构体非常灵活,适合用来表示对象、数据模型等。

    // 指针 Pointer
    // 指针类型用于存储另一个变量的地址。Go 中的指针不支持指针算术(即不允许直接修改指针值)。
    // *:指针类型声明时使用,表示指针指向某种类型的值。
    // &:取地址操作符,返回变量的内存地址。
    var p *int  // p 是一个指向 int 类型的指针
    x := 20
    x_ad := &x  // x_ad 是 x 的指针,指向 x 的内存地址
    p = &x 
    fmt.Println(x_ad)  // 输出: 指向 x 的内存地址

    //通过 * 操作符访问指针所指向的值(解引用):
    fmt.Println(*p) // 输出20

    // 零值和nil指针
    // Go 中,指针的零值是 nil,表示没有指向任何变量。使用一个未初始化的指针(nil 指针)会导致运行时错误。
    // var p *int  // p 是一个 nil 指针
    // fmt.Println(p)  // 输出: <nil>,指针没有指向任何变量

    // var p *int
    // if p == nil {
    //  fmt.Println("p is nil")  // 输出: p is nil
    // }

    // 总结
    // 指针 是存储内存地址的变量,它允许通过地址访问和修改数据。
    // Go 使用 * 来声明指针类型,使用 & 获取变量的地址。
    // 通过指针可以避免值传递带来的性能开销,特别是在结构体、数组等大型数据结构中。
    // 指针用于修改函数外部的数据,或是返回对数据的引用。
    // Go 指针与 C/C++ 中的指针相似,但它避免了直接内存管理和复杂的指针运算,提供了一定程度的安全性。

    // 接口(Interface)
    // 接口是 Go 实现 多态 的核心机制,允许不同的类型(只要它们实现了接口的所有方法)都可以作为接口类型的值。
    // 接口定义了一组方法签名,但并不提供具体实现。一个类型如果实现了接口中的所有方法,那么它就隐式地实现了这个接口。

    // 声明接口
    type Animal interface {
        Speak() string
    }
    // 在上面的例子中,Animal 是一个接口类型,包含了一个方法 Speak,它返回一个字符串。任何类型只要实现了 Speak 方法,就隐式地实现了 Animal 接口

    // 实现接口
    dogs_1 := Dogs{
        Name:"小狗1",
    }

    fmt.Println(dogs_1.Speak())

    // 空接口 
    // Go 中的 空接口 interface{} 是一个特殊的接口类型,它没有方法签名,意味着所有类型都实现了空接口。空接口可以用来存储任何类型的值,通常用在需要处理不同类型的场景中

    var ki interface {}

    ki = 2
    fmt.Println(ki)

    ki = "hello"

    fmt.Println(ki)

    ki = [...]int{1,2,3}
    fmt.Println(ki)

    ki = "hello123"

    // 接口断言
    // Go 提供了 类型断言 来判断和提取接口类型中实际存储的具体类型。类型断言用于从接口类型提取实际的类型值。
    // 基本语法
    // value, ok := interfaceValue.(ConcreteType)
    // value 是接口值实际存储的数据。 ok 是一个布尔值,表示断言是否成功。

    str, ok := ki.(string) // 判断是否是string类型
    if ok {
        fmt.Println(str)  // 输出: Hello, World!
    } else {
        fmt.Println("ki is not a string")
    }

    // 强制类型断言
    // 如果你确信一个接口值是某个具体类型,可以进行强制类型断言。如果断言失败,Go 会引发运行时错误(panic)。
    // str := ki.(int)  // 运行时错误: panic: interface conversion: interface {} is int, not string

    // 接口类型的类型判断
    //Go 语言也提供了一个 类型开关(type switch) 来根据不同的接口类型执行不同的操作。类型开关是一个基于 switch 语句的扩展,专门用于处理接口类型的多态
    // func PrintType(v interface{}) {
    //  switch v := v.(type) {
    //  case int:
    //      fmt.Println("int:", v)
    //  case string:
    //      fmt.Println("string:", v)
    //  case bool:
    //      fmt.Println("bool:", v)
    //  default:
    //      fmt.Println("unknown type")
    //  }
    // }

    // func main() {
    //  PrintType(42)      // 输出: int: 42
    //  PrintType("hello")  // 输出: string: hello
    //  PrintType(true)     // 输出: bool: true
    // }
    // 在上面的例子中,PrintType 函数根据传入的接口值的实际类型执行不同的操作

    // 接口嵌套
    // 接口可以嵌套其他接口,这允许你构建更加灵活和组合的接口设计。

    // type ReadWriter interface {
    //  Reader
    //  Writer
    // }

    // type Reader interface {
    //  Read(p []byte) (n int, err error)
    // }

    // type Writer interface {
    //  Write(p []byte) (n int, err error)
    // }

    // 接口的实现和组合
    // Go 的接口可以通过组合其他接口来构建更复杂的接口。这种组合接口的设计可以使得代码更加灵活和可复用

    // type Speaker interface {
    //  Speak() string
    // }

    // type Walker interface {
    //  Walk() string
    // }

    // type Person interface {
    //  Speaker
    //  Walker
    // }

    // type Human struct {
    //  Name string
    // }

    // func (h Human) Speak() string {
    //  return "Hello, my name is " + h.Name
    // }

    // func (h Human) Walk() string {
    //  return h.Name + " is walking"
    // }

    // func main() {
    //  // 注:该位置p类型为Person接口类型
    //  var p Person = Human{Name: "Alice"}
    //  fmt.Println(p.Speak())  // 输出: Hello, my name is Alice
    //  fmt.Println(p.Walk())   // 输出: Alice is walking
    // }
    // // 在这个例子中,Person 接口通过组合 Speaker 和 Walker 接口来实现更复杂的行为。

    // 接口和多态
    // 接口的主要用途之一是实现多态。多态允许不同类型的对象以相同的方式进行操作,只要这些对象实现了相同的接口

    // type Speaker interface {
    //  Speak() string
    // }

    // type Dog struct {
    //  Name string
    // }

    // func (d Dog) Speak() string {
    //  return "Woof!"
    // }

    // type Cat struct {
    //  Name string
    // }

    // func (c Cat) Speak() string {
    //  return "Meow!"
    // }

    // func MakeSound(speaker Speaker) {
    //  fmt.Println(speaker.Speak())
    // }

    // func main() {
    //  dog := Dog{Name: "Buddy"}
    //  cat := Cat{Name: "Whiskers"}

    //  MakeSound(dog)  // 输出: Woof!
    //  MakeSound(cat)  // 输出: Meow!
    // }
    // 以上两个不同的结构体实现了相同的方法

    // 总结
    // 接口(Interface) 是 Go 中的核心概念,用于定义类型的行为集合。接口的实现是隐式的,只要类型实现了接口的所有方法,就自动实现该接口。
    // 空接口 interface{} 可以存储任何类型的值,是实现多态的基础。
    // 类型断言 和 类型开关 提供了对接口实际类型的处理方法。
    // 接口可以通过组合多个接口来创建更加复杂的接口。
    // 接口支持多态,允许不同类型通过相同的接口进行操作。

    // 数据类型转换
    // Go 中的类型转换不像其他语言那样隐式发生,必须显式地进行类型转换
    var i float64 = 66.66
    var f int = int(i)
    fmt.Println(f) // 输出: 66

    // 常量(const)
    // 常量是不可改变的值,Go 中常量的类型是由值的上下文决定的,常量可以是数字、字符、字符串或布尔值。
    // Go 会自动推导常量的类型,如果没有显式指定类型,则由常量的值推导出类型
    const Pi = 3.14
    const Hello = "Hello, Go!"
    const IsTrue = true

    // 批量声明常量
    const (
        PA = 1
        PB = 2
        Pc = 3
    )

    // 常量表达式
    // Go 支持在常量声明中使用 常量表达式,这些表达式在编译时就被计算出结果。例如,你可以在常量声明中使用算术运算、位运算等
    const (
        xA = 10
        yA = 20
        sumA = xA + yA  // sumA 计算为 30
    )

    // 常量与枚举
    // Go 语言本身没有 enum(枚举)类型,但可以使用常量组合来实现类似枚举的效果,通常通过 iota 来实现一组有序常量。

    // iota 是 Go 中的一个常量生成器,用于生成一组递增的常量。每次使用 iota 时,它的值会自动递增。

    const (
        Sunday = iota  // Sunday = 0
        Monday         // Monday = 1
        Tuesday        // Tuesday = 2
        Wednesday      // Wednesday = 3
        Thursday       // Thursday = 4
        Friday         // Friday = 5
        Saturday       // Saturday = 6
    )

    // 在上面的例子中,iota 从 0 开始递增,因此每个常量依次取得不同的整数值。如果你想手动调整某个常量的值,也可以在它之前设置一个常量的值
    const (
        _ = iota  // 忽略第一个值
        SundayA = iota  // SundayA = 1
        MondayA         // MondayA = 2
    )
    // 在这里,iota 会从 1 开始递增。

    // 使用 iota 实现位标志
    const (
        Read = 1 << iota  // Read = 1 << 0 = 1
        Write             // Write = 1 << 1 = 2
        Execute           // Execute = 1 << 2 = 4
    )
    // 在这个例子中,iota 被用来生成二进制位标志,表示不同的权限。

    // 也可以这样写
    const (
        Num = iota*2 // 0
        Num1 // 2
        Num2 // 4
        Num3 // 6
        Num4 // 8
     )

    // 常量的用途
    // 表示固定的值:例如数学常量(Pi、e)、常用的系统配置(如最大值、最小值)。
    // 避免魔法数字(Magic Numbers):通过常量代替数字常量,使代码更加易读和易维护。
    // 类型安全:通过常量定义,使代码在编译时能捕捉到潜在的类型错误。
    // 位标志和掩码:利用 iota 和常量,可以生成一组位标志,并用于位操作。

    // 常量的作用域
    // 常量的作用域与其他变量一样,在常量声明时确定。如果常量是全局声明的,它的作用域会是整个包;如果常量在函数内部声明,则它的作用域仅限于函数内部
    // package main

    // import "fmt"

    // const GlobalConst = 100  // 全局常量

    // func main() {
    //  const LocalConst = 200  // 局部常量
    //  fmt.Println(GlobalConst)  // 输出: 100
    //  fmt.Println(LocalConst)   // 输出: 200
    // }

    // 总结
    // const 声明常量,常量在 Go 中是不可修改的,编译时就确定了其值。
    // 常量支持多种基本数据类型,包括整数、浮点数、布尔值和字符串。
    // iota 用于生成有序的常量,可以轻松实现类似枚举的效果。
    // 常量可以用来避免魔法数字,提供类型安全,帮助实现位标志等。
    // 常量的作用域由声明位置决定,可以是全局常量也可以是局部常量。

    // 类型别名与自定义类型

    // Go 支持类型别名和自定义类型。类型别名为已有类型提供新的名称,而自定义类型是基于已有类型定义的新类型。
    // 类型别名
    type MyInt = int
    var mx MyInt = 88888
    fmt.Println(mx) // 输出: 88888

    // 自定义类型
    type Age int
    var myAge Age = 20
    fmt.Println(myAge) // 输出: 20

    // 匿名变量 _
    // 由于在函数内部存在未使用的变量会无法通过编译,但有些变量又确实用不到,这个时候就可以使用匿名变量_,使用_来表示该变量可以忽略,例如
    // a, b, _ := 1, 2, 3

    //各种数据类型的零值
    // 整数类型 0
    // 浮点数类型    0.0
    // 复数类型 0 + 0i
    // 布尔类型 false
    // 字符串类型    "" (空字符串)
    // 指针类型 nil
    // 数组类型 每个元素的零值
    // 切片类型 nil
    // 映射类型 nil
    // 通道类型 nil
    // 接口类型 nil
    // 函数类型 nil
    // 结构体类型    字段的零值

    // 通道Channel
    // Channel 是 Go 中用于 goroutines 之间传递数据的管道。你可以把它想象成两个 goroutine 之间的通信线路。
    // 一个 goroutine 向 channel 发送数据,另一个 goroutine 从 channel 接收数据。

    // Channel 的声明和创建

    // 在 Go 中,channel 通过 make 函数来创建

    // ch1 := make(chan Type)
    // Type 是 channel 中存储的数据类型。
    // make(chan Type) 会创建一个用于传递 Type 类型数据的 channel。

    ch1 := make(chan int) // 创建一个传递int类型的channel

    // 带缓冲区的 Channel

    // Go 的 channel 可以有缓冲区,也就是说,当发送数据时,数据会先存储在缓冲区里,直到接收方接收它们。缓冲区的大小是在创建时指定的:
    ch2 := make(chan int, 3)  // 创建一个缓冲区大小为 3 的 channel

    //如果向一个已满的缓冲区发送数据,发送操作会阻塞,直到有空间可以存放新的数据。如果从一个空的缓冲区接收数据,接收操作也会阻塞,直到有数据可以接收。

    // Channel 的发送和接收

    //发送数据:使用 <- 操作符将数据发送到 channel。
    //接收数据:使用 <- 操作符从 channel 接收数据。

    // ch := make(chan int)  // 创建一个 channel

    // // 发送数据
    // go func() {
    //  ch <- 42  // 将数字 42 发送到 channel
    // }()

    // // 接收数据
    // value := <-ch  // 从 channel 接收数据

    // Channel 的关闭
    //关闭 channel 表示不再向该 channel 发送数据。
    //你可以使用 close 函数来关闭 channel。关闭 channel 后,接收方可以继续接收数据,但不能再向 channel 发送数据。
    // ch := make(chan int)

    // go func() {
    //  for i := 1; i <= 5; i++ {
    //      ch <- i  // 发送数据到 channel
    //  }
    //  close(ch)  // 关闭 channel
    // }()

    // // 接收数据直到 channel 关闭
    // for val := range ch {
    //  fmt.Println(val)  // 输出:1 2 3 4 5
    // }
    // 这里,for val := range ch 会在 channel 关闭时自动结束,它会一直接收数据,直到 channel 被关闭。

    // Channel 的方向性
    // Channel 可以通过声明为不同的方向来限制它的使用方式。

    //双向 channel:既可以发送数据,也可以接收数据。
    var ch3 chan int  // 双向 channel

    //只发送数据的 channel:只能发送数据,不能接收数据。通过 chan<- 来声明:
    // func sendData(ch chan<- int) {
    //  ch <- 42  // 只能发送数据
    // }

    //只接收数据的 channel:只能接收数据,不能发送数据。通过 <-chan 来声明:
    // func receiveData(ch <-chan int) {
    //  val := <-ch  // 只能接收数据
    //  fmt.Println(val)
    // }

    // 方向性声明可以帮助你在函数中限制 channel 的操作,确保数据流动的方向性。

    // Channel 的传递

    // Channel 是 引用类型,因此在函数传递时,实际上是传递了一个指向该 channel 的指针。这意味着你不需要担心内存开销或复制问题。当你把 channel 作为参数传递时,多个 goroutine 都可以共享同一个 channel

}

// 结构体测试值/指针接口方法
type Dog struct {
    Name string
    Age int
}

// 值接收者
func (d Dog) Greet(){
    fmt.Println("值接收者",d.Name)
}

// 指针接收者
func (d *Dog) Hello(){
    d.Age++
}

// 测试实现接口方法
type Dogs struct{
    Name string
}

func (d Dogs) Speak() string {
    return "wangwang"
}

杨佳乐 发布于  2025-1-9 22:26