本文介绍: 鉴于全网Go语言知识点总结分散难懂、良莠不齐,为了避免初学者少走弯路,更好更快地掌握Go知识博主特地将自己所学的笔记分享出来。本文内容均为重点知识点,是学习Go的不二选择。Go语言(又称为Golang)是一种开源编程语言,由Google于2007年启动并于2009年首次公开发布。Go语言是一门静态类型编译型的语言,旨在提供一种简单、高效、可靠编程方式。现在越来越多的人开始使用Go语言进行开发,其原因有以下几点。Go 语言被设计成一门应用于学习不是一蹴而就的过程,切勿囫囵吞枣。我是秋说,我们下次见。

写在前面

鉴于全网Go语言知识点的总结分散难懂、良莠不齐,为了避免初学者少走弯路,更好更快地掌握Go知识博主特地将自己所学的笔记分享出来。

在这里插入图片描述


Go语言概念

Go语言(又称为Golang)是一种开源编程语言,由Google于2007年启动并于2009年首次公开发布。Go语言是一门静态类型编译型的语言,旨在提供一种简单、高效、可靠编程方式


语言特色

现在越来越多的人开始使用Go语言进行开发,其原因有以下几点:

  1. Go语言设计简洁语法清晰明了,容易上手和理解。它避免了冗余的语法复杂的概念,使得编写和维护代码更加高效。
  2. Go语言天生支持并发编程,提供了轻量级的Goroutine通道channel)机制,使并发编程变得更加简单安全
  3. 高性能:Go语言在运行时表现出色,具有延迟和高吞吐量。它采用了垃圾回收机制,使内存管理变得自动化且高效,同时还提供了一些优化策略,如原生协程调度器和快速编译等。
  4. 内建工具:Go语言提供了丰富的标准库,覆盖网络编程、文件处理文本处理加密解密各个领域。它还有强大的构建工具可以方便地进行代码构建测试分发

Go 语言用途

Go 语言被设计成一门应用于搭载 Web 服务器存储集群或类似用途的巨型中央服务器系统编程语言

对于高性能分布式系统领域而言,Go 语言比大多数语言有着更高的开发效率。它提供了海量并行的支持,十分适用于游戏服务端的开发。


Go语言环境安装

安装包下载地址https://go.dev/dl/

根据操作系统选择安装包

在这里插入图片描述

运行msi文件

在这里插入图片描述

一路next,并且选择安装路径

在这里插入图片描述

等待安装

在这里插入图片描述

安装完成后将bin目录添加path环境变量中:

在这里插入图片描述

创建一个practice目录来测试装配是否成功:

在这里插入图片描述

practice目录中新建test.go文件

package main

import "fmt"

func main() {
   fmt.Println("Hello, World!")
}//Println不能写作println

命令行输出

在这里插入图片描述

页面回显Hello,World! 则说明Go环境安装成功。


Go语言基础组成

我们以下面的代码为例:

package main

import "fmt"

func main() {
   /* 输出 */
   fmt.Println("Hello, World!")
}

Go语言中的每个文件属于一个包package)。包声明用于定义当前文件所属的包名,不同的包之间可以通过包名进行引用调用

包的声明必须是在源文件中非注释第一行,且每一个Go程序包含一个名为main的包。

通过 import 关键字引入其他包,以便当前文件中使用其他包提供的功能类型引入包后,就可以使用其提供的函数变量结构体等。本题中,fmt实现格式化 IO(输入/输出)的函数

函数是实现特定功能的代码块。在Go语言中,函数由 func 关键字定义,并可以带有参数返回值通过定义函数,可以将代码模块化重复使用。

在Go语言中,变量用于存储数据。使用关键字 var声明变量,同时指定变量名称类型变量可以存储数值字符串、布尔值等不同类型数据

语句是Go程序执行单位,由一个多个表达式组成。表达式用于计算值或执行特定操作。常见的语句包括赋值语句、条件语句(如 if 语句)、循环语句(如 for 语句)等。

注释用于向代码中添加注解和说明信息,对于其他人阅读代码时起到解释作用。在Go语言中,注释可以使用 // 开始的单行注释,或者使用 /* */ 包围的多行注释

注意事项

  1. { 不能单独放在一行,如
func main()  
{  // 错误,{ 不能在单独的行上
    fmt.Println("Hello, World!")
}

在 Go 语言中,大括号通常应该相关的语句在同一行,并且需要一个空格将大括号与前面的语句分隔开。

同样,函数的左括号 {应该与函数签名在同一行,并且右括号 } 应该独占一行

在这里插入图片描述

  1. Go 语言在大多数情况下不需要显式的分号来结束语句。编译器会根据规则自动插入分号。但是,如果一行上有多个语句,则需要使用分号将它们分隔开。

  2. Go 语言采用驼峰命名法。变量和函数应该使用有意义且描述性的名称公共public)的标识应该大写字母开头,非公共(private)的标识应该以小写字母开头。

也就是说,当标识符(包括常量变量类型、函数名、结构字段等等)以一个大写字母开头,如:Qiu,那么使用这种形式的标识符的对象可以被外部包的代码所使用(类似于面向对象语言中的 public);标识符如果以小写字母开头,则对包外是不可见的,但是他们在整个包的内部是可见并且可用的(类似于面向对象语言中的 protected )


GO语言基础语法

Go标记

在Go语言中,标记(Tokens)是源代码最小语法单位编译器源代码分解为一系列标记进行解析处理。以下是Go语言中的一些常见标记类型

  1. 标识符(Identifiers):标识符用于表示变量、函数、类型等的名称标识符由大小写字母数字下划线组成,并且不能以数字开头。

    以下是无效的标识符:
    
    1aht(以数字开头)
    case(Go 语言的关键字a+3运算符是不允许的)
    
  2. 关键字(Keywords):Go语言预先定义了一些关键字,它们具有特殊的含义和用途,例如ifforfunc等。关键字不能作为标识符使用。

  3. 运算符(Operators):运算符用于执行各种算术、逻辑比较操作,例如+-*/等。

  4. 分隔符(Delimiters):分隔符用于将程序的不同部分分隔开来,例如括号( )、花括号{ }、方括号[ ]、逗号,、分号;等。

  5. 字面量(Literals):字面量表示直接使用的常量值,例如整型字面123、浮点型字面3.14字符串字面"QiuShuo"、布尔字面truefalse等。

  6. 注释(Comments):注释用于向代码中添加注解和说明,不会被编译器解析单行注释以//开头,多行注释以/*开始,以*/结束

举个例子

fmt.Println("Hello, World!")

以上代码含有6个标记:

1. fmt
2. .
3. Println
4. (
5. "Hello, World!"
6. )

这些标记构成了Go语言源代码基本元素,它们按照一定的规则组合在一起形成具有意义的句子和表达式。编译器通过解析这些标记来理解和执行代码逻辑

行分隔符

在 Go 程序中,一行代表一个语句结束每个语句不需要以分号结尾。

如果将多个语句写在同一行,它们必须使用 ; 进行区分,使编译理解代码逻辑

例如:

package main

import "fmt"

func main() {
    var a = 10; var b = 20; fmt.Println(a + b)
}

但我们并不推荐这种做法,因为它会降低代码的可读性。

字符串连接

Go 语言的字符串连接可以通过 + 实现

package main
import "fmt"
func main() {
    fmt.Println("Qiu" + "Shuo")
}

在这里插入图片描述

空格

Go 语言中变量的声明必须使用空格隔开,例如:

var a float
const Pi float64 = 3.14159265358979323846

在关键字和表达式之间要使用空格,例如:

if x<20 {
    // do something
}

在这里插入图片描述

在函数调用时,函数名和左边等号之间要使用空格,参数之间也要使用空格。

例如:

result = add(2, 3)

格式化字符串

Go 语言中使用 fmt.Sprintf 或 fmt.Printf 格式化字符串赋值给新串:

它的语法如下

func Sprintf(format string, a ...interface{}) string

其中,format 是一个表示格式字符串,a ...interface{} 是一个可变参数,用于替换格式字符串中的占位符。

举个例子

package main

import "fmt"

func main() {
    var code = 1
    var date = "2020"
    var url = "Code=%d&amp;date=%s"

    var a = fmt.Sprintf(url, code, date)
    fmt.Println(a)
}

fmt.Sprintf() 函数将 codedate 的值替换url 字符串中,并将结果存储在变量 a 中。然后使用 fmt.Println() 函数打印结果

在这里插入图片描述

举个例子

package main

import "fmt"

func main() {
    var code = 2
    var date = "1990"
    var url = "code=%d&amp;date=%s"
    fmt.Printf(url,code,date)
}

fmt.Printf() 函数会将 codedate 的值替换url 字符串中,并将结果打印出来:

在这里插入图片描述


Go语言数据类型

在 Go 语言中,数据类型用于定义数据的存储和操作方式,关于 Go 语言中基本数据类型的详细说明如下

1. 整数类型:
   - int:根据程序运行平台,可以是 32 位或 64整数类型。
   - int8、int16、int32、int64:固定长度的有符号整数类型。
   - uint、uint8、uint16、uint32、uint64:固定长度的无符号整数类型。

2. 浮点数类型:
   - float32:单精度浮点数类型,占用 32 位。
   - float64:双精度浮点数类型,占用 64 位。

3. 复数类型:
   - complex64:包含实部和虚部为 float32 类型的复数。
   - complex128:包含实部和虚部为 float64 类型的复数。

4. 布尔类型:
   - bool:表示逻辑值,只能取 truefalse5. 字符串类型:
   - string:表示文本数据,由一系列 Unicode 字符组成。

6. 字符类型:
   - rune:表示单个 Unicode 字符,类型别名为 int32,常用于处理 Unicode 字符串。

7. 字节类型:
   - byte:表示单个字节数据,类型别名uint8,常用于处理二进制数据8. 指针类型:
   - *T:表示指向类型 T 的指针,用于间接引用变量。

9. 数组类型:
   - [n]T:表示具有固定长度 n 的同类型元素数组10. 切片类型:
    - []T:表示可变长度的同类型元素序列。
    - 切片可以动态增长和缩减,通常比数组更灵活和方便。

11. 映射类型:
    - map[K]V:表示键值对的无序集合。
    - K 表示键的类型,V 表示值的类型。
    - 常用于实现字典关联数组数据结构12. 结构体类型:
    - struct:表示用户自定义复合数据类型。
    - 可以包含不同类型的字段来组成一个结构13. 函数类型:
    - func:表示函数类型。
    - 在 Go 语言中,函数是一等公民,可以作为参数返回值等。

14. 接口类型:
    - interface:表示一组方法抽象集合。
    - 可以通过实现接口来达到多态效果

Go语言变量

Go 语言变量名字母数字下划线组成,其中首个字符不能为数字

我们使用var关键字来声明变量,如:

var a string

也可以一次声明多个变量:

var b, c int

我们也可用 := 来声明变量:

a := 1
a, b, c := 5, 7, "abc"

例如:

var s string = "qiushuo"
等同于
s:="qiushuo"

注意:在声明变量,就不能再对该变量进行声明。

所以在声明变量之后,不能再使用:=对其赋值,而是要使用=进行赋值。

例如:
a := 20
a = 24

在这里插入图片描述

当变量没有初始化时,变量为系统默认设置的值。

举个例子

package main
import "fmt"
func main() {

    // 声明一个变量并初始化
    var a = "Qiushuo" 
    //变量 a 的类型被推断为字符串类型,所以不需要显式地指定它为 string 类型。
    //编译器会根据赋值的值来确定变量的类型。
    fmt.Println(a)

    // 没有初始化就为零值
    var b int
    fmt.Println(b)

    // bool 零值为 false
    var c bool
    fmt.Println(c)
    
    var d string
    fmt.Println(d)
}

输出:

Qiushuo
0
false
""

局部变量全局变量

在编程中,变量可以分为全局变量局部变量,它们的作用域生命周期有所不同:

  1. 全局变量(Global Variables):在函数外部定义的变量称为全局变量全局变量具有全局范围,可以在整个程序、任何函数中被访问和使用。全局变量在程序启动创建,在程序结束时销毁。

示例代码:

package main

import "fmt"

var globalVariable int // 定义一个全局变量globalVariable

func main() {
    globalVariable = 10 // 在主函数中访问和修改全局变量
    fmt.Println(globalVariable)
    someFunction()
}

func someFunction() {
    fmt.Println(globalVariable) // 在其他函数中访问全局变量
}

运行结果

在这里插入图片描述

  1. 局部变量(Local Variables):在函数内部定义的变量称为局部变量局部变量只能在声明它们的函数内部被访问和使用,并且它们的作用域限定在这个函数内部局部变量在函数每次被调用创建,在函数结束时销毁。每个函数调用都会有自己独立局部变量实例

示例代码:

package main

import "fmt"

func main() {
    someFunction()
}

func someFunction() {
    localVariable := 20 // 局部变量
    fmt.Println(localVariable)
}

在这个示例中,localVariable 是一个局部变量,只能在 someFunction() 函数内部访问。它在函数每次被调用时创建,并且每次调用都会有自己独立localVariable 实例

在这里插入图片描述

反例

package main

import "fmt"

func main() {
    someFunction()
    fmt.Println(localVariable) // 在函数外部尝试访问局部变量,会导致编译错误
}

func someFunction() {
    localVariable := 10 // 声明并初始化局部变量
    fmt.Println(localVariable) // 在函数内部可以访问局部变量
}

在这里插入图片描述

要注意的是:

声明局部变量/声明局部变量并对其赋值 后却没有使用它会发生报错

在这里插入图片描述

全局变量是允许声明并不被使用的

Go 语言程序中全局变量与局部变量名称可以相同,但是函数内的局部变量会被优先考虑
实例如下

package main

import "fmt"

/* 声明全局变量 */
var a int = 20

func main() {
   /* 声明局部变量 */
   var a int = 10

   fmt.Printf ("a=%d",a)
}

输出如下

a=10

Go语言常量

在Go语言中,常量(Constants)是指在程序编译时就确定并且不可更改的值,也就是说,常量不能被重新赋值或取地址,并且不能在运行修改

以下是定义常量的语法

const identifier [type] = value

多个相同类型的声明可以简写为:

const Name1, Name2 = value1, value2

其中:

显式类型定义: const b string = "abc"

隐式类型定义: const b = "abc"

注意:常量的命名应该遵循Go语言的命名规范,通常使用驼峰命名法(camelCase),并且常量一般使用大写字母命名以表示其为不可变的值。

以下是一些常见的常量示例:

  1. 基本类型常量:
const Pi = 3.14 // 定义一个浮点数常量
const MaxSize int = 100 // 定义一个整数常量
const truth = true // 定义一个布尔常量
  1. 枚举类型常量:
const (
    Monday    = 0
    Tuesday   = 1
    Wednesday = 2
    Thursday  = 3
    Friday    = 4
    Saturday  = 5
    Sunday    = 6
)
  1. 字符串常量:
const Greeting = "Hello, world!" // 定义一个字符串常量
  1. 常量可以用len(), cap(), unsafe.Sizeof()函数计算表达式的值。常量表达式中,函数必须是内置函数。

举个例子

package main

import "unsafe"
import "fmt"

func main() {
    const (
        strLen   = len("hello")                 // 使用 len() 函数计算字符串长度
        arrayLen = len([3]int{1, 2, 3})         // 使用 len() 函数计算数组长度
        size     = unsafe.Sizeof(int(0))        // 使用 unsafe.Sizeof() 函数计算整数类型的大小
    )

    sliceLen := len([]int{1, 2, 3})            // 使用 len() 函数计算切片长度
    capacity := cap(make(chan int, 10))        // 使用 cap() 函数计算通道容量

    fmt.Println(strLen, sliceLen, arrayLen, capacity, size)
}

在这里插入图片描述

  1. 预定义标识符iota

iota 用于常量的自增计数

在常量声明中,iota初始值为 0,然后每次在下一个常量声明中使用时都会自动自增。它通常与常量表达式一起使用,在每个常量声明中按顺序递增。

以下是一个示例代码,演示了 iota 的使用:

package main

import "fmt"

const (
	Red    = iota // 0
	Green         // 1
	Blue          // 2
)

func main() {
	fmt.Println(Red, Green, Blue)
}

输出结果为:

0 1 2

复杂一点:

package main

import "fmt"

func main() {
    const (
            a = iota   //0
            b          //1
            c          //2
            d = "qiu"   //独立值,iota += 1, iota变为3
            e          
//e是显式赋值的常量声明,它们不会影响 iota 的自增。因此 e 的值仍然是 "qiu",同时iota += 1, iota变为4 
            f = 100    //iota +=1,iota变为5
            g          //100  iota +=1,iota变为6
            h = iota   //7,恢复计数
            i          //8
    )
    fmt.Println(a,b,c,d,e,f,g,h,i)
}

输出:

0 1 2 qiu qiu 100 100 7 8

Go语言运算符

Go语言中常用的运算符包括:

  1. 算术运算符:用于执行基本的算术操作,如加法 +,减法 -,乘法 *,除法 /取余 %,以及自增 ++ 和自减 --
运算符 描述 示例
+ 相加 a + b
相减 a – b
* 相乘 a * b
/ 相除 a / b
% 取余 a % b
++ 自增 a++ 或 ++a
自减 a– 或 –a
  1. 关系运算符:用于比较两个值之间的关系,如相等 ==,不等 !=,大于 >,小于 <,大于等于 >=,小于等于 <=

  2. 逻辑运算符:用于进行逻辑判断,包括逻辑与 &amp;&amp;,逻辑或 ||,逻辑非 !

运算 描述 示例
&amp;&amp; 逻辑与 a &amp;&amp; b
|| 逻辑或 a || b
! 逻辑非 !a
  1. 运算符:用于对二进制数据进行位操作,如按位&amp;按位|按位异或 ^按位取反 ~,左移 <<,右移 >>
运算 描述 示例
&amp; 按位 a &amp; b
| 按位 a | b
^ 按位异或 a ^ b
~ 按位取反 ~a
<< 左移 a << b
>> 右移 a >> b
p q p &amp; q p | q p ^ q
0 0 0 0 0
0 1 0 1 1
1 1 1 1 0
1 0 0 1 1
  1. 赋值运算符:用于将值赋给变量,如赋值 =,加法赋值 +=,减法赋值 -=,乘法赋值 *=,除法赋值 /=,取余赋值 %=

  2. 其他运算符:包括取地址 &取值 *指针运算符 ->条件运算符 ? :,类型断言运算符 .管道运算符 |索引运算符 [],以及取长度 len() 等。

运算符 描述 示例
& 地址运算符 &a
* 取值运算符 *ptr
-> 指针运算符(C语言中使用) ptr->data
?: 条件运算符 condition ? expr1 : expr2
. 类型断言运算符 value.(type)
| 管道运算符 cmd1 | cmd2
[] 索引运算符 array[index]
len() 取长度函数 len(array)

除了要熟练使用这些运算符之外,我们还需要掌握运算符优先级

下面的表格中,由上至下代表优先级由高到低:

优先级 运算符
5 * / % << >> & &^
4 + – | ^
3 == != < <= > >=
2 &&
1 ||

Go语言条件语句

Go语言中的条件语句有两种形式:if语句和switch语句。

  1. if语句:
    if语句用于根据一个条件的真假执行不同的代码块。

语法:

if condition {
    // 当条件为真时执行的代码块
} else {
    // 当条件为假时执行的代码块(可选)
}

示例:

num := 10

if num%2 == 0 {
    fmt.Println("num偶数")
} else {
    fmt.Println("num奇数")
}

除了基本的if语句外,还可以使用if语句的简短语法:

语法:

if condition {
    // 当条件为真时执行的代码块
}

示例:

if num := 10; num > 0 {
    fmt.Println("num大于0")
}
  1. switch语句:
    switch语句用于基于不同的条件执行不同的代码块。

语法:

switch expression {
case value1:
    // 当expression等于value1时执行的代码块
case value2:
    // 当expression等于value2时执行的代码块
default:
    // 当expression不等于任何已匹配的值时执行的代码块(可选)
}

示例:

grade := "C"

switch grade {
case "A":
    fmt.Println("优秀")
case "B":
    fmt.Println("良好")
case "C":
    fmt.Println("及格")
default:
    fmt.Println("不及格")
}

在Go语言的switch语句中,每个case后面的值和expression的类型必须相同。此外,当匹配case执行完毕后,不会自动执行后续的case,而是跳出switch语句,除非使用fallthrough关键字。


Go语言循环语句

Go语言中有三种主要的循环语句:for循环while循环和do-while循环。

  1. for循环:
    for循环用于重复执行一段代码块,可以指定循环的起始条件、循环执行前的初始化语句,以及每次循环结束后的迭代语句。

语法:

for 初始化语句; 条件表达式; 迭代语句 {
    // 循环体
}

示例:

for i := 0; i < 5; i++ {
    fmt.Println(i)
}

在这里插入图片描述

  1. while循环:
    Go语言中没有专门的while循环关键字,但可以使用for循环来实现类似的功能。

语法:

for 条件表达式 {
    // 循环体
}

示例:

i := 0
for i < 5 {
    fmt.Println(i)
    i++
}

在这里插入图片描述

  1. do-while循环:
    Go语言中也没有专门的do-while循环关键字,但可以使用for循环结合break语句来实现类似的功能。

语法:

for {
    // 循环体
    if !条件表达式 {
        break
    }
}

示例:

i := 0
for {
    fmt.Println(i)
    i++
    if i >= 5 {
        break
    }
}

在这里插入图片描述

除了以上常用的循环语句外,Go语言还提供了range关键字用于遍历数组、切片、映射数据结构

示例:

arr := []int{1, 2, 3, 4, 5}
for index, value := range arr {
    fmt.Println(index, value)
}//在每次循环时,range 返回两个值:当前元素的下标 index对应的值 value

在这里插入图片描述

在循环中,常用continue和goto控制流程

  1. continue:

continue 是一个控制流程的关键字,用于跳过当前循环迭代中的剩余代码,直接进入一次迭代

示例:

for i := 0; i < 5; i++ {
    if i == 2 {
        continue
    }
    fmt.Println(i)
}

上述代码的输出结果为:

0
1
3  //i==2时,跳出当前循环,进入一次循环
4
  1. goto:
    goto 是一个控制流程的关键字,用于无条件地转移到程序中的一个标签

示例:

package main

import "fmt"

func main() {
	i := 0

Loop:
	for i < 5 {
		fmt.Println(i)
		i++
		if i == 3 {
			goto Loop
		}
	}
}

上述代码的输出结果为:

0
1
2
3
4

在这个例子中,我们使用 goto 关键字和标签 Loop 实现了一个完整的循环,当 i 的值为 3 时,程序会跳转到标签 Loop 处,继续执行循环。


Go语言函数

可以使用函数来执行需要的功能。Go 语言程序中最少有个 main() 函数。

Go 语言函数定义格式如下:

func function_name( [parameter list] ) [return_types] {
   函数体
}

举个例子

package main

import "fmt"

// 定义一个计算两个整数之和的函数
func add(x int, y int) int {
	return x + y
}

func main() {
	result := add(3, 5)
	fmt.Println(result) // 输出结果为 8
}

以上代码定义了一个名为 add 的函数。该函数接受两个整数类型的参数 xy,并返回它们的和。在 main 函数中,我们调用 add 函数,并将参数传递为 35。然后,将返回结果赋值给变量 result


Go语言数组

在Go语言中,数组是一种固定长度的数据结构,用于存储一组相同类型的元素。

语法格式如下:

var arrayName [size]dataType

其中,arrayName数组的名称,size数组大小dataType 是数组中元素的数据类型。

举个例子:

var a [10]float64

以上定义了数组 a 长度为 10 类型为 float64

数组的初始化

var numbers [5]int

数组初始值为0

var numbers = [5]int{1, 2, 3, 4, 5}

分别被赋值为1,2,3,4,5

numbers := [5]int{1, 2, 3, 4, 5}

如果数组长度不确定,可以使用 ... 代替数组的长度,编译器会根据元素个数自行推断数组的长度:

var a = [...]float32{1000.0, 2.0, 3.4, 7.0, 50.0}

如果设置了数组的长度,我们还可以通过指定下标来初始化元素:

//  将索引为 1 和 3 的元素初始化
a := [5]float32{1:9.9,3:7.7}

访问数组元素:

数组元素可以通过索引位置)来读取。格式为数组名后加中括号中括号中为索引的值。例如:

var b float32 = a[9]

以上实例读取了数组 a 第 10 个元素的值。


Go语言指针

在Go语言中,指针是一种特殊的数据类型,用于存储变量的内存地址指针可以用于间接访问和修改变量的值。

语法格式:

  1. 声明指针

    var ptr *Type
    

    其中,ptr指针变量的名称,Type 是指针所指向的变量类型。指针变量的初始化可以选择性地进行。

  2. 地址操作符(&):

    ptr = &variable
    

    使用 & 运算符可以获取变量 variable内存地址,并将其赋值给指针变量 ptr

  3. 引用操作符(*):

    value = *ptr
    

    使用 * 运算符可以获取指针变量 ptr 所指向的变量的值。

  4. 修改指针所指变量的值:

    *ptr = newValue
    

    使用 * 运算符,可以修改指针变量 ptr 所指向的变量的值。

下面是使用Go语言定义和操作指针的示例代码:

package main

import "fmt"

func main() {
    // 定义一个整数变量
    var num int = 42

    // 声明一个指向整数的指针,并将其初始化为num的内存地址
    var ptr *int = &num

    // 输出指针的值和所指向的变量的值
    fmt.Println("指针的值:", ptr) // 输出: 指针的值: 0xc000096068
    fmt.Println("指针所指向的变量的值:", *ptr) // 输出: 指针所指向的变量的值: 42

    // 修改所指向的变量的值
    *ptr = 100

    // 输出被修改后的变量的值
    fmt.Println("被修改后的变量的值:", num) // 输出: 被修改后的变量的值: 100
}

Go语言结构

在Go语言中,结构体(Struct)是一种用户定义的复合类型,用于封装不同类型的数据字段。结构体可以包含零个或多个字段,并且可以根据需要进行组合

语法格式

  1. 定义结构体:

    type StructName struct {
        field1 fieldType1
        field2 fieldType2
        // ...
    }
    

    其中,StructName 是结构体的名称,field1field2 等是字段的名称,fieldType1fieldType2 等是字段的类型。

  2. 创建结构体对象:

    var obj StructName
    

    使用 var 声明结构体对象,并初始化为零值。

  3. 访问结构体字段:

    obj.field = value
    

    使用对象名加上.操作符来访问结构体中的字段,并进行赋值或获取值操作。

下面代码展示了结构体的定义和使用:

package main

import "fmt"

// 定义一个结构体类型
type Person struct {
    name string
    age  int
}

func main() {
    // 创建一个结构体对象p1
    var p1 Person

    // 访问结构体字段并赋值
    p1.name = "Alice"
    p1.age = 20

    // 输出结构体字段的值
    fmt.Println("姓名:", p1.name)
    fmt.Println("年龄:", p1.age)

    // 创建结构体对象并初始化
    p2 := Person{name: "Bob", age: 25}

    // 输出结构体字段的值
    fmt.Println("姓名:", p2.name)
    fmt.Println("年龄:", p2.age)
}

结构体参数传递

在Go语言中,可以将结构体作为函数的参数进行传递,以便在函数中对结构体进行操作或使用结构体中的字段。

结构体作为函数参数有两种传递方式:值传递和引用传递。

  1. 值传递(Pass by Value):
    在值传递方式下,函数会复制传入的结构体,函数内部对结构体的修改不会影响原始结构体。

    下面是一个使用值传递方式的示例代码:

    package main
    
    import "fmt"
    
    type Person struct {
        name string
        age  int
    }
    
    func updateName(p Person) {
        p.name = "Alice"
    }
    
    func main() {
        p := Person{name: "Bob", age: 25}
        fmt.Println("修改前:", p)
    
        updateName(p)
        fmt.Println("修改后:", p)
    }
    
    //输出
    修改前: {Bob 25}
    修改后: {Bob 25}
    

    在该示例中,我们定义了一个 Person 结构体,并在 updateName 函数中修改了结构体的 name 字段。然而,在 main 函数中调用 updateName 函数时,输出结果仍然是原来的结构体,表明在函数内部对结构体字段的修改不会影响原始结构体。

  2. 引用传递(Pass by Reference):
    在引用传递方式下,函数接收的是结构体的指针,函数内部对结构体的修改会影响原始结构体。

下面是一个使用引用传递方式的示例代码:

   package main
   
   import "fmt"
   
   type Person struct {
       name string
       age  int
   }
   
   func updateName(p *Person) {
       p.name = "Alice"
   }
   
   func main() {
       p := &Person{name: "Bob", age: 25}
       fmt.Println("修改前:", p)
   
       updateName(p)
       fmt.Println("修改后:", p)
   }

在这里插入图片描述

结构体指针

在Go语言中,可以使用指针来操作结构体。通过指针,可以直接修改结构体的字段值,而无需进行复制操作。

指向结构体的指针,定义格式如下:

var struct_pointer *Person

查看结构体变量地址,可以将 & 符号放置于结构体变量前:

struct_pointer = &Person1

使用结构体指针访问结构体成员,使用 “.” 操作符

struct_pointer.title

实例如下:

package main

import "fmt"

type Person struct {
   name string
   age int
}

func main() {
   var Person1 Person        /* 声明 Person1 为 Person  类型 */
   var Person2 Person      /* 声明 Person2 为 Person  类型 */

   /* Person 1 描述 */
   Person1.name="秋说"
   Person1.age="1"


   /* Person 2 描述 */
   Person2.name="花无缺"
   Person2.age="2"


   /* 打印 Person1 信息 */
   printPerson(&Person1)

   /* 打印 Person2 信息 */
   printPerson(&Person2)
}
func printPerson( Person *Person ) {
   fmt.Printf( "Person name : %sn", Person.name)
   fmt.Printf( "Person age : %dn", Person.age)
}

在这里插入图片描述


Go语言切片

Go语言中的切片(Slice)是一种动态数组的抽象。切片提供了对底层数组的封装,可以方便地操作和管理数组的片段。

具体来说:

  • 切片是对数组的引用,它包含了指向底层数组的指针、切片的长度和切片的容量。
  • 切片的长度表示其中元素的个数,切片的容量则是从切片的起始位置到底层数组的末尾位置的元素个数
  • 切片的长度可以动态改变,而切片的容量只能向后扩展

在Go语言中,使用切片的语法为[]T,其中T表示切片中元素的类型。创建切片可以通过以下方式:

  1. 通过数组创建切片的模板语法:
slice := array[start:end]

其中,array 是一个已有的数组,start 是切片的起始索引(包含),end 是切片的结束索引(不包含)。这个语法将创建一个切片 slice,包含了从 start 索引到 end-1 索引的元素。

实例:

arr := [5]int{1, 2, 3, 4, 5}
slice := arr[1:4]  // 创建一个切片,包含arr索引1到索引3的元素,即[2, 3, 4]
  1. 使用 make 函数创建切片的模板语法:
slice := make([]T, length, capacity)

其中,T 是切片中元素的类型,length 是切片的长度,capacity 是切片的容量。通过 make 函数创建的切片具有指定的长度和容量,并初始化了相应类型的零值。

实例:

slice := make([]int, 3, 5)  // 创建一个长度为3,容量为5的切片
  1. 使用切片字面量创建切片的模板语法:
slice := []T{element1, element2, ..., elementN}

其中,T 是切片中元素的类型,element1elementN 是要添加到切片中的元素。使用切片字面量创建切片时,切片的长度会根据提供的元素个数自动确定。

实例:

slice := []int{1, 2, 3, 4, 5}  // 直接创建一个切片,包含元素1到5

切片常用的操作有以下几种

  • 获取切片的长度和容量:
length := len(slice)  // 获取切片的长度
capacity := cap(slice)  // 获取切片的容量
slice = append(slice, 6)  // 在切片的末尾追加元素6
for index, value := range slice {
    // 遍历切片的索引和对应的值
}
newSlice := slice[1:3]  // 对切片进行切割,创建新的切片包含索引1到2的元素
  1. append() 函数用于向切片末尾追加元素或另一个切片:
slice = append(slice, element1, element2, ..., elementN)

其中,slice 是要追加元素的切片,element1elementN 是要添加到切片中的元素。append() 函数会返回一个新的切片,如果原切片的容量不够,会自动分配更大的底层数组,并将新元素追加到其中。

示例:

slice := []int{1, 2, 3}
slice = append(slice, 4, 5)  // 追加元素 4 和 5
  1. copy() 函数用于将一个切片的内容复制到另一个切片:
copy(destSlice, srcSlice)

其中,destSlice目标切片,srcSlice 是源切片。copy() 函数会将源切片中的元素复制到目标切片中,两个切片必须有相同的元素类型。

示例:

srcSlice := []int{1, 2, 3}
destSlice := make([]int, len(srcSlice))
copy(destSlice, srcSlice)  // 复制 srcSlice 到 destSlice

需要注意的是,append() 函数会返回一个新的切片,因此在使用时需要将其赋值给原来的切片变量;而 copy() 函数则直接在目标切片上进行复制操作。

以下是一个使用切片的示例代码:

package main

import "fmt"

func main() {
    // 创建切片
    numbers := []int{1, 2, 3, 4, 5}
    
    // 获取切片长度和容量
    fmt.Println("Length:", len(numbers))   // 输出:Length: 5
    fmt.Println("Capacity:", cap(numbers))  // 输出:Capacity: 5
    
    // 追加元素
    numbers = append(numbers, 6)
    fmt.Println(numbers)  // 输出:[1 2 3 4 5 6]
    
    // 遍历切片
    for index, value := range numbers {
        fmt.Println(index, value)
    }
    
    // 切割切片
    newSlice := numbers[1:4]
    fmt.Println(newSlice)  // 输出:[2 3 4]
}

Go语言范围(Range)

Go语言中的范围(Range)是一种迭代数据结构(如数组、切片、映射等)的元素的方法。通过使用范围,可以遍历并访问数据结构中的每个元素,而不需要使用索引或迭代器。

范围语法如下:

for index, value := range collection {
    // 使用 index 和 value 来处理元素
}

其中,collection 是要迭代的数据结构(如数组、切片、映射等),index 是当前元素的索引,value 则是当前元素的值。在循环的每次迭代中,范围语句会将 indexvalue 更新为下一个元素的索引和值,直到遍历完整个集合

范围还支持忽略索引或值,如果你只关心其中一项,可以使用 _下划线)来忽略另一项。例如:

for _, value := range collection {
    // 只使用 value 处理元素,忽略索引
}

举个例子:

遍历简单的数组,2**%d 的结果为 2 对应的次方数:

package main

import "fmt"

var pow = []int{1, 2, 4, 8, 16, 32, 64, 128}

 func main() {
   for i, v := range pow {
    fmt.Printf("2**%d = %dn", i, v)
   }
 }

输出:

2**0 = 1
2**1 = 2
2**2 = 4
2**3 = 8
2**4 = 16
2**5 = 32
2**6 = 64
2**7 = 128

for 循环的 range 格式可以省略 key 和 value,如下实例:

package main
import "fmt"

 func main() {
   map1 := make(map[int]float32)
   map1[1] = 1.0
   map1[2] = 2.0
   map1[3] = 3.0
   map1[4] = 4.0

   // 读取 key 和 value
   for key, value := range map1 {
    fmt.Printf("key is: %d - value is: %fn", key, value)
   }

   // 读取 key
   for key := range map1 {
    fmt.Printf("key is: %dn", key)
   }

   // 读取 value
   for _, value := range map1 {
    fmt.Printf("value is: %fn", value)
   }
 }

以上实例运行输出结果为:

key is: 4 - value is: 4.000000
key is: 1 - value is: 1.000000
key is: 2 - value is: 2.000000
key is: 3 - value is: 3.000000
key is: 1
key is: 2
key is: 3
key is: 4
value is: 1.000000
value is: 2.000000
value is: 3.000000
value is: 4.000000

Go 语言Map(集合)

在Go语言中,Map(映射)是一种无序的键值对的集合。可以将其看作是一个动态的数组,其中每个元素都是一个键值对,即一个唯一的键关联一个值。您可以使用键来访问映射中的值,并可以通过添加、修改和删除元素来修改映射

Map的定义方式如下:

// 定义一个键为string类型,值为int类型的map
var m map[string]int

// 初始化map
m = map[string]int{"foo": 1, "bar": 2}

// 或者可以使用make函数初始化map
m = make(map[string]int)

可以通过make()函数来初始化一个空的Map对象,然后使用 map[key] = value 的方式向Map中添加元素。例如:

m := make(map[string]int)

// 添加元素
m["foo"] = 1
m["bar"] = 2

可以使用 delete() 函数来删除Map中的元素:

delete(m, "bar") // 删除键为"bar"的元素

可以使用 len() 函数获取Map中键值对的数量。还可以使用范围(Range)语句迭代Map中的所有键值对:

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

需要注意的是,Map 的遍历是无序的,因为 Map 内部实现哈希表(Hash Table)来存储键值对,所以元素的排列顺序是不确定的。

package main

import "fmt"

func main() {
	// 定义一个string类型的键,int类型的值的map
	scores := make(map[string]int)

	// 添加学生的成绩
	scores["张三"] = 90
	scores["李四"] = 85
	scores["王五"] = 98

	// 循环遍历map中的每个元素
	for name, score := range scores {
		fmt.Printf("%s的成绩是:%dn", name, score)
	}

	// 删除指定的元素
	delete(scores, "李四")

	// 输出删除后的map
	fmt.Println("删除李四之后的成绩:", scores)

	// 判断指定的键是否存在
	if score, ok := scores["张三"]; ok {
		fmt.Printf("张三的成绩是:%dn", score)
	} else {
		fmt.Println("找不到张三的成绩")
	}
}

输出:

张三的成绩是:90
李四的成绩是:85
王五的成绩是:98
删除李四之后的成绩: map[王五:98 张三:90]
张三的成绩是:90

Go 语言递归函数

在Go语言中,递归函数是指在函数体内调用自身的函数。递归函数是一种常用的算法设计技巧,可以简化问题解决方法,并且能够解决一些需要重复执行相同操作的问题

语法格式如下:

func recursion() {
   recursion() /* 函数调用自身 */
}

func main() {
   recursion()
}

下面是一个示例,展示如何使用递归函数来计算一个数的阶乘:

package main

import "fmt"

// 计算n的阶乘
func factorial(n int) int {
	if n <= 1 {
		return 1
	}
	return n * factorial(n-1)
}

func main() {
	num := 5
	result := factorial(num)
	fmt.Printf("%d的阶乘是:%dn", num, result)
}

在上面的代码中,factorial()函数是一个递归函数,用于计算给定n的阶乘。当n为1或更小的值时,递归终止条件被满足,直接返回1。否则,函数会调用自身,并将n减1后的结果与n相乘,然后返回乘积作为结果。

main()函数中,我们调用了factorial()函数来计算num这里是5)的阶乘,并将结果打印出来。

运行该程序,输出结果为:

5的阶乘是:120

以下实例通过 Go 语言的递归函数实现斐波那契数列:

package main

import "fmt"

func fibonacci(n int) int {
  if n < 2 {
   return n
  }
  return fibonacci(n-2) + fibonacci(n-1)
}

func main() {
    var i int
    for i = 0; i < 10; i++ {
       fmt.Printf("%dt", fibonacci(i))
    }
}

以上实例执行输出结果为:

0    1    1    2    3    5    8    13    21  34

Go 语言类型转换

在Go语言中,可以使用类型转换将一个类型的值转换为另一个类型。Go语言支持显式类型转换,并且只能在相互兼容的类型之间进行转换

下面是一些常见的类型转换示例:

package main

import "fmt"

func main() {
	// 整数类型转换
	var x int = 10
	var y float64 = float64(x)
	fmt.Println(y)

	// 数字类型转换
	var a float64 = 3.14
	var b int = int(a)
	fmt.Println(b)

	// 字符串类型转换
	var s string = "100"
	var c int = int(s) // 错误示例,无法直接将字符串转换为整数
	fmt.Println(c)

	// 使用strconv包进行字符串转换
	import "strconv"
	var s string = "100"
	c, _ := strconv.Atoi(s) // 将字符串转换为整数
	fmt.Println(c)
}

在上述示例中,我们展示了几种常见的类型转换。首先,将整数类型x转换为浮点数类型float64,并输出结果。然后,将浮点数类型a转换为整数类型int,并输出结果。接着,演示了错误的字符串类型转换示例,直接将字符串转换为整数会导致编译错误。为了解决这个问题,我们使用了strconv包中的Atoi()函数,将字符串转换为整数类型,并输出结果。

在进行类型转换时,如果两个类型不兼容或者转换不合法,编译器会报错。因此,在进行类型转换时,需要确保被转换的值和目标类型是兼容的。

另外,strconv包提供了更多的字符串转换函数,例如ParseInt()ParseFloat()等,可以根据需要选择适合的方法进行类型转换。


Go 语言接口

Go语言中的接口(interface)是一种类型,它描述了一组方法的集合。接口定义了方法签名,但是没有具体的实现代码。在Go语言中,通过实现接口的方法来实现接口的功能。

Go语言中声明接口的语法如下:

go
type 接口名称 interface {
	方法1() 返回类型
	方法2() 返回类型
	// ...
}

其中,接口名称是你给接口起的名字。接口中定义了一组方法,每个方法都由方法名、参数列表和返回类型组成。你可以在接口中定义任意数量的方法。

下面是一个简单的示例:

package main

import "fmt"

// 定义接口
type Shape interface {
	Area() float64
	Perimeter() float64
}

// 定义结构体 Circle,并实现 Shape 接口的方法
type Circle struct {
	Radius float64
}

func (c Circle) Area() float64 {
	return 3.14 * c.Radius * c.Radius
}

func (c Circle) Perimeter() float64 {
	return 2 * 3.14 * c.Radius
}

// 定义结构体 Rectangle,并实现 Shape 接口的方法
type Rectangle struct {
	Width  float64
	Height float64
}

func (r Rectangle) Area() float64 {
	return r.Width * r.Height
}

func (r Rectangle) Perimeter() float64 {
	return 2 * (r.Width + r.Height)
}

func main() {
	// 创建一个 Circle 实例
	circle := Circle{Radius: 5}
	fmt.Println("Circle Area:", circle.Area())
	fmt.Println("Circle Perimeter:", circle.Perimeter())

	// 创建一个 Rectangle 实例
	rectangle := Rectangle{Width: 3, Height: 4}
	fmt.Println("Rectangle Area:", rectangle.Area())
	fmt.Println("Rectangle Perimeter:", rectangle.Perimeter())
}

上述代码中,我们定义了一个Shape接口,其中包含了Area()Perimeter()两个方法。然后,我们创建了CircleRectangle两个结构体,并分别实现了Shape接口的方法。在main()函数中,我们创建了一个Circle实例和一个Rectangle实例,并调用它们的Area()Perimeter()方法。

Go语言中的接口是隐式实现的,即不需要显式声明接口的实现,只需实现接口所定义的方法即可


Go 错误处理

Go 语言通过内置错误接口提供了非常简单错误处理机制。

error 类型是一个接口类型,这是它的定义:

type error interface {
    Error() string
}

我们可以在编码中通过实现 error 接口类型来生成错误信息

函数通常在最后返回值中返回错误信息。使用 errors.New 可返回一个错误信息

func Sqrt(f float64) (float64, error) {
    if f < 0 {
        return 0, errors.New("math: square root of negative number")
    }
    // 实现
}

在下面的例子中,我们在调用 Sqrt 的时候传递的一个负数,然后就得到了 nonnilerror 对象,将此对象与 nil 比较,结果为 true,所以 fmt.Println(fmt 包在处理 error 时会调用 Error 方法)被调用,以输出错误,请看下面调用的示例代码:

result, err:= Sqrt(-1)

if err != nil {
   fmt.Println(err)
}
package main

import (
    "fmt"
)

// 定义一个 DivideError 结构
type DivideError struct {
    dividee int
    divider int
}

// 实现 `error` 接口
func (de *DivideError) Error() string {
    strFormat := `
    Cannot proceed, the divider is zero.
    dividee: %d
    divider: 0
`
    return fmt.Sprintf(strFormat, de.dividee)
}

// 定义 `int` 类型除法运算的函数
func Divide(varDividee int, varDivider int) (result int, errorMsg string) {
    if varDivider == 0 {
            dData := DivideError{
                    dividee: varDividee,
                    divider: varDivider,
            }
            errorMsg = dData.Error()
            return
    } else {
            return varDividee / varDivider, ""
    }

}

func main() {

    // 正常情况
    if result, errorMsg := Divide(100, 10); errorMsg == "" {
            fmt.Println("100/10 = ", result)
    }
    // 当除数为零的时候会返回错误信息
    if _, errorMsg := Divide(100, 0); errorMsg != "" {
            fmt.Println("errorMsg is: ", errorMsg)
    }

}

输出如下:

100/10 =  10
errorMsg is:  
    Cannot proceed, the divider is zero.
    dividee: 100
    divider: 0

Go 并发

Go 语言支持并发,我们只需要通过 go 关键字来开启 goroutine 即可

goroutine轻量级线程goroutine调度是由 Golang 运行时进行管理的。

goroutine 语法格式:

go 函数名( 参数列表 )

例如:

go f(x, y, z)

开启一个新的 goroutine:

f(x, y, z)

Go 允许使用 go 语句开启一个新的运行线程, 即 goroutine,以一个不同的、新创建的 goroutine 来执行一个函数。 同一个程序中的所有 goroutine 共享同一个地址空间

实例如下:

package main

import (
        "fmt"
        "time"
)

func say(s string) {
        for i := 0; i < 5; i++ {
                time.Sleep(100 * time.Millisecond)
                fmt.Println(s)
        }
}

func main() {
        go say("world")
        say("hello")
}

以上代码创建了两个 goroutine 分别输出 “hello” 和 “world”


Go语言开发工具汇总

  1. Go编译器(go):Go编译器是Go语言的官方编译器,用于将Go源代码编译成可执行文件或库。它还提供了一些命令行参数和选项,用于控制编译过程
  2. Go命令(go):Go命令是Go语言的一个重要工具,用于构建安装管理Go项目。通过Go命令,你可以创建新的项目构建项目运行测试下载依赖包等。
  3. Go Modules:Go Modules 是 Go 1.11 版本引入的官方依赖管理工具。它能够管理项目中的依赖关系,并支持版本控制,可以解决在 Go 语言中对于第三方版本管理的难题。
  4. GoDoc:GoDoc 是Go语言官方提供的文档工具,用于生成Go代码的文档。通过GoDoc,可以将代码中的文档注释提取出来,自动生成易于阅读的HTML文档,方便其他开发人员查看和使用你的代码。
  5. GoLand / Visual Studio Code / Sublime Text集成开发环境(IDE):有许多流行的集成开发环境(IDE)对于Go语言开发提供了强大的支持。这些IDE通常具有代码补全调试器、内置终端等功能,可以提高开发效率并简化调试过程
  6. VSCode Go插件:VSCode Go 插件是 Visual Studio Code 编辑器的一个插件,它提供了很多有用的功能,如语法高亮、自动完成、代码导航、格式化等。它可以帮助你更方便地编写和调试Go代码。
  7. GoTest:GoTest 是Go语言官方提供的测试工具,用于编写和运行Go的单元测试性能测试。通过编写测试用例并运行GoTest,可以确保代码的正确性和性能
  8. GoLint:GoLint 是一个静态代码分析工具,用于检查Go代码的风格和潜在问题。它可以提供一些建议和警告,帮助开发人员遵循Go语言的最佳实践。

写在最后

本文内容均为重点知识点,是学习Go的不二选择。学习不是一蹴而就的过程,切勿囫囵吞枣。

我是秋说,我们下次见。

原文地址:https://blog.csdn.net/2301_77485708/article/details/133799834

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任

如若转载,请注明出处:http://www.7code.cn/show_16583.html

如若内容造成侵权/违法违规/事实不符,请联系代码007邮箱:suwngjj01@126.com进行投诉反馈,一经查实,立即删除

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注