从零自制docker-0-【前置知识-go语言快速入门】(package main 变量 运算符 分支 循环 函数 数组 slice map 结构 指针 方法 接口 错误处理 并发)

package main

package main 是Go编程语言中的一个关键表达。它出现在每个Go源文件的开头,并定义了Go源代码所属的包(package)。

在Go中,package关键字用于声明一个文件属于哪个包,这对于组织代码和可重用性至关重要。

具体来说,package main 表明该源文件是Go程序的入口点。Go语言的程序执行开始于main包中的main函数,因此,如果你正在编写一个可执行程序,你需要定义一个main包和一个main函数。如果文件中声明了package main,则编译器在编译时会寻找main函数来作为程序的起始执行点。

以下是一个简单的Go程序示例:

package main

import "fmt"

func main() {
    fmt.Println("Hello, World!")
}

在这个示例中,第一行package main表明这是主包。在import语句之后,定义了main函数,当程序被运行时,它会执行并打印出"Hello, World!"。

main包除了标记文件为Go程序的启动入口之外,还意味着编译这个包将生成一个可执行文件,而不是库文件。在Go中,非main包通常用于创建库,可以被其他包导入并使用。

变量

在Go语言中,定义变量需要使用关键字 var,然后是变量的名称和类型。下面是一个示例:

var x int
var y float64
var z string
 
这段代码定义了三个变量:x 是一个整数类型的变量,y 是一个浮点数类型的变量,z 是一个字符串类型的变量。

除了使用关键字 var,Go语言还提供了一种更简洁的方式来定义和初始化变量,称为短变量声明。使用短变量声明,可以省略 var 关键字,并且可以在同一行同时定义和初始化变量。示例如下:

x := 10
y := 3.14
z := "Hello, World!"
 
这里的 := 符号表示进行变量声明和初始化操作。根据右侧的值的类型,Go语言会自动推断出变量的类型。

需要注意的是,一旦变量被声明并赋值,在同一个作用域内不能再次对其进行声明,只能对其进行赋值操作。如果需要重新赋值,可以直接使用变量名进行赋值操作,不需要再次使用 var 关键字。例如:

x = 20
y = 6.28
z = "Hello, Go!"
 
这段代码将新的值赋给了已经存在的变量。

在这里插入图片描述
在这里插入图片描述

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

在这里插入图片描述

在Go语言中,复数类型被定义为 complex64complex128complex64 是由 float32 的实部和虚部组成的复数类型,而 complex128 则是由 float64 组成。

下面是一个示例,展示了如何声明和使用复数类型:

package main

import "fmt"

func main() {
    // 使用内置的 complex 函数来创建复数
    c1 := complex(5, 2)
    c2 := complex(3.2, 4.7)

    // 直接声明复数变量并赋值
    var c3 complex64 = 4.2 + 1.8i
    var c4 complex128 = 9.6 + 5.3i

    // 打印复数变量的实部和虚部
    fmt.Println(real(c1), imag(c1))
    fmt.Println(real(c2), imag(c2))
    fmt.Println(real(c3), imag(c3))
    fmt.Println(real(c4), imag(c4))

    // 进行复数运算
    sum := c1 + c2
    diff := c1 - c2
    prod := c1 * c2
    quot := c1 / c2

    // 打印运算结果
    fmt.Println(sum)
    fmt.Println(diff)
    fmt.Println(prod)
    fmt.Println(quot)
}
 
运行以上代码,你将看到复数的实部和虚部以及复数的运算结果。

需要注意的是,Go语言中的复数类型并没有提供类似获取模(绝对值)和辐角(angle)的内置函数,但你可以使用 math 包中的函数来进行相关的运算。例如,要获取复数的模和辐角,可以使用 math.Abs 和 math.Atan2。

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

运算符

在这里插入图片描述

分支

Go语言的分支结构与C语言有一些不同之处。下面是Go语言分支结构与C语言的一些不同之处:

  1. 缺少括号: 在Go语言中,if语句和switch语句的条件部分不需要使用括号括起来。这是与C语言不同之处,C语言中条件部分需要使用括号括起来。

  2. switch语句的自动 break: 在Go语言中,switch语句中的每个case自动地包括了一个break语句,不需要显式地使用break来阻止执行下一个case。这与C语言中的switch语句不同,C语言中需要使用break关键字来阻止执行下一个case。

  3. 可以使用多个条件的switch语句: 在Go语言中,switch语句可以不带条件,可以在每个case后面使用条件表达式。这使得在一个switch语句中可以使用多个条件进行判断。而在C语言中,switch语句的条件表达式只能是一个整型或字符型的值。

  4. switch语句的默认行为: 在Go语言的switch语句中,没有case匹配时不会自动执行下一个case语句,而是直接执行默认的case语句(如果有)。而在C语言中,如果没有case匹配时,会执行下一个case语句。
    在这里插入图片描述

在Go语言中,if语句的条件部分可以包含一个短变量声明,并且新声明的变量只在if语句的作用域中有效。这意味着可以在if语句的条件部分进行变量赋值。

下面是if语句中进行变量赋值的示例代码:

package main

import "fmt"

func main() {
    if x := 5; x > 3 {
        fmt.Println("x is greater than 3")
    } else {
        fmt.Println("x is less than or equal to 3")
    }
}

在示例代码中,条件部分x := 5表示对变量x进行赋值,并将其作为条件判断的一部分。在if语句的作用域内,x的值为5。

输出结果为:

x is greater than 3

需要注意的是,通过在if语句的条件部分进行变量赋值,可以限制变量的作用域,并且该变量仅在if语句块中有效。这样可以有效地控制变量的可见性和使用范围,并避免变量在其他地方被错误地引用。

在这里插入图片描述
Go语言中的Switch语句与其他语言中的Switch语句有一些特别之处。

  • Case自动终止:在其他语言中,每个Case语句之后都需要显式地使用break语句来终止语句块。而在Go语言中,Case块会自动终止,不需要使用break语句。这样可以避免忘记添加break而导致的错误。

  • 变量作用域:在Go语言中,每个Case语句块都有独立的作用域。这意味着可以在一个Switch语句中定义并使用与其他Case语句块中同名的变量,而不会发生变量重定义的错误。

  • Fallthrough语句:在Go语言中,可以使用fallthrough关键字来实现Case语句的“穿透”,即使一个Case语句执行完毕后,仍然继续执行下一个Case语句。这在某些特定的情况下非常有用,但需要谨慎使用,以免引起逻辑错误。
    在这里插入图片描述

循环

Go语言中的循环有三种形式: for循环、range循环和无限循环。

for循环: for循环是Go语言中最常用的循环形式,它具有初始化语句、条件判断和后续语句。语法如下:

for 初始化语句; 条件判断; 后续语句 {
    // 循环体
}
 
示例:
for i := 0; i < 5; i++ {
    fmt.Println(i)
}
 
range循环: range循环用于遍历数组、切片、映射、字符串等数据结构。它会返回索引和对应的值。语法如下:

for 索引,:= range 遍历对象 {
    // 循环体
}
 
示例:
arr := []int{1, 2, 3, 4, 5}
for index, value := range arr {
    fmt.Println(index, value)
}
 
无限循环: Go语言中的无限循环可以通过for循环结构模拟,也可以使用for{}方式。示例:

// 使用for循环模拟无限循环
for {
    // 无限循环体
}


在这里插入图片描述
在这里插入图片描述

函数

在这里插入图片描述

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

在 Go 语言中,可以使用匿名函数来创建没有函数名的函数。这是通过使用 func 关键字以及不带函数名的函数字面量来实现的。匿名函数可以直接赋值给变量,或者作为参数传递给其他函数。以下是匿名函数的基本定义形式:

func(parameters) returnType {
    // 函数体
}
 
匿名函数可以直接在声明后调用,也可以赋值给变量后再进行调用。例如:

package main

import "fmt"

func main() {
    // 直接调用匿名函数
    func(s string) {
        fmt.Println(s)
    }("Hello, anonymous function!")

    // 将匿名函数赋值给变量后调用
    myFunc := func(x, y int) int {
        return x + y
    }
    result := myFunc(3, 4)
    fmt.Println(result)
}
 

在这里插入图片描述

数组

在Go语言中,数组的定义方式如下:

var arrayName [size]dataType
 
其中: - arrayName 表示数组的变量名 - size 表示数组的大小 - dataType 表示数组中元素的数据类型

例如,定义一个包含5个整数的数组:

var numbers [5]int
 
另外,还可以使用数组字面量的方式进行数组定义和初始化:

var numbers = [5]int{1, 2, 3, 4, 5}
 
还可以使用:=操作符进行数组的定义和初始化:

numbers := [5]int{1, 2, 3, 4, 5}
 
需要注意的是,Go语言中数组的大小是数组类型的一部分,因此不同大小的数组被视为不同的类型。

在这里插入图片描述

slice

在Go语言中,Slice是一种动态数组的抽象,可以按需自动增长或缩小。Slice的定义方式如下:

var sliceName []dataType
 
其中: - sliceName 表示Slice的变量名 - dataType 表示Slice中元素的数据类型

另外,可以使用make函数创建一个指定长度和容量的Slice:

sliceName := make([]dataType, length, capacity)
 
其中: - length 表示Slice的初始长度 - capacity 表示Slice的初始容量

例如,创建一个初始长度和容量为5的整数Slice:

sliceName := make([]int, 5, 5)
 
在实际使用中,一般使用Slice的字面量方式进行定义和初始化,例如:

sliceName := []dataType{value1, value2, value3}
 
例如,创建一个包含整数的Slice并初始化:

numbers := []int{1, 2, 3, 4, 5}
 
需要注意的是,Slice是引用类型,它指向底层数组。因此,在函数传递Slice时,实际上传递的是Slice的引用,对Slice的操作会影响到底层数组。
在Go语言中,Slice的初始长度(length)表示Slice中初始包含元素的个数,而容量(capacity)表示Slice可以容纳的元素个数的上限。在使用make函数创建Slice时,可以指定Slice的初始长度和容量。

初始化Slice时,如果未指定容量,则Slice的容量会和长度相同。当Slice中的元素个数超过了初始容量时,Slice会自动扩容,新的容量一般是原容量的两倍。这种自动扩容的特性使得Slice成为了一个非常灵活的数据结构。

举个例子,创建一个长度和容量为5的整数Slice:

sliceName := make([]int, 5, 5)
 
这里的5表示Slice的初始长度,即Slice中包含5个元素;而第二个5表示Slice的初始容量,即Slice可以容纳的元素个数的上限为5。

需要注意的是,当Slice的长度超过了初始容量时,会触发自动扩容,这可能会导致底层数组的重新分配和拷贝,因此如果预先知道Slice的最大容量,最好在初始化的时候就指定好容量,以避免频繁的扩容操作。

在Go语言中,append是一个内置的函数,用于向切片追加元素。它的定义如下:

func append(slice []Type, elems ...Type) []Type
 
其中,slice是切片类型的参数,elems是要追加到切片的元素。append函数会返回一个新的切片,其中包含原始切片的元素以及追加的新元素。
例如:

slice := []int{1, 2, 3}
slice = append(slice, 4, 5)
// 现在slice中包含了1, 2, 3, 4, 5
 
需要注意的是,如果追加的元素个数超过了原始切片的容量,append函数会自动分配一个新的底层数组来存储更多的元素,并返回新的切片。

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

map

map[类型]类型=一个类型

在Go语言中,map是一种集合类型,用于存储键-值对。每个键在map中必须是唯一的,并且与每个键关联的值可以是任何类型。map是一种引用类型,因此在函数参数中传递map时,不需要显式使用指针。以下是一些map的基本特性:

声明和初始化:map可以使用make函数来初始化,也可以使用map字面值进行初始化。下面是几种示例:

var m map[string]int  // 声明一个map
m = make(map[string]int)  // 使用make函数初始化
m := map[string]int{"a": 1, "b": 2}  // 使用map字面值初始化
 
添加和获取键-值对:可以使用下标操作符[]来添加、修改和获取map中的键-值对。例如:

m["c"] = 3  // 添加键-值对
value := m["a"]  // 获取键为"a"的值
 
删除键-值对:使用delete函数可以删除map中的键-值对。例如:

delete(m, "b")  // 删除键为"b"的键-值对
 
遍历:可以使用range关键字来遍历map中的所有键-值对。每次迭代,range返回键和对应的值。例如:

for key, value := range m {
    fmt.Println(key, value)
}

在这里插入图片描述
在这里插入图片描述

结构

当你在Go语言中定义结构体时,你可以使用关键字 typestruct。语法如下:

type MyStruct struct {
    Field1 type1
    Field2 type2
    // ...
}
 
type 关键字用于声明新的类型。
MyStruct 是你创建的结构体的名称。
struct 关键字用于定义结构体的开始。
Field1 和 Field2 是结构体中的字段(也称为成员或属性)。
type1 和 type2 是字段的类型。
例如,这是一个表示用户的结构体示例:

type User struct {
    ID       int
    Name     string
    Email    string
    Age      int
    IsActive bool
}
 
在这个示例中,User 是结构体的名称,而 ID、Name、Email、Age 和 IsActive 是结构体的字段,它们分别具有不同的类型。

在这里插入图片描述

使用键值对初始化 可以通过指定结构体字段名和对应的值来初始化结构体实例。例如:
type Person struct {
    Name string
    Age  int
}

func main() {
    p := Person{Name: "Alice", Age: 25}
    fmt.Println(p) // 输出:{Alice 25}
}
 
使用零值初始化 可以直接初始化为结构体类型的零值(即各字段被初始化为其类型的零值)。例如:
type Point struct {
    X int
    Y int
}

func main() {
    p := Point{} // 结构体Point的字段X和Y被初始化为0
    fmt.Println(p) // 输出:{0 0}
}
 
使用new函数创建指针 可以使用内置的new函数创建结构体实例的指针。例如:
type Circle struct {
    Radius float64
}

func main() {
    c := new(Circle) // c是指向Circle类型的指针
    c.Radius = 5.0
    fmt.Println(*c) // 输出:{5}
}

在这里插入图片描述

指针

不是结构指针传递那么只是拷贝,不会影响原来的结构实例的数据
在这里插入图片描述
在这里插入图片描述
q为结构指针时,其引用内部变量可以省略掉*
在这里插入图片描述

方法

Go语言中的方法是在特定类型上定义的函数。方法定义的一般形式如下:

func (receiver Type) methodName(parameters) returnType {
    // 方法实现
}
 
其中,receiver是方法的接收者,Type是接收者的类型,methodName是方法的名称,parameters是方法的参数列表,returnType是方法的返回类型。

下面是一个简单的方法示例:

package main

import "fmt"

type Circle struct {
    radius float64
}

func (c Circle) area() float64 {
    return 3.14 * c.radius * c.radius
}

func main() {
    c := Circle{radius: 5}
    fmt.Println("Circle Area:", c.area())
}
 
在上面的示例中,Circle类型定义了一个area方法,它接收一个Circle类型的接收者,并返回一个float64类型的值。在main函数中,我们创建了一个Circle实例并调用了area方法来计算其面积。

在这里插入图片描述
同样方法中如果是指针传参才会修改原来的元素
在这里插入图片描述
在这里插入图片描述

接口

在Go语言中,接口是一种抽象类型,它定义了对象的行为规范,但不包含具体的实现。接口由一组方法签名定义,并且任何实现了这些方法的类型都被称为实现了该接口。

接口的定义形式如下:

type InterfaceName interface {
    Method1(parameters) returnType
    Method2(parameters) returnType
    // ...
}

其中,InterfaceName是接口名称,Method1等是接口中定义的方法。

下面是一个简单的接口实例:

package main

import "fmt"

// 定义一个接口
type Shape interface {
    area() float64
}

// 定义一个矩形结构体
type Rectangle struct {
    width  float64
    height float64
}

// 矩形结构体实现了Shape接口
func (r Rectangle) area() float64 {
    return r.width * r.height
}

// 定义一个圆形结构体
type Circle struct {
    radius float64
}

// 圆形结构体实现了Shape接口
func (c Circle) area() float64 {
    return 3.14 * c.radius * c.radius
}

func getArea(shape Shape) {
    fmt.Println("Area:", shape.area())
}

func main() {
    rect := Rectangle{width: 10, height: 5}
    circle := Circle{radius: 5}

    getArea(rect)
    getArea(circle)
}

在上面的示例中,我们定义了一个Shape接口,该接口包含了一个名为area的方法。然后我们定义了Rectangle和Circle两个结构体,并分别在它们上面实现了area方法。最后,在getArea函数中,我们接收了一个Shape类型的参数,并调用了area方法来计算各种形状的面积。

这个示例演示了接口的概念,通过接口,我们可以对不同类型的对象进行通用的操作。
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

错误处理

在Go语言中,错误处理是通过返回错误对象来实现的。标准库中的error接口定义如下:

type error interface {
    Error() string
}
 

这意味着任何实现了Error方法并且返回一个字符串的类型都可以被当作错误处理。在函数或方法中,通常会返回一个错误对象作为额外的返回值,以便调用者能够判断函数是否执行成功,并且得到失败的具体原因。

下面是一个简单的错误处理示例:

package main

import (
    "errors"
    "fmt"
    "math"
)

func main() {
    result, err := squareRoot(9)
    if err != nil {
        fmt.Println("Error:", err)
    } else {
        fmt.Println("Square root:", result)
    }

    result, err = squareRoot(-9)
    if err != nil {
        fmt.Println("Error:", err)
    } else {
        fmt.Println("Square root:", result)
    }
}

func squareRoot(x float64) (float64, error) {
    if x < 0 {
        return 0, errors.New("Cannot take square root of a negative number")
    }
    return math.Sqrt(x), nil
}

在上面的示例中,我们定义了一个squareRoot函数,它接收一个float64类型的参数并返回一个float64类型的平方根值,以及一个error类型的错误对象。这样,当调用squareRoot函数时,我们可以检查返回的错误对象是否为nil,如果不为nil,则意味着发生了错误,并且可以通过错误对象获取到具体的错误信息
在这里插入图片描述

并发

Go 语言的并发是指能够同时执行多个独立的任务,可以采用 goroutine 来实现并发。下面是一个简单的例子,演示了如何使用 goroutine 实现并发执行:

package main

import (
    "fmt"
    "time"
)

func main() {
    // 同时启动两个 goroutine 来执行不同的任务
    go printNumbers("goroutine 1", 5)
    go printNumbers("goroutine 2", 5)

    // 等待一段时间以确保 goroutine 有足够的时间执行
    time.Sleep(2 * time.Second)
}

func printNumbers(name string, count int) {
    for i := 1; i <= count; i++ {
        fmt.Printf("%s: %d\n", name, i)
        time.Sleep(500 * time.Millisecond) // 模拟一些耗时的工作
    }
}
 

在这个例子中,main 函数同时启动了两个 goroutine,它们分别执行 printNumbers 函数,并在一定时间后结束。在 printNumbers 函数中,每个 goroutine 打印一系列数字,并模拟一些耗时的工作。由于这两个 goroutine 是并发执行的,因此它们的输出会交错出现

package main

import (
    "fmt"
    "time"
)

func hello() {
    for i := 0; i < 3; i++ {
        fmt.Println("Hello Goroutine")
        time.Sleep(1 * time.Second)
    }
}

func main() {
    go hello() // 启动一个goroutine
    time.Sleep(2 * time.Second)
    fmt.Println("Main function")
}

在这个示例中,hello()函数被启动为一个goroutine,同时主函数(main)也在执行。由于goroutine和主函数是并发执行的,因此可以看到它们交替输出信息。
在这里插入图片描述
在 Go 语言中,chan 是用于在 Goroutine 之间进行通信的关键字,用于创建通道(channel),通过通道可以实现 Goroutine 之间的数据传递和协调。以下是关于 chan 的用法:

创建通道(channel):

// 创建一个通道,用于传递整数类型数据
ch := make(chan int)
// 创建一个缓冲通道,缓冲大小为10
ch := make(chan int, 10)
 

发送数据到通道:

// 向通道发送数据
ch <- 42

从通道接收数据:

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

关闭通道:

// 关闭通道
close(ch)

使用通道进行同步与通信:

package main

import "fmt"

func worker(ch chan string) {
    fmt.Println("Worker: Working...")
    ch <- "Work is done!"
}

func main() {
    ch := make(chan string)

    go worker(ch)//先建立一个线程来运行该函数一段时间后再来运行主函数线程一段时间

    // 从通道接收数据
    msg := <-ch
    fmt.Println(msg)
}

在上述示例中,我们创建了一个通道 ch 用于在 worker 函数和 main 函数之间通信。worker 函数向通道发送了消息 “Work is done!”,而 main 函数从通道接收消息并输出。通过通道,可以实现 Goroutine 之间的数据交换和同步操作。
在这里插入图片描述

  • 14
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

看星猩的柴狗

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值