【GoLang#2】:基础入门(工具链 | 基础语法 | 内置函数)

发布于:2025-07-27 ⋅ 阅读:(18) ⋅ 点赞:(0)

前言:Go 的一些必备知识

1. Go 语言命名

Go的函数、变量、常量、自定义类型、包(package)的命名方式遵循以下规则:

  1. 首字符可以是任意的Unicode字符或者下划线
  2. 剩余字符可以是Unicode字符、下划线、数字
  3. 字符长度不限

Go 语言代码风格及开发事项

  • 代码每一行结束后不用写分号

  • 推荐使用驼峰式命名

  • Go 编译器是一行一行进行编译的,因此我们一行就写一条语句,不能把多条语句写在一起

  • Go 定义的变量 或 import 的包如果没有用到,代码不能编译通过

  • 左括号必须紧接着语句不换行

    func main() {
        fmt.Println("Hello Go")
    } 
    

2. Go 关键字

Go 一共有 25 个关键字

break        default      func         interface    select
case         defer        go           map          struct
chan         else         goto         package      switch
const        fallthrough  if           range        type
continue     for          import       return       var

37 个保留字

Constants:    true  false  iota  nil

Types:    int  int8  int16  int32  int64  
          uint  uint8  uint16  uint32  uint64  uintptr
          float32  float64  complex128  complex64
          bool  byte  rune  string  error

Functions:   make  len  cap  new  append  copy  close  delete
             complex  real  imag
             panic  recover

3. Go 语言声明

有四种主要的声明方式

var(声明变量), const(声明常量), type(声明类型) ,func(声明函数)

Go的程序是保存在多个.go文件中,文件的第一行就是 package XXX 声明,用来说明该文件属于哪个包(package),package声明下来就是 import 声明,再下来是类型,变量,常量,函数的声明。

可见性

  1. 声明在函数内部,是函数的本地值,类似private
  2. 声明在函数外部,是对当前包可见(包内所有.go文件都可见)的全局值,类似protect
  3. 声明在函数外部且首字母大写是所有包可见的全局值,类似public

4. Printf 和 Println 的区别

这个其实和 Java 有点像, Print 不自动换行,而 Println 会自动换行

还有就是变量输出的方法,如下:

func main(){
    a:=10
    b:=20
    fmt.Println("a=",a, "b=",b)	// a= 10 b= 20
    fmt.Printf("a=%d, b=%d", a, b) // a=10, b=20
}

5. Init 和 main 函数

在 Go 语言中,init() 函数和 main() 函数是程序执行的两个关键入口点,它们有明确的执行顺序和作用:

举个例子

func init() {
	fmt.Println("imp-init1()")
}

func init() {
	fmt.Println("imp-ini2()")
}

func main() {
	fmt.Println("main()")
}

// 输出
imp-init1()
imp-ini2()
main()

init() 函数特点

  • 自动执行:无需显式调用,在程序启动时自动运行。
  • 包级别:每个包(包括依赖包)都可以包含一个或多个 init() 函数。
  • 执行顺序
    1. 按包的依赖关系递归执行(先执行导入包的 init())。
    2. 同一包中,按文件名的字典序执行不同文件中的 init()
    3. 同一文件中,按代码中出现的顺序执行多个 init()
  • 用途
    • 初始化包级变量(尤其是复杂初始化)。
    • 注册操作(如数据库驱动、插件)。
    • 执行一次性配置(如读取配置文件)。
  • 无参数无返回值func init() { ... }

② main 函数特点

  • 程序入口main 包中的 main() 是唯一程序入口。
  • 执行时机:在所有依赖包的 init() 完成后执行。
  • 唯一性:整个程序只能有一个 main() 函数(位于 package main)。
  • 无参数无返回值func main() { ... }

关键区别

特性 init() 函数 main() 函数
所在包 任意包(包括依赖包) 必须在 main
数量 每个包可有多个 init() 整个程序仅一个 main()
调用方式 自动执行 自动执行(程序入口)
执行顺序 在导入的包初始化后、main() 在所有 init() 完成后执行
典型用途 初始化包级状态、注册操作 主程序逻辑入口

注意事项

  1. 避免滥用 init()
    • 复杂的初始化逻辑应封装成显式函数调用,而非全放在 init() 中。
    • 防止隐式依赖导致代码难以追踪。
  2. 慎用全局状态init() 中初始化的包级变量相当于全局状态,可能引发并发问题。
  3. 错误处理init() 中发生错误通常通过 panic 终止程序,需谨慎处理(如配置文件不存在)。
  4. 明确初始化顺序:当多个包有相互依赖的 init() 时,需确保执行顺序符合预期(通过调整导入顺序)。

一、Go 工具链

Go 提供了强大而完整的命令行工具链,让开发者能够高效地开发、测试、构建和部署 Go 应用程序。

1. 核心命令概览

$ go
Go is a tool for managing Go source code.

Usage:

    go command [arguments]

The commands are:

    build       compile packages and dependencies
    clean       remove object files
    doc         show documentation for package or symbol
    env         print Go environment information
    bug         start a bug report
    fix         run go tool fix on packages
    fmt         run gofmt on package sources
    generate    generate Go files by processing source
    get         download and install packages and dependencies
    install     compile and install packages and dependencies
    list        list packages
    run         compile and run Go program
    test        test packages
    tool        run specified go tool
    version     print Go version
    vet         run go tool vet on packages

Use "go help [command]" for more information about a command.

Additional help topics:

    c           calling between Go and C
    buildmode   description of build modes
    filetype    file types
    gopath      GOPATH environment variable
    environment environment variables
    importpath  import path syntax
    packages    description of package lists
    testflag    description of testing flags
    testfunc    description of testing functions

Use "go help [topic]" for more information about that topic.
  • go env:用于打印Go语言的环境信息。
  • go run:编译并运行命令源码文件。
  • go get:根据要求和实际情况从互联网上下载或更新指定的代码包及其依赖包,并对它们进行编译和安装。
  • go build:用于编译我们指定的源码文件或代码包以及它们的依赖包。
  • go install:编译并安装指定的代码包及它们的依赖包。
  • go clean:删除掉执行其它命令时产生的一些文件和目录。
  • go doc:可以打印附于Go语言程序实体上的文档。我们可以通过把程序实体的标识符作为该命令的参数来达到查看其文档的目的。
  • go test:用于对Go语言编写的程序进行测试。
  • go list:列出指定的代码包的信息。
  • go fix:把指定代码包的所有Go语言源码文件中的旧版本代码修正为新版本的代码。
  • go fmt:格式化代码
  • go vet:是一个用于检查Go语言源码中静态错误的简单工具。
  • go tool:工具集。
  • go mod:模块管理

2. 举例说明

① go run —— 编译运行

go run main.go # 运行单个文件
go run main.go utils.go # 运行多个文件
go run . # 运行当前包的所有 .go 文件
go run main.go arg1 arg2 # 传递参数给程序

② go fmt —— 代码格式化

// 格式化前
package main
import "fmt"
func main() {
	fmt.Println("Hello");fmt.Println("World")
}

// 格式化后 (go fmt)
package main

import "fmt"

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

③ go test —— 运行测试

go test # 运行当前目录所有测试
go test ./package # 运行指定包的测试
go test -v # 显示详细测试信息
go test -cover # 运行覆盖率测试

# 生成覆盖率报告
go test -coverprofile=coverage.out
go tool cover -html=coverage.out

比如

// math_test.go
package math

import "testing"

func TestAdd(t *testing.T) {
    result := Add(2, 3)
    if result != 5 {
        t.Errorf("Add(2, 3) = %d; want 5", result)
    }
}

func BenchmarkAdd(b *testing.B) {
    for i := 0; i < b.N; i++ {
        Add(2, 3)
    }
}

// 运行测试
go test -v
// 运行基准测试
go test -bench=.

④ go vet —— 代码静态分析

go vet # 分析当前包
go vet github.com/user/project # 分析指定包
go vet -v # 显示详细信息

⑤ go tool —— 工具集

go tool pprof # 性能分析工具
go tool cover # 覆盖率工具
go tool nm # 符号表查看
go tool objdump # 反汇编
go tool link # 链接器

⑥ go mod —— 模块管理

go mod init module_name # 初始化新模块
go mod tidy # 添加依赖
go mod graph # 查看依赖图
go mod verify # 验证依赖
go mod download # 下载依赖
go mod why package_name # 查看模块信息

# 具体操作
# 1. 创建新项目
mkdir myproject && cd myproject
go mod init github.com/user/myproject
# 2. 添加外部依赖
go get github.com/gin-gonic/gin
# 3. 整理依赖
go mod tidy

3. 完整开发流程

# 1. 初始化项目
mkdir myapp && cd myapp
go mod init github.com/user/myapp

# 2. 编写代码
# 创建 main.go 文件

# 3. 快速测试运行
go run main.go

# 4. 格式化代码
go fmt ./...

# 5. 静态分析
go vet

# 6. 编写测试
# 创建 *_test.go 文件

# 7. 运行测试
go test -v

# 8. 构建可执行文件
go build -o myapp main.go

# 9. 安装到系统
go install

组合命令

# 一键格式化、分析、测试
go fmt ./... && go vet ./... && go test ./...

跨平台编译

GOOS=linux GOARCH=amd64 go build main.go # 编译 Linux 版本
GOOS=windows GOARCH=amd64 go build main.go # 编译 Windows 版本
GOOS=darwin GOARCH=amd64 go build main.go # 编译 macOS 版本

性能优化编译

go build -ldflags "-s -w" main.go # 优化编译
go build -ldflags "-X main.version=1.0.0" main.go# 指定版本信息

二、基础语法

1. 变量 & 常量 申明

变量背景:程序运行过程中的数据都是保存在内存中,我们想要在代码中操作某个数据时就需要去内存上找到这个变量,但是如果我们直接在代码中通过内存地址去操作变量的话,代码的可读性会非常差而且还容易出错,所

1.1 常见的变量定义方式

变量名称命名要求:由 字母、数字、下划线组成,其中首个字符不能为数字,而且 Go 语言中 关键字 和 保留字 都不能用作变量名

Go语言在声明变量的时候,会自动对变量对应的内存区域进行初始化操作。每个变量会被初始化成其类型的默认值,例如: 整型和浮点型变量的默认值为0。 字符串变量的默认值为空字符串。 布尔型变量默认为false。 切片、函数、指针变量的默认为nil

当然我们也可在声明变量的时候为其指定初始值。变量初始化的标准格式如下:

var 变量名 类型 = 表达式

① var 声明和定义变量

var age int
var name string = "zs" 

// 批量声明
var(
	email string = "@12.com"
    phone int = 123
    flag bool = true
    namex = "zs"
)
var a1, a2, a3 string // 这样也可以

② 类型推导 定义变量

有时候我们会将变量的类型省略,这个时候编译器会根据等号右边的值来推导变量的类型完成初始化。

var name = "ls"

③ 短变量声明

在函数内部,可以使用更简略的 := 方式声明并初始化变量。

// 简短声明
:a = 10
:b = 20
  • 注意:短变量 := 只能用于声明局部变量,不能用于全局变量

如下是变量作用域

// 全局变量
var globalVar = "全局变量"

func main() {
    // 局部变量
    localVar := "局部变量"
    fmt.Println(globalVar, localVar)
    
    // 块级作用域
    {
        blockVar := "块级变量"
        fmt.Println(blockVar)
    }
    // fmt.Println(blockVar) // 错误:blockVar 作用域已结束
}

④ 匿名变量

在使用多重赋值时,如果想要忽略某个值,可以使用匿名变量(anonymous variable)。 匿名变量用一个下划线_表示,例如:

func foo() (int, string) {
    return 10, "Q1mi"
}
func main() {
    x, _ := foo()
    _, y := foo()
    fmt.Println("x=", x)
    fmt.Println("y=", y)
}

匿名变量 不占用命名空间不会分配内存,所以匿名变量之间不存在重复声明。 (在Lua等编程语言里,匿名变量也被叫做 哑元变量。)

注意事项

  • 函数外的每个语句都必须以关键字开始(var、const、func等)
  • := 不能使用在函数外。
  • _ 多用于占位,表示忽略值。

注意:go 语言中变量定义以后必须要使用,而且同一作用域内不支持重复声明

func main(){
    var a = "aa" 
    fmt.Println(a) // 不使用就会提示错误
}

补充:下面赋值正确的是:

  • A. var x = nil B. var x interface{} = nil
  • C. var x string = nil D. var x error = nil

参考答案及解析:BD。这道题考的知识点是 nil。nil 只能赋值给指针、chan、func、interface、map 或 slice 类型的变量。强调下 D 选项的 error 类型,它是一种内置接口类型,看它的源码就知道,所以 D 是对的。

1.2 常量声明

相对于变量,常量是恒定不变的值,多用于定义程序运行期间不会改变的那些值。 常量的声明和变量声明非常类似,只是把var换成了const,常量在定义的时候必须赋值。

func main() {
    // 1. 基本常量声明
    const pi = 3.14159
    const maxUsers = 1000
    
    // 2. 指定类型常量
    const version string = "1.0.0"
    const port int = 8080
    
    // 3. 批量声明
    const (
        appName = "MyApp"
        appVersion = "2.0"
        debugMode = true
    )
    fmt.Println(pi, maxUsers, version)
}
  • 注意:常量定义之后必须要赋值,不能先声明再赋值

const同时声明多个常量时,如果省略了值则表示和上面一行的值相同。 例如:

const (
    n1 = 100
    n2
    n3
)

上面示例中,常量n1、n2、n3的值都是100

1.3 const 常量结合 iota 使用

iota 是 Go 语言的常量计数器,只能在常量的表达式中使用(否则会出现 编译错误)

  • iota 在 const 关键字出现时将被重置为 0(const 内部的第一行之前),const 中每新增一行常量声明将使 iota 计数一次
  • iota 可理解为 const 语句块的行索引,使用iota能简化定义,在定义枚举时很有用。

举个例子

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

常见的 iota 示例

① 使用 _ 跳过某些值

const (
    n1 = iota //0
    n2        //1
    _
    n4        //3
)

iota 声明中间插队

const (
        n1 = iota //0
        n2 = 100  //100
        n3 = iota //2
        n4        //3
    )
const n5 = iota //0

③ 带步长的 iota

const (
    A = iota * 10    // 0
    B                // 10
    C                // 20
    D                // 30
)

//这里的<<表示左移操作,1<<10表示将1的二进制表示向左移10位,也就是由1变成了10000000000,也就是十进制的1024。同理2<<2表示将2的二进制表示向左移2位,也就是由10变成了1000,也就是十进制的8。
const (
    _  = iota
    KB = 1 << (10 * iota)
    MB = 1 << (10 * iota)
    GB = 1 << (10 * iota)
    TB = 1 << (10 * iota)
    PB = 1 << (10 * iota)
)

④ 多个 iota 定义在一行

const (
    a, b = iota + 1, iota + 2 //1,2
    c, d                      //2,3
    e, f                      //3,4
)

2. 数据类型

Go 预言者的数据类型一般分为两种:

  • 基本数据类型:整型、浮点型、布尔型、字符串
  • 复合数据类型:数组、切片、结构体、函数、map、通道(channel)、接口

Golang 更明确的数字类型命名,支持 Unicode,支持常用数据结构。

类型 长度(字节) 默认值 说明
bool 1 false
byte 1 0 uint8
rune 4 0 Unicode Code Point, int32
int, uint 4或8 0 32 或 64 位
int8, uint8 1 0 -128 ~ 127, 0 ~ 255,byte是uint8 的别名
int16, uint16 2 0 -32768 ~ 32767, 0 ~ 65535
int32, uint32 4 0 -21亿~ 21亿, 0 ~ 42亿,rune是int32 的别名
int64, uint64 8 0
float32 4 0.0
float64 8 0.0
complex64 8
complex128 16
uintptr 4或8 以存储指针的 uint32 或 uint64 整数
array 值类型
struct 值类型
string “” UTF-8 字符串
slice nil 引用类型
map nil 引用类型
channel nil 引用类型
interface nil 接口
function nil 函数
2.1 基本数据类型

整型可以分为下面两个大类

  • 有符号整型按长度分为:int8、int16、int32、int64
  • 对应的无符号整型:uint8、uint16、uint32、uint64

特殊整型

  • uint:32 位操作系统上就是 uint32,64 位操作系统上就是 uint64
  • int:32 位操作系统上就是 int32,64 位操作系统上就是 int64
  • uintptr:无符号整型, 用于存放一个指针

可以通过 unsafe.Sizeof() 查看不同长度的整型在内存中的存储空间

浮点型

Go 语言支持两种浮点型数: float32 | float64,这两种浮点型数据格式遵循 IEEE 754 标准。

  • float32 浮点数的最大范围位 3.4e38,可用常量定义为: math.MaxFloat32
  • float32 浮点数的最大范围位 1.8e308,可用常量定义为: math.MaxFloat64

Go 中 float 精度丢失问题

func main() {
	var f float64 = 1129.6
	fmt.Println(f * 100)	// 112959.99999999999
}
  • 而且 减法的时候也会出现这种问题,一般来说可以引入第三方包来调用相关方法来解决

举例说明

func main() {
    // 整数类型
    var a int = 42           // 根据平台决定大小
    var b int8 = 127         // 8位整数 (-128 to 127)
    var c int16 = 32767      // 16位整数
    var d int32 = 2147483647 // 32位整数
    var e int64 = 9223372036854775807 // 64位整数
    
    var f uint = 42          // 无符号整数
    var g uint8 = 255        // 8位无符号整数
    var h uint16 = 65535     // 16位无符号整数
    
    // 浮点数类型
    var i float32 = 3.14     // 32位浮点数
    var j float64 = 3.141592653589793 // 64位浮点数(默认)
    
    // bool 类型 -- 默认值为 false
    var flag = true
    
    // 复数类型
    var k complex64 = 1 + 2i
    var l complex128 = 3 + 4i
    
    // 字节和别名
    var m byte = 'A'         // uint8 的别名
    var n rune = '中'        // int32 的别名,表示 Unicode 码点
    
	fmt.Printf("整数: %d %d %d %d %d\n", a, b, c, d, e)
	fmt.Println("int 字节大小: " unsafe.Sizeof(a))	// 8

	fmt.Printf("无符号: %d %d %d\n", f, g, h)
	fmt.Printf("浮点数: %.2f %.6f\n", i, j)
	fmt.Printf("复数: %v %v\n", k, l)	// 复数: (1+2i) (3+4i)
	fmt.Printf("字符: %c %c\n", m, n)
}
2.2 字符串类型

字符串转义符

转义符 含义
\r 回车符(返回行首)
\n 换行符(直接跳到下一行同列位置)
\t 制表符
\' 单引号
\" 双引号
\\ 反斜杠

字符串 常用操作

  • len(s):求长度

  • + 或者 fmt.Sprintf():拼接字符串

  • strings.Split():分割字符串

  • strings.contains:判断是否包含该子串

  • strings.HasPrefix(), strings.HasSuffix:前缀/后缀 判断

  • strings.Index(), strings.LastIndex():子串出现位置

  • strings.Join(a[]string, sep string):Join 操作

举例说明

func main() {
	s1 := "goLang"
	fmt.Printf("%v -- %T\n", s1, s1)

	// 输出 多行字符串 使用 `
	s2 := `"Hello
	"World`
	fmt.Println(s2)
	fmt.Println("中文: ", len("你"), "英文: ", len("s")) // 中文:  3 英文:  1

	// 字符串拼接
	result := s1 + " " + s2
	fmt.Println(result)

	// 字符串遍历
	for i, char := range s1 {
		fmt.Printf("索引 %d: %c\n", i, char)
	}
    
    arr := []string{"c++", "java", "go"} // 切片
	s3 := strings.Join(arr, "-")
	fmt.Println(s3) // c++-java-go
}
2.3 字符类型

Go 语言的字符有以下两种类型

  1. uint8 类型(byte):代表 ASCII 码的一个字符
  2. rune 类型:代表一个 UTF-8 字符

当需要处理 中文、日文 或 其他复合字符 时候,需用到 rune 类型,rune 类型实际是一个 int32

byte 类型:组成每个字符串的元素叫作 “字符”,可以通过遍历字符串元素获取字符,字符用 '' 包裹起来,比如:

func main(){
    a := 'a'
    b := '0'
    // 直接输出 byte 得到的是这个字符对应码值
    fmt.Println(a)
    fmt.Println(b)
    // 格式化输出 得到这个字符
    fmt.Printf("%c--%c\n", a, b)
    
    // 循环输出字符串字符
    s := "你好 golang"
    for i := 0; i < len(s); i++{
        fmt.Printf("%v(%c) ", s[i], s[i])
    }
}

上面我们看输出结果会发现 循环输出 中文的时候会出现乱码字符,此时就应该使用 rune 来处理 Unicode

func main() {
    s := "你好 golang"
	for _, v := range s {
		fmt.Printf("%v(%c) ", v, v)
	}
}

注意:修改字符串需要先将其转成 []rune[]byte,完成后再转为 string,但是无论哪种转换都会重新分配内存,并复制 字节数组

func main() {
	s := "big"
	// s[0] = 'p'	// 报错
	// 复制
	bs := []byte(s)
	bs[0] = 'p'
	fmt.Println(string(bs))
}
2.4 数据类型转换
func main() {
	// 1. 整型和整型间转换
	var (
		a1 int8  = 20
		a2 int16 = 40
	)
	fmt.Println(int16(a1) + a2)

	// 2. 整型和浮点型转换
	var f float64 = 20
	fmt.Println(float64(a1) + f)

	// 3. 其他类型 -> string (Sprintf)
	// 注意: Sprintf 使用转换格式 int(%d), float(%f), bool(%t), byte(%c)
	s := fmt.Sprintf("%d", a1)
	fmt.Printf("v: %v, type: %T\n", s, s)

	// 4. 通过 strconv 进行转换
	var a int = 8
	s1 := strconv.Itoa(a) // 也可以  := strcinv.FomatInt(a1, 10) // 指定进制
	fmt.Printf("v: %v, type: %T\n", s1, s1)
	// folat 转换参数如下:
	// 参数1: 要转换值 		参数2: 格式化类型 'f'、'b'、'e'、'E'、'g'、'G'
	// 参数3: 保留小数点 -1(不对小数点格式化)	参数4: 格式化的类型
	s2 := strconv.FormatFloat(f, 'f', 2, 64)
	fmt.Printf("v: %v, type: %T\n", s2, s2)
}

注意:go 中 数值类型 无法直接转成 bool 类型,同理 bool 也不能直接转成数值类

2.5 占位符格式

fmt 包实现了格式化 IO 函数,类似于 C 的 printf 和 scanf,格式 占位符 衍生自 C,但比 C 更简单

使用如下

package main
import "fmt"

func main() {
	a := 10
	fmt.Printf("a=%v, %T", a, a)
}
// 输出: a=10, int

一般

%v	相应值的默认格式, 在打印结构体时, "加号" 标记 (% + v) 会添加字段名
%#v 相应值的 Go 语法表示
%T	相应值的类型的 Go 语法表示
%%  字面上上的百分号, 并非值的占位符

布尔:%t 单词 ture 或 false

整数

%b	二进制表示
%c	相应 Unicode 码点所表示字符
%d	十进制表示
%o	八进制表示
%q	单引号围绕的字符字面值, 由 Go 语法安全的转义
%x  十六进制表示, 字母形式为小写 a-f
%X	十六进制表示, 字母形式为大写 A-F
%U	Unicode格式: U + 1234, 等同于 "U+%04X"

浮点数及其复合构成

%b 无小数部分的、指数为 2 的幂的科学计数法,-123456p-78
%e 	科学计数法,-1234.56e+78
%E 	科学计数法,-1234.56E+78
%f 	有小数点但无指数,123.456
%g	根据情况选择 %e 或 %f 以产生更紧凑的(无末尾的0)输出
%G	根据情况选择 %E 或 %f 以产生更紧凑的(无末尾的0)输出

3. 运算符 & 控制语句

运算符就那些最基本的,这里就不过过多讲解,直接来看看 控制语句 相关例子

条件语句
Go 的 if 语句表达式外无需小括号 ( ),而大括号 { } 则是必须的

  • 而且if 语句可以在条件表达式前执行一个简短语句,该语句声明的变量作用域仅在 if 之内。
  • 在Go语言中,else必须紧跟在if的右大括号后面,不能换行(即else不能放在下一行的开头,除非前面有右大括号)
func main() {
    score := 85
    
    // 基本 if 语句
    if score >= 60 {
        fmt.Println("及格")
    }
    
    // if-else 语句
    if score >= 90 {
        fmt.Println("优秀")
    } else if score >= 80 {
        fmt.Println("良好")
    } else if score >= 60 {
        fmt.Println("及格")
    } else {
        fmt.Println("不及格")
    }
    
    // if 中的简短声明
    if n := len("Hello"); n > 5 {
        fmt.Println("字符串长度大于5")
    } else {
        fmt.Println("字符串长度小于等于5")
    }
    
    // 注意:n 在这里不可访问
    // fmt.Println(n) // 错误!
}

循环语句

Go 的for 循环 与 if 语句类似,C/C++ 中的While循环相当于Go中的 for 循环

func main() {
    // 1. 传统 for 循环
    for i := 0; i < 5; i++ {
        fmt.Printf("i = %d ", i)
    }
    fmt.Println()
    
    // 2. while 风格循环
    j := 0
    for j < 5 {
        fmt.Printf("j = %d ", j)
        j++
    }
    fmt.Println()
    
    // 3. 无限循环
    count := 0
    for {
        count++
        if count > 3 {
            break
        }
        fmt.Printf("count = %d ", count)
    }
    fmt.Println()
    
    // 4. continue 和 break
    for i := 0; i < 10; i++ {
        if i%2 == 0 {
            continue // 跳过偶数
        }
        if i > 7 {
            break // 大于7时跳出
        }
        fmt.Printf("%d ", i)
    }
    fmt.Println()
}

Go 中可以使用 for range 遍历数组、切片、字符串、map 及 通道,通过 for range 遍历的返回值规律如下:

  1. 数组、切片、字符串 返回索引和值
  2. map 返回 键和值
  3. 通道(channel) 只返回通道内的值

例子

func main(){
     // 1. for range 键值循环(遍历数组、切片、map等)
    numbers := []int{1, 2, 3, 4, 5}
    for index, value := range numbers {
        fmt.Printf("索引 %d: 值 %d\n", index, value)
    }
    
    // 2. 只遍历索引
    for index := range numbers {
        fmt.Printf("索引: %d ", index)
    }
    fmt.Println()
    
    // 3. 只遍历值
    for _, value := range numbers {
        fmt.Printf("值: %d ", value)
    }
    fmt.Println()
}

分支语句

func main() {
    grade := "B"
    
    // 基本 switch
    switch grade {
    case "A":
        fmt.Println("优秀")
    case "B":
        fmt.Println("良好")
    case "C":
        fmt.Println("及格")
    default:
        fmt.Println("不及格")
    }
    
    // 多条件 case
    score := 85
    switch {
    case score >= 90:
        fmt.Println("优秀")
    case score >= 80:
        fmt.Println("良好")
    case score >= 60:
        fmt.Println("及格")
    default:
        fmt.Println("不及格")
    }
    
    // switch 中的简短声明
    switch day := "Monday"; day {
    case "Monday", "Tuesday", "Wednesday", "Thursday", "Friday":
        fmt.Println("工作日")
    case "Saturday", "Sunday":
        fmt.Println("周末")
    }
}
fallthrough

在 Go 语言中,fallthrough 是一个特殊的控制流语句,用于在 switch 语句中强制"穿透"到下一个 case 块。它的主要用途是允许单个匹配条件执行多个 case 块,这在某些特定场景下非常有用。

如下:

switch n := 2; n {
case 1:
    fmt.Println("Case 1")
    fallthrough // 强制穿透到下一个 case
case 2:
    fmt.Println("Case 2")
    fallthrough // 继续穿透
case 3:
    fmt.Println("Case 3")
default:
    fmt.Println("Default case")
}

注意

  • 必须是 case 块的最后一条语句,后面不能有其他语句(编译器会报错)
  • 只能穿透到下一个 case 块,不能穿透到 default 块(如果后面是 default 会执行它)

4. 数组

func main() {
    // 1. 声明数组
    var arr1 [5]int                    // 声明长度为5的整数数组
    var arr2 [3]string = [3]string{"a", "b", "c"} // 声明并初始化
    arr3 := [4]float64{1.1, 2.2, 3.3, 4.4}        // 类型推导
    
    // 2. 自动计算长度
    arr4 := [...]int{10, 20, 30, 40} // 长度自动计算为4
    
    // 3. 访问和修改数组元素
    fmt.Println("arr1[0]:", arr1[0]) // 默认值为0
    arr1[0] = 100
    fmt.Println("修改后 arr1[0]:", arr1[0])
    
    // 4. 数组长度
    fmt.Printf("arr2 长度: %d\n", len(arr2))
    fmt.Printf("arr3 长度: %d\n", len(arr3))
    
    // 5. 遍历数组
    fmt.Println("遍历 arr4:")
    for i := 0; i < len(arr4); i++ {
        fmt.Printf("arr4[%d] = %d ", i, arr4[i])
    }
    fmt.Println()
    
    // 6. range 遍历
    fmt.Println("range 遍历 arr3:")
    for index, value := range arr3 {
        fmt.Printf("索引 %d: 值 %.1f\n", index, value)
    }
    
    // 7. 数组是值类型(拷贝传递)
    arr5 := [3]int{1, 2, 3}
    arr6 := arr5 // 拷贝整个数组
    arr6[0] = 100
    fmt.Printf("arr5: %v, arr6: %v\n", arr5, arr6) // arr5 不变
    
    // 8. 多维数组
    var matrix [2][3]int = [2][3]int{
        {1, 2, 3},
        {4, 5, 6},
    }
	var pos = [2][2] string{
        {"北京", "上海"},
        {"广州", "深圳"},
    }
    fmt.Println("二维数组:")
    for i := 0; i < len(matrix); i++ {
        for j := 0; j < len(matrix[i]); j++ {
            fmt.Printf("%d ", matrix[i][j])
        }
        fmt.Println()
    }
}

实用示例 1

func main() {
    // 示例1:数组初始化的不同方式
    var scores [5]float64
    scores = [5]float64{95.5, 87.0, 92.5, 88.0, 90.0}
    
    // 计算平均分
    sum := 0.0
    for _, score := range scores {
        sum += score
    }
    average := sum / float64(len(scores))
    fmt.Printf("平均分: %.2f\n", average)
    
    // 示例2:查找最大值
    numbers := [...]int{34, 78, 23, 90, 12, 67}
    max := numbers[0]
    for _, num := range numbers {
        if num > max {
            max = num
        }
    }
    fmt.Printf("数组中的最大值: %d\n", max)
}

实用示例2(值 类型 & 引用类型)

// 数组作为函数参数(值传递)
func modifyArray(arr [3]int) {
    arr[0] = 100
    fmt.Println("函数内 arr:", arr)
}

// 数组指针作为参数(引用传递)
func modifyArrayPtr(arr *[3]int) {
    (*arr)[0] = 100
    fmt.Println("函数内 arr:", *arr)
}

func main(){
    // 数组传递
    arr := [3]int{1, 2, 3}
    fmt.Println("调用前 arr:", arr)
    modifyArray(arr)
    fmt.Println("调用后 arr:", arr) // 原数组不变
    
    modifyArrayPtr(&arr)
    fmt.Println("指针调用后 arr:", arr) // 原数组改变
    
    // 值类型: 数组
	var a1 = [...]int{1, 2, 3}
	a2 := a1
	a1[0] = 11
	fmt.Println(a1) // 11 2 3
	fmt.Println(a2) // 1 2 3

	// 引用类型: 切片
	var b1 = []int{1, 2, 3}
	b2 := b1
	b1[0] = 11
	fmt.Println(b1) // 11 2 3
	fmt.Println(b2) // 11 2 3
}

5. 下划线

“_” 是特殊标识符,用来忽略结果

① 下划线在 import 中

在Golang里,import的作用是导入其他package

import 下划线(如:import hello/imp)的作用:当导入一个包时,该包下的文件里所有 init() 函数都会被执行,然而,有些时候我们并不需要把整个包都导入进来,仅仅是是希望它执行 init() 函数而已。

  • 这个时候就可以使用 import 引用该包。即使用【import _ 包路径】只是引用该包,仅仅是为了调用 init() 函数,所以无法通过包名来调用包中的其他函数。

示例

// 代码结构
src 
|
+--- main.go            
|
+--- hello
       |
        +--- hello.go

package main

import _ "./hello"

func main() {
    // hello.Print() 
    //编译报错:./main.go:6:5: undefined: hello
}

// hello.go
package hello

import "fmt"

func init() {
    fmt.Println("imp-init() come here.")
}

func Print() {
    fmt.Println("Hello!")
}

输出结果

imp-init() come here.

② 下划线在代码中

import "os"

func main() {
    buf := make([]byte, 1024)
    f, _ := os.Open("/Users/***/Desktop/text.txt")
    defer f.Close()
    for {
        n, _ := f.Read(buf)
        if n == 0 {
            break    

        }
        os.Stdout.Write(buf[:n])
    }
}

解释1

  • 下划线意思是忽略这个变量
  • 比如 os.Open,返回值为 *os.File,error;普通写法是 f,err := os.Open("xxxxxxx")
  • 如果此时不需要知道返回的错误值,就可以用 f, _ := os.Open("xxxxxx"),如此则忽略了error变量

解释2

  • 占位符,意思是那个位置本应赋给某个值,但是咱们不需要这个值,所以就把该值赋给下划线,意思是丢掉不要。
  • 这样编译器可以更好的优化,任何类型的单个值都可以丢给下划线。
  • 这种情况是占位用的,方法返回两个结果,而你只想要一个结果。那另一个就用 “_” 占位,而如果用变量的话,不使用,编译器是会报错的。

补充

import "database/sql"
import _ "github.com/go-sql-driver/mysql"

第二个import就是不直接使用mysql包,只是执行一下这个包的init函数,把mysql的驱动注册到sql包里,然后程序里就可以使用sql包来访问mysql数据库了。

三、函数初始

我们这里暂时就看一些最基本的函数,后面再来深入

// 最简单的函数
func sayHello() {
    fmt.Println("Hello, World!")
}

// 带参数的函数
func greet(name string) {
    fmt.Printf("Hello, %s!\n", name)
}

// 带返回值的函数
func add(a, b int) int {
    return a + b
}

// 多返回值函数
func divide(a, b float64) (float64, error) {
    if b == 0 {
        return 0, fmt.Errorf("除数不能为零")
    }
    return a / b, nil
}

func main() {
    sayHello()
    greet("张三")
    
    result := add(3, 5)
    fmt.Printf("3 + 5 = %d\n", result)
    
    quotient, err := divide(10, 2)
    if err != nil {
        fmt.Println("错误:", err)
    } else {
        fmt.Printf("10 / 2 = %.2f\n", quotient)
    }
}

内置函数

Go 语言拥有一些不需要进行导入操作就可以使用的内置函数。它们有时可以针对不同的类型进行操作,例如:len、cap 和 append,或必须用于系统级的操作,例如:panic。因此,它们需要直接获得编译器的支持。

  • append :用来追加元素到数组、slice中,返回修改后的数组、slice
  • close:主要用来关闭channel
  • delete:从map中删除key对应的value
  • panic:停止常规的 goroutine (panic和recover:用来做错误处理)
  • recover:允许程序定义goroutine的panic动作
  • imag :返回complex的实部 (complex、real imag:用于创建和操作复数)
  • real:返回complex的虚部
  • make:用来分配内存,返回Type本身(只能应用于slice, map, channel)
  • new:用来分配内存,主要用来分配值类型,比如int、struct。返回指向Type的指针
  • cap:capacity是容量的意思,用于返回某个类型的最大容量(只能用于切片和 map)
  • copy:用于复制和连接slice,返回复制的数目
  • len:来求长度,比如string、array、slice、map、channel ,返回长度
  • print、println :底层打印函数,在部署环境中建议使用 fmt 包

在这里插入图片描述


网站公告

今日签到

点亮在社区的每一天
去签到