Go 快速指南
Go - 快速入门指南
Section titled “Go - 快速入门指南”本指南提供了 Go 编程语言的快速概览,涵盖了从环境设置到核心语言特性的基本方面。它旨在为初学者和希望快速回顾的人提供帮助。
Go - 概览
Section titled “Go - 概览”Go,也被称为 Golang,是一种开源编程语言,由 Robert Griesemer、Rob Pike 和 Ken Thompson 在 Google 设计。它于 2009 年 11 月公开宣布。Go 是一种静态类型、编译型语言,以其简洁性、高效性、对并发编程的强大支持以及健壮的标准库而闻名。它非常适合系统编程、Web 开发、网络服务和命令行工具。
Go 程序由 package(包)构建而成,这有助于高效的 dependency management(依赖管理)。Go toolchain(工具链)使用传统的 compile-and-link(编译和链接)模型来生成 statically linked executable binaries(静态链接的可执行二进制文件),这简化了 deployment(部署),因为它们通常没有外部依赖。
- Simple and Readability(简洁性和可读性):Go 追求简洁清晰的语法,使代码更易于阅读和维护。
- Concurrency(并发性):通过 goroutine(轻量级线程)和 channel(通道,用于 Goroutine 之间通信)以及
select语句提供内置的并发编程支持。 - Fast Compilation(快速编译):编译时间显著快速,提高了开发人员的生产力。
- Static Typing with Type Inference(带类型推断的静态类型):强大的静态类型有助于在编译时捕获错误,而 type inference(类型推断,例如
x := 0)减少了冗余。 - Garbage Collection(垃圾回收):通过垃圾回收进行自动内存管理。
- Powerful Standard Library(强大的标准库):提供广泛功能的综合标准库。
- Interfaces for Composition(用于组合的接口):隐式 interface(接口)鼓励使用 composition(组合)而不是 inheritance(继承)。
- Static Linking(静态链接):默认生成单个静态链接的二进制文件,简化了部署。
主要语言特性(以及有意不同的地方)
Section titled “主要语言特性(以及有意不同的地方)”为了保持简洁并实现其设计目标,Go 有意省略或修改了其他语言中常见的某些特性:
- No Type Inheritance(无类型继承):Go 倾向于使用 struct embedding(结构体嵌入)和 interface(接口)进行组合,而不是传统的继承。
- No Method or Operator Overloading(无方法或运算符重载):每个 method(方法)和 operator(运算符)都有固定的含义。
- No Circular Dependencies(无循环依赖):package(包)的依赖必须形成一个有向无环图(DAG)。
- Restricted Pointer Arithmetic(受限的指针运算):出于安全考虑,Go 不允许直接的 pointer arithmetic(指针运算),尽管
unsafe包在绝对必要时提供了低级能力。 - No Assertions (in the traditional sense)(无断言(传统意义上)):Go 鼓励显式的 error handling(错误处理)。
- Generics(泛型):最初为了保持简洁性不支持泛型,但在 Go 1.18 中引入,允许编写独立于特定类型的代码,增强了某些上下文中的代码可重用性。
- No Exceptions (in the try/catch sense)(无异常(try/catch 意义上)):Go 使用显式的 error(错误)返回值,并使用
panic/recover机制处理真正异常的情况。
一个 Go 程序由一个或多个 .go 源文件组成。这些文件被组织到 package(包)中。一个可执行程序必须有一个 main package(包)和一个 main function(函数),它作为程序的 entry point(入口点)。
本教程假设您熟悉基本的编程概念以及如何编辑文本文件。
Go - 环境设置
Section titled “Go - 环境设置”设置 Go 开发环境非常简单。您主要需要 Go 发行版(包括 compiler(编译器)和标准库)以及一个文本编辑器或 IDE。
在线 Go Playground
Section titled “在线 Go Playground”无需本地安装即可快速试用 Go 代码,您可以使用Go Playground。本教程中的许多示例可以直接在那里运行。
本地环境设置
Section titled “本地环境设置”- 下载 Go:访问 Go 官方下载页面(go.dev/dl/)并下载适用于您的操作系统的安装程序或存档(Windows、macOS、Linux)。按照网站上提供的安装说明进行操作。这通常涉及在系统 PATH 中设置 Go 可执行文件。
- 验证安装:打开终端或命令提示符并输入
go version。您应该会看到已安装的 Go 版本。 - 文本编辑器/IDE:选择一个文本编辑器或集成开发环境(IDE)。流行的选择包括:
- Visual Studio Code (VS Code),安装 Go 扩展。
- GoLand(JetBrains 的商业 IDE)。
- Vim、Emacs、Sublime Text,安装相应的 Go 插件。
- Go Workspace 和 Modules:传统上,Go 使用
GOPATH环境变量来定义工作区。然而,自 Go 1.11 起,Go Modules 已成为管理 dependencies(依赖)和项目结构的 standard(标准)。Modules 允许您在GOPATH之外组织项目。 要使用 Module 启动一个新项目:这会创建一个Terminal window mkdir myprojectcd myprojectgo mod init myprojectname # 例如:go mod init github.com/yourusername/myprojectgo.mod文件,用于跟踪项目的 dependencies(依赖)。
编写您的第一个 Go 程序(Hello, World!)
Section titled “编写您的第一个 Go 程序(Hello, World!)”- 在您的项目目录中创建一个名为
hello.go的文件(例如,myproject/hello.go)。 - 添加以下代码:
package main
import "fmt"
func main() { fmt.Println("Hello, World!")}- 在
myproject目录内的终端中运行程序:
go run hello.go您应该会看到输出:
Hello, World!其他有用的 Go 命令包括:
go build:编译您的 package(包)和 dependencies(依赖)。go install:编译并安装 package(包)或 command(命令)。go test:运行 tests(测试)。
Go - 程序结构
Section titled “Go - 程序结构”在深入更复杂的主题之前,理解 Go 程序的基本结构至关重要。让我们分解一个最小的 Go 程序。
回顾 Go Hello World 示例
Section titled “回顾 Go Hello World 示例”一个 Go 程序通常包含以下部分:
- Package Declaration(包声明):每个 Go 程序都以
package声明开始。Package(包)是 Go 组织和重用代码的方式。 - Import Packages(导入包):
import语句用于包含来自其他 package(包)的代码。 - Functions(函数):代码执行被组织到 function(函数)中。
mainfunction(函数)是可执行程序的 entry point(入口点)。 - Variables(变量):用于存储数据。
- Statements & Expressions(语句和表达式):执行操作和计算。
- Comments(注释):被 compiler(编译器)忽略,用于提供人类可读的解释。
考虑一下“Hello, World!”程序:
package main // 1. Package Declaration
import "fmt" // 2. Import Package
// 3. Main function - program entry pointfunc main() { // This is a single-line comment /* This is a multi-line comment. */ fmt.Println("Hello, World!") // 4. Statement & Expression (calling a function)}让我们逐一 بررسی(检查)每个部分:
package main:声明此文件属于mainpackage(包)。mainpackage(包)很特殊;它定义了一个独立的 executable program(可执行程序),而不是一个 library(库)。此 package(包)中的mainfunction(函数)是执行开始的地方。import “fmt”:导入fmtpackage(包)(format 的缩写)。此 package(包)提供了用于 formatted I/O(格式化输入/输出)的 function(函数),类似于 C 的printf和scanf。您可以使用import (“pkg1”; “pkg2”)或import ( “pkg1” “pkg2” )导入多个 package(包)。func main() { … }:定义mainfunction(函数)。这是程序执行开始的地方。// …和/… /**:这些是 comments(注释)。单行注释以//开头,多行注释包含在/和/之间。Comments(注释)不能嵌套。fmt.Println(“Hello, World!”):此行调用fmtpackage(包)中的Printlnfunction(函数)。Println将其参数打印到标准输出,后跟一个换行符。在 Go 中,以大写字母开头的名称(例如fmtpackage(包)中的Println)是 **exported(导出)**的,这意味着它们对于导入此 package(包)的其他 package(包)是可见和可访问的。以小写字母开头的名称是 package(包)内的 private(私有)成员。
执行 Go 程序
Section titled “执行 Go 程序”要运行 Go 程序:
- 将代码保存为带有
.go扩展名的文件(例如,myprogram.go)。 - 打开终端或命令提示符。
- 导航到保存文件的目录。
- 使用命令
go run myprogram.go。
示例执行:$ go run hello.goHello, World!或者,您可以使用 go build hello.go 构建一个 executable binary(可执行二进制文件),然后运行编译后的程序(例如,在 Linux/macOS 上是 ./hello,在 Windows 上是 hello.exe)。
Go - 基本语法
Section titled “Go - 基本语法”现在您已经看到了一个基本的 Go 程序,让我们深入了解一些 fundamental(基本)的 syntax(语法)元素。
Go 中的 Token(词法单元)
Section titled “Go 中的 Token(词法单元)”Go 程序由 token(词法单元)组成。一个 token(词法单元)可以是 keyword(关键字)、identifier(标识符)、constant(常量)、string literal(字符串字面量)或 symbol(符号)。例如,语句:
fmt.Println("Hello, World!")由以下 token(词法单元)组成:
1. `fmt` (identifier - 标识符)2. `.` (symbol - 符号 - 选择器)3. `Println` (identifier - 标识符)4. `(` (symbol - 符号 - 开括号)5. `"Hello, World!"` (string literal - 字符串字面量)6. `)` (symbol - 符号 - 闭括号)Semicolons(分号)
Section titled “Semicolons(分号)”在 Go 中,semicolon(分号 ;)用于终止语句,但在您的源代码中它们大多是 可选 的。Go compiler(编译器)根据一些简单的规则自动在大多数行末尾插入分号。这意味着您通常不需要自己输入它们,这使得代码更整洁。
您可能会看到分号的一个地方是在单行上编写多个简短语句时,例如:a := 1; b := 2。
Comments(注释)
Section titled “Comments(注释)”Comments(注释)被 compiler(编译器)忽略,用于为代码添加解释。
- Single-line comments(单行注释)以
//开头:// 这是注释 - Multi-line comments(多行注释)包含在
/和/之间:
/*这是多行注释。多行注释。*/Comments(注释)不能嵌套。
Identifiers(标识符)
Section titled “Identifiers(标识符)”An identifier(标识符)是用于标识 variables(变量)、functions(函数)、types(类型)、constants(常量)等的名称。Identifiers(标识符)必须以字母(A-Z,a-z)或下划线(_)开头,后跟零个或多个字母、digits(数字,0-9)或下划线。
有效 identifiers(标识符)的示例:myVar、CalculateValue、_temp、user_id、PI。
Go 是 case-sensitive(区分大小写)的,因此 myVar 和 MyVar 是不同的 identifiers(标识符)。
Exported vs. Unexported Identifiers(导出标识符与未导出标识符):如果一个 identifier(标识符)以大写字母开头,则它是 **exported(导出)**的,这意味着它对于导入此 package(包)的其他 package(包)是可见和可访问的。如果以小写字母开头,则它是 **unexported(未导出)**的(private - 私有),只能在其自己的 package(包)内部访问。
Keywords(关键字)
Section titled “Keywords(关键字)”以下是 Go 中的 reserved keywords(保留关键字),不能用作 identifiers(标识符):
| 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 |
Go 中的 Whitespace(空白符)
Section titled “Go 中的 Whitespace(空白符)”Whitespace(空白符,包括空格、制表符、换行符)用于分隔 Go 中的 token(词法单元)。空行被 compiler(编译器)忽略。虽然 Go 对 whitespace(空白符)很灵活,但 idiomatic(地道)的 Go 代码通常使用 gofmt 工具进行格式化,该工具强制执行 standard(标准)风格。
例如,在 var age int 中,var、age 和 int 之间需要 whitespace(空白符)。然而,在 x = y + z 中,= 和 + 周围的 whitespace(空白符)是为了 readability(可读性),compiler(编译器)并非严格要求(尽管强烈推荐并由 gofmt 强制执行)。
Go - 数据类型
Section titled “Go - 数据类型”Go 中的 data type(数据类型)指定 variable(变量)可以持有的数据的类型、占用的内存量以及如何解释存储的位模式。Go 是一种 statically-typed language(静态类型语言),这意味着 variable(变量)的 types(类型)在 compile time(编译时)进行检查。
Go 的 types(类型)可以大致分为:
| 类别 | 类型和描述 |
|---|---|
| Basic Types(基本类型) | 这些是 fundamental(基本)的构建块: - Boolean(布尔型): bool(预定义常量 true 和 false)。- Numeric(数值型):Integers(整数)、floating-point numbers(浮点数)和 complex numbers(复数)。 - String(字符串): string(一个字节序列,通常表示 UTF-8 编码的文本。Strings 是 immutable(不可变)的)。 |
| Composite Types(复合类型) | 这些类型由 basic types(基本类型)或其他 composite types(复合类型)构建而成: - Array(数组):固定大小的同类型元素序列。 - Slice(切片):动态大小、灵活地查看 underlying array(底层数组)。 - Struct(结构体):将可能不同类型的 fields(字段)(variables - 变量)分组在一起的集合。 - Pointer(指针):保存 variable(变量)的 memory address(内存地址)。 - Function(函数):可调用的一段代码。 - Interface(接口):定义一组 method signature(方法签名)。一个 type(类型)通过实现其 methods(方法)来 implements(实现)一个 interface(接口)。 - Map(映射):无序的 key-value pair(键值对)集合。 - Channel(通道):用于 goroutine(并发执行单元)之间的 communication(通信)。 |
Boolean Types(布尔类型)
Section titled “Boolean Types(布尔类型)”bool type(类型)表示一个 boolean value(布尔值),它可以是 true 或 false。
Numeric Types(数值类型)
Section titled “Numeric Types(数值类型)”Integer Types(整数类型): Go 提供了各种大小的 signed(有符号)和 unsigned(无符号)整数。
Floating-Point Types(浮点类型):
Complex Types(复数类型):
String Types(字符串类型)
Section titled “String Types(字符串类型)”string type(类型)表示一个 byte sequence(字节序列)。Go 中的 Strings 是 **immutable(不可变)**的,这意味着一旦创建,其内容就不能更改。String literal(字符串字面量)可以使用 double quotes(双引号)”…”(interpreted - 解释的,允许 escapes - 转义,如 \n)或 backticks(反引号)(raw string literals - 原始字符串字面量,不处理 escapes - 转义,可以跨越多行)创建。...
Derived/Composite Types 简述(详情见后续章节)
Section titled “Derived/Composite Types 简述(详情见后续章节)”uintptr:一个无符号整数类型,其大小足以存储 pointer value(指针值)的未解释位。主要与unsafepackage(包)一起使用。
为您的 variables(变量)选择 appropriate(适当的) data type(数据类型)对于编写高效且正确的 Go 程序至关重要。type(类型)的选择会影响 memory usage(内存使用)、performance(性能)以及可以执行的操作。
Go - 变量
Section titled “Go - 变量”一个 variable(变量)是一个命名的存储位置,程序可以操作它。Go 中的每个 variable(变量)都有一个 specific(特定)的 type(类型),这决定了 variable(变量) memory(内存)的大小和布局、可以存储的值的 range(范围)以及可以应用的操作集合。
Variable Declaration(变量声明)
Section titled “Variable Declaration(变量声明)”Variables(变量)在使用之前必须先声明。Go 提供了几种声明 variables(变量)的方法。
使用 var 关键字:
var 关键字声明一个或多个 variables(变量)。type(类型)在 variable name(s)(变量名)之后指定。
// Declare a single variablevar age int
// Declare multiple variables of the same typevar x, y float64
// Declare variables of different types (using a block)var ( name string isEnabled bool)在 Declaration(声明)期间 Initialization(初始化): Variables(变量)可以在声明时进行 initialization(初始化)。
var message string = "Hello, Go!"var count int = 10var pi float64 = 3.14159
// If an initializer is present, the type can often be omitted (type inference)var inferredMessage = "Hello again!" // type string is inferred (类型 string 被推断)var inferredCount = 20 // type int is inferred (类型 int 被推断)Short Variable Declaration(短变量声明) (:=)
在 function(函数)内部,:= short assignment statement(短赋值语句)可用于声明和初始化 local variables(局部变量)。type(类型)始终从 right-hand side(右侧)的值推断出来。这种形式不能用于 package-level(包级别)的 variables(变量)。
func main() { // Short variable declaration (短变量声明) city := "New York" // type string inferred (类型 string 被推断) population := 8400000 // type int inferred (类型 int 被推断) isValid := true // type bool inferred (类型 bool 被推断)
// Multiple short variable declarations (多个短变量声明) width, height := 100, 50.5 // width is int, height is float64 (width 是 int,height 是 float64)
fmt.Println(city, population, isValid, width, height)}Zero Values(零值)
Section titled “Zero Values(零值)”没有显式初始值声明的 variables(变量)被赋予其 zero value(零值)。zero value(零值)是:
0对于 numeric types(数值类型)(integers - 整数、floats - 浮点数、complex numbers - 复数)false对于 boolean types(布尔类型)""(the empty string - 空字符串) 对于 string types(字符串类型)nil对于 pointers(指针)、functions(函数)、interfaces(接口)、slices(切片)、channels(通道)和 maps(映射)。
package main
import "fmt"
func main() { var i int var f float64 var b bool var s string var p *int // pointer (指针)
fmt.Printf("int: %d\n", i) // Output: int: 0 (输出:int: 0) fmt.Printf("float64: %f\n", f) // Output: float64: 0.000000 (输出:float64: 0.000000) fmt.Printf("bool: %t\n", b) // Output: bool: false (输出:bool: false) fmt.Printf("string: '%s'\n", s) // Output: string: '' (输出:string: '') fmt.Printf("pointer: %v\n", p) // Output: pointer: <nil> (输出:pointer: <nil>)}Scope of Variables(变量的作用域)
Section titled “Scope of Variables(变量的作用域)”variable(变量)的 scope(作用域)定义了 identifier(标识符)在程序中可以访问的区域。
- Package-level variables(包级变量)(也称为 global variables - 全局变量)在任何 function(函数)外部声明,在整个 package(包)中都可访问,如果其名称是 exported(导出)的(以大写字母开头),则可能可以从其他 package(包)中访问。
- **Local variables(局部变量)**在 function(函数)或 block(块)内部声明(例如,在
if语句或for循环内部),并且只能在该 function(函数)或 block(块)内部访问。
Lvalues and Rvalues (Conceptual)(左值和右值(概念))
Section titled “Lvalues and Rvalues (Conceptual)(左值和右值(概念))”虽然 Go 的 specification(规范)并不 heavily(大量)使用这些源自 C 的术语,但这些概念是 relevant(相关)的:
- lvalue (locator value - 定位符值):表示一个 memory location(内存位置)。Variables(变量)是 lvalues;它们可以出现在 assignment(赋值)的 left-hand side(左侧)。
- rvalue (read value - 读取值):表示一个 data value(数据值)。Literals(字面量,例如
10或“hello”)是 rvalues;它们不能被 assigned to(赋值)并且只能出现在 assignment(赋值)的 right-hand side(右侧)。
x = 20 是 valid(有效)的(x 是一个 lvalue)。
10 = y 是 invalid(无效)的(10 是一个 rvalue)。
Go - 常量
Section titled “Go - 常量”Constant(常量)是 fixed values(固定值),程序在执行期间不能改变它们。当直接写在代码中时,它们也称为 literals(字面量)。在 Go 中,constant(常量)使用 const keyword(关键字)声明。
Constant Declaration(常量声明)
Section titled “Constant Declaration(常量声明)”Constant(常量)可以是 character(字符)、string(字符串)、boolean(布尔)或 numeric values(数值)。
const Pi float64 = 3.14159265359const Greeting string = "Hello, Go!"const IsEnabled bool = true
// Constants can be grouped (常量可以分组)const ( StatusOK = 200 StatusError = 500)
// Type can be omitted for untyped constants (more flexible) (对于无类型常量,类型可以省略(更灵活))const Version = "1.0.2" // Untyped string constant (无类型字符串常量)const Year = 2023 // Untyped integer constant (无类型整数常量)Typed vs. Untyped Constants(有类型常量与无类型常量)
Section titled “Typed vs. Untyped Constants(有类型常量与无类型常量)”Go 中的 Constant(常量)可以是 typed(有类型) 或 untyped(无类型)。
- Typed constants(有类型常量) 行为类似于其声明类型的 variable(变量):
const MyInt int = 10。 - Untyped constants(无类型常量) 有一个 kind(类型,例如 boolean - 布尔、rune - rune、integer - 整数、floating-point - 浮点、complex - 复数、string - 字符串),但在需要特定类型上下文之前没有 fixed type(固定类型)。Untyped constants(无类型常量)提供了更多 flexibility(灵活性),因为它们可以在不同 compatible(兼容)类型的 variables(变量)中使用,无需 explicit conversion(显式转换)。Numeric constants(数值常量)尤其强大,因为它们在被 assigned(赋值)或用于 typed context(有类型上下文)之前支持 arbitrary precision(任意精度)。
const UntypedInt = 100 // Kind: integer (种类:整数)const UntypedFloat = 3.14 // Kind: floating-point (种类:浮点)
var myFloat32 float32 = UntypedFloat // OK, UntypedFloat converts to float32 (OK,UntypedFloat 转换为 float32)var myInt int = UntypedInt // OK, UntypedInt converts to int (OK,UntypedInt 转换为 int)// var myByte byte = UntypedInt // This would be OK if 100 fits in a byte (如果 100 Fits 在 byte 中,这将是 OK 的)// var myComplex complex64 = UntypedFloat // OK
const TypedInt int = 100// var myFloat64 float64 = TypedInt // Error: cannot use TypedInt (type int) as type float64 in variable declaration (错误:在变量声明中不能将 TypedInt(类型 int)用作类型 float64)Numeric Constants(数值常量)
Section titled “Numeric Constants(数值常量)”Integer literal(整数字面量)可以是 decimal(十进制)、octal(八进制,前缀 0o 或 0O)、hexadecimal(十六进制,前缀 0x 或 0X)或 binary(二进制,前缀 0b 或 0B)。
Floating-point literals(浮点字面量)有一个 integer part(整数部分)、一个 decimal point(小数点)、一个 fractional part(小数部分)和一个可选的 exponent part(指数部分)(e 或 E)。
const DecimalVal = 99const OctalVal = 0o17 // 15 in decimal (十进制为 15)const HexVal = 0xFF // 255 in decimal (十进制为 255)const BinaryVal = 0b1010 // 10 in decimal (十进制为 10)
const FloatVal = 3.14159const ExpVal = 6.022e23String Literals and Rune Literals(字符串字面量和 Rune 字面量)
Section titled “String Literals and Rune Literals(字符串字面量和 Rune 字面量)”String literals(字符串字面量) 是包含在 double quotes(双引号 ”…”)或 backticks(反引号 )中的 character sequence(字符序列)。...
- Double-quoted strings(双引号字符串)是 interpreted(解释的):escape sequences(转义序列)如
\n(换行)、\t(制表符)、“(双引号)会被处理。 - Backtick-quoted strings(反引号字符串)是 raw(原始的):不处理 escape sequences(转义序列),并且可以跨越多行。
Rune literals(Rune 字面量) 表示一个 single character(单个字符)(Unicode code point - 码点),并包含在 single quotes(单引号 ’…’)中(例如,‘a’、‘\n’、‘\u03A9’)。Rune literal(Rune 字面量)的类型是 rune(int32 的 alias - 别名)。
const SingleLine = "Hello\nWorld"const MultiLine = `这是一个原始字符串字面量。它保留格式。`
const MyRune = 'G'const NewlineRune = '\n'iota Constant Generator(常量生成器)
Section titled “iota Constant Generator(常量生成器)”iota 是 constant declaration(常量声明)中用于创建相关 values(值)序列的 special identifier(特殊标识符)。它简化了 enumerated constants(枚举常量)的定义。iota 在每个 const block(块)中从 0 开始,并为每个 subsequent constant specification(后续常量规范)递增 1。
package main
import "fmt"
const ( Red = iota // Red = 0 Green // Green = 1 (iota increments - iota 递增) Blue // Blue = 2 (iota increments - iota 递增))
const ( Read = 1 << iota // Read = 1 (1 << 0) Write // Write = 2 (1 << 1) Execute // Execute = 4 (1 << 2))
func main() { fmt.Println(Red, Green, Blue) // Output: 0 1 2 (输出: 0 1 2) fmt.Println(Read, Write, Execute) // Output: 1 2 4 (输出: 1 2 4)}Go - 运算符
Section titled “Go - 运算符”An operator(运算符)是一个 symbol(符号),它告诉 compiler(编译器)执行 specific(特定)的 mathematical(数学)、relational(关系)、logical(逻辑)或 bitwise(位)操作。Go 支持丰富的 operators(运算符)集合。
- Arithmetic Operators(算术运算符)
- Relational Operators(关系运算符)
- Logical Operators(逻辑运算符)
- Bitwise Operators(位运算符)
- Assignment Operators(赋值运算符)
- Miscellaneous Operators(其他运算符)(例如,address - 地址、dereference - 解引用、channel operators - 通道运算符)
Arithmetic Operators(算术运算符)
Section titled “Arithmetic Operators(算术运算符)”假设 A = 10 且 B = 20:
| 运算符 | 描述 | 示例 |
|---|---|---|
| + | 加法 | A + B 将得到 30 |
| - | 减法 | B - A 将得到 10 |
| * | 乘法 | A * B 将得到 200 |
| / | 除法 | B / A 将得到 2。对于 integers(整数),除法向零截断。 |
| % | 取模(余数) | B % A (20 % 10) 将得到 0。21 % 10 将得到 1。 |
Increment ++ and Decrement — Operators(自增 ++ 和自减 — 运算符):
在 Go 中,++ 和 — 是 statements(语句),而不是 expressions(表达式)。它们只能以 postfix form(后缀形式)使用(例如,A++),并且不 return value(返回值)。它们直接 modify(修改) variable(变量)。
var a = 10a++ // a becomes 11 (a 变为 11)fmt.Println(a) // Prints 11 (打印 11)// x = a++ // This is a compile-time error in Go (这在 Go 中是一个编译时错误)Relational Operators(关系运算符)
Section titled “Relational Operators(关系运算符)”假设 A = 10 且 B = 20:
| 运算符 | 描述 | 示例(结果) |
|---|---|---|
| == | 等于 | A == B 是 false |
| != | 不等于 | A != B 是 true |
| > | 大于 | A > B 是 false |
| < | 小于 | A < B 是 true |
| >= | 大于或等于 | A >= B 是 false |
| <= | 小于或等于 | A <= B 是 true |
Logical Operators(逻辑运算符)
Section titled “Logical Operators(逻辑运算符)”假设 X = true 且 Y = false:
| 运算符 | 描述 | 示例(结果) |
|---|---|---|
| && | 逻辑与 | X && Y 是 false。如果左操作数为 false,则右操作数不 evaluated(求值)(short-circuiting - 短路评估)。 |
| || | 逻辑或 | X || Y 是 true。如果左操作数为 true,则右操作数不 evaluated(求值)(short-circuiting - 短路评估)。 |
| ! | 逻辑非 | !X 是 false。 |
Bitwise Operators(位运算符)
Section titled “Bitwise Operators(位运算符)”Bitwise operators(位运算符)在 binary(二进制) level(级别)(bit - 位)上对 integers(整数)进行操作。假设 A = 60 (00111100) 且 B = 13 (00001101):
| 运算符 | 描述 | 示例(二进制 -> 十进制) |
|---|---|---|
| & | 按位与 | A & B (00001100) -> 12 |
| | | 按位或 | A | B (00111101) -> 61 |
| ^ | 按位异或 | A ^ B (00110001) -> 49 |
| &^ | 位清零(与非) | A &^ B (00110000) -> 48。清除 A 中在 B 中设置的位。 |
| << | 左移 | A << 2 (11110000) -> 240。将 A 的位向左移动 2 个位置。 |
| >> | 右移 | A >> 2 (00001111) -> 15。将 A 的位向右移动 2 个位置。对于 signed negative numbers(带符号的负数),它是 arithmetic shift(算术移位)。 |
Unary(一元)^ operator(运算符)也是 bitwise NOT(按位非)(complement - 补码):^A 反转 A 的所有 bits(位)。
Assignment Operators(赋值运算符)
Section titled “Assignment Operators(赋值运算符)”| 运算符 | 描述 | 示例 |
|---|---|---|
| = | 简单赋值 | C = A + B 将 A + B 的值 assigned(赋值)给 C |
| += | 加后赋值 | C += A 等同于 C = C + A |
| -= | 减后赋值 | C -= A 等同于 C = C - A |
| *= | 乘后赋值 | C *= A 等同于 C = C * A |
| /= | 除后赋值 | C /= A 等同于 C = C / A |
| %= | 取模后赋值 | C %= A 等同于 C = C % A |
| <<= | 左移后赋值 | C <<= 2 等同于 C = C << 2 |
| >>= | 右移后赋值 | C >>= 2 等同于 C = C >> 2 |
| &= | 按位与后赋值 | C &= 2 等同于 C = C & 2 |
| |= | 按位或后赋值 | C |= 2 等同于 C = C | 2 |
| ^= | 按位异或后赋值 | C ^= 2 等同于 C = C ^ 2 |
| &^= | 位清零后赋值 | C &^= 2 等同于 C = C &^ 2 |
Miscellaneous Operators(其他运算符)
Section titled “Miscellaneous Operators(其他运算符)”| 运算符 | 描述 | 示例 |
|---|---|---|
| & | 取地址运算符 | &a 返回 variable(变量)a 的 memory address(内存地址)(一个 pointer - 指针)。 |
| * | 解引用运算符(指针) | *p 访问 stored(存储)在 memory address(内存地址)p 处的 value(值)(其中 p 是一个 pointer - 指针)。 |
| <- | 通道运算符 | 用于在 channels(通道)上发送和接收 values(值)(例如,ch <- value 发送,value := <-ch 接收)。 |
Operator Precedence in Go(Go 中的运算符优先级)
Section titled “Operator Precedence in Go(Go 中的运算符优先级)”Operator precedence(运算符优先级)决定了 operation(操作)在 expression(表达式)中执行的顺序。与 C++ 或 Java 等语言相比,Go 具有 simpler(更简单)的 precedence hierarchy(优先级层次)。以下是优先级从高到低的 summary(摘要):
- Unary Operators(一元运算符):
+ - ! ^ & * <-(highest precedence - 最高优先级) - Multiplicative Operators(乘法运算符):
* / % << >> & &^ - Additive Operators(加法运算符):
+ - | ^ - Comparison Operators(比较运算符):
== != < <= > >= - Logical AND(逻辑与):
&& - Logical OR(逻辑或):
||(lowest precedence - 最低优先级)
例如,在 x = 7 + 3 * 2 中,multiplication(乘法 *)的 precedence(优先级)高于 addition(加法 +),因此先 evaluate(求值)3 * 2, resulting in(结果是)x 被 assigned(赋值)为 13 (7 + 6)。Parentheses(括号 ())可用于 override(覆盖) default precedence(默认优先级):x = (7 + 3) * 2 将 resulting in(结果是)x 被 assigned(赋值)为 20。
Operator Usage 示例(运算符使用示例)
Section titled “Operator Usage 示例(运算符使用示例)”package main
import "fmt"
func main() { a := 21 b := 10 var c int
c = a + b fmt.Printf("Line 1 - Value of c (a+b) is %d\n", c) // 31 (输出:第 1 行 - c 的值 (a+b) 是 31)
c = a - b fmt.Printf("Line 2 - Value of c (a-b) is %d\n", c) // 11 (输出:第 2 行 - c 的值 (a-b) 是 11)
c = a * b fmt.Printf("Line 3 - Value of c (a*b) is %d\n", c) // 210 (输出:第 3 行 - c 的值 (a*b) 是 210)
c = a / b fmt.Printf("Line 4 - Value of c (a/b) is %d\n", c) // 2 (integer division - 整数除法) (输出:第 4 行 - c 的值 (a/b) 是 2)
c = a % b fmt.Printf("Line 5 - Value of c (a%%b) is %d\n", c) // 1 (输出:第 5 行 - c 的值 (a%%b) 是 1)
a++ // a is now 22 (a 现在是 22) fmt.Printf("Line 6 - Value of a after a++ is %d\n", a) // (输出:第 6 行 - a 自增后 a 的值是 22)
b-- // b is now 9 (b 现在是 9) fmt.Printf("Line 7 - Value of b after b-- is %d\n", b) // (输出:第 7 行 - b 自减后 b 的值是 9)
// Relational (关系运算符) if a > b { fmt.Println("a is greater than b") // (输出:a 大于 b) }
// Logical (逻辑运算符) isValid := true isEnabled := false if isValid && !isEnabled { fmt.Println("isValid is true AND isEnabled is false") // (输出:isValid 为 true 并且 isEnabled 为 false) }}Go - 决策(Decision Making)
Section titled “Go - 决策(Decision Making)”Decision-making structures(决策结构)允许程序根据 certain conditions(某些条件)的 true 或 false 来执行不同的 blocks of code(代码块)。Go 为此提供了 several statements(几个语句)。
Go 的 decision-making statements(决策语句)包括:
| 语句 | 描述 |
|---|---|
if statement | 如果 condition(条件)为 true,则执行 block of code(代码块)。 |
if...else statement | 如果 condition(条件)为 true,执行一个 block of code(代码块);如果为 false,执行另一个 block(块)。 |
if...else if...else statement | 按顺序测试 multiple conditions(多个条件)。 |
switch statement | 将一个 expression(表达式)与 possible values(可能的值)列表(cases - 情况)进行比较或 evaluate types(评估类型)。 |
select statement | 专门用于 channels(通道)进行 concurrent operations(并发操作),等待多个 channel operations(通道操作)。 |
Common Feature: Initialization Statement(共同特性:初始化语句)
Section titled “Common Feature: Initialization Statement(共同特性:初始化语句)”Go 中的 if 和 switch statements(语句)都可以在 condition(条件)之前包含一个 optional short initialization statement(初始化语句)。在此语句中声明的 variables(变量)其 scope(作用域)限定在 if 或 switch statement(语句)的 blocks(块)内。
if statement(语句)
Section titled “if statement(语句)”Syntax(语法):
if optional_initialization_statement; condition { // block of code executed if condition is true (如果条件为 true,执行此代码块)}Example(示例):
package main
import "fmt"
func main() { a := 10
if a < 20 { fmt.Printf("a (%d) is less than 20\n", a) // (输出:a (10) 小于 20) } fmt.Println("Value of a is:", a) // (输出:a 的值是: 10)
// With initialization statement (带有初始化语句) if b := a * 2; b > 30 { fmt.Printf("b (%d) is greater than 30\n", b) // (输出:b (20) 不大于 30) } else { fmt.Printf("b (%d) is not greater than 30\n", b) // (输出:b (20) 不大于 30) } // fmt.Println(b) // Error: b is not defined here (scoped to if/else) (错误:b 未在此处定义(作用域限定在 if/else 块内))}if…else statement(语句)
Section titled “if…else statement(语句)”Syntax(语法):
if optional_initialization_statement; condition { // block for true condition (条件为 true 时执行的代码块)} else { // block for false condition (条件为 false 时执行的代码块)}if…else if…else statement(语句)
Section titled “if…else if…else statement(语句)”Syntax(语法):
if condition1 { // block for condition1 true (condition1 为 true 时执行的代码块)} else if condition2 { // block for condition2 true (condition2 为 true 时执行的代码块)} else if condition3 { // block for condition3 true (condition3 为 true 时执行的代码块)} else { // block if all preceding conditions are false (如果所有先前的条件都为 false,执行此代码块)}Example(示例):
package main
import "fmt"
func main() { score := 85 var grade string
if score >= 90 { grade = "A" } else if score >= 80 { grade = "B" } else if score >= 70 { grade = "C" } else { grade = "D" } fmt.Printf("Score: %d, Grade: %s\n", score, grade) // Score: 85, Grade: B (输出:分数: 85, 等级: B)}switch statement(语句)
Section titled “switch statement(语句)”Go 的 switch statement(语句)比许多 C-like languages(类似 C 的语言)更灵活。Cases(情况)不会自动 fall through(贯穿);需要 fallthrough statement(语句)才能实现该行为。switch 可以对 expressions(表达式)(expression switch - 表达式开关)或 types(类型)(type switch - 类型开关)进行操作。
Expression Switch Syntax(表达式开关语法):
switch optional_initialization_statement; optional_expression { case expression_case1, expression_case1_alternative: // code block (代码块) case expression_case2: // code block (代码块) fallthrough // optional: execute next case block too (可选:也执行下一个 case 块) default: // code block (optional) (代码块(可选))}如果 optional_expression 被 omitted(省略),它默认为 true,允许 case statements(语句)成为 boolean expressions(布尔表达式)(例如 if-else if-else 链)。
Example (Expression Switch)(示例(表达式开关)):
package main
import ( "fmt" "time")
func main() { today := time.Now().Weekday()
switch today { case time.Saturday, time.Sunday: fmt.Println("It's the weekend!") // (输出:是周末!) default: fmt.Println("It's a weekday.") // (输出:是工作日。) }
// Switch as if-else chain (将 switch 作为 if-else 链) num := 75 switch { case num < 0: fmt.Println("Number is negative") // (输出:数字是负数) case num > 0 && num < 100: fmt.Println("Number is positive and less than 100") // (输出:数字是正数且小于 100) default: fmt.Println("Number is 0 or 100 or more") // (输出:数字是 0 或 100 或更大) }}Type Switch Syntax(类型开关语法): A type switch(类型开关)用于 determine(确定)an interface value(接口值)的 dynamic type(动态类型)。
switch v := interface_variable.(type) { case Type1: // v is of Type1 (v 的类型是 Type1) case Type2, Type3: // v is of Type2 or Type3 (v 的类型是 Type2 或 Type3) default: // v is of some other type (v 的类型是其他类型)}Example (Type Switch)(示例(类型开关)):
package main
import "fmt"
func printType(i interface{}) { switch v := i.(type) { case int: fmt.Printf("Integer: %d\n", v) // (输出:整数: %d) case string: fmt.Printf("String: %s\n", v) // (输出:字符串: %s) default: fmt.Printf("Unknown type: %T, value: %v\n", v, v) // (输出:未知类型: %T, 值: %v) }}
func main() { printType(42) printType("hello") printType(true)}select statement(语句)
Section titled “select statement(语句)”select statement(语句)用于 wait on(等待)multiple channel operations(多个通道操作)。它会阻塞直到其中一个 cases(情况)可以运行,然后执行该 case(情况)。如果 multiple cases(多个情况)都 ready(准备好),它会 randomly(随机地)选择一个。
Syntax(语法):
select { case operation1: // e.g., value := <-channel1 or channel2 <- value (例如,value := <-通道1 或 通道2 <- value) // code block for operation1 (operation1 的代码块) case operation2: // code block for operation2 (operation2 的代码块) default: // code block if no other communication is ready (non-blocking select) (如果没有其他通信准备好,执行此代码块(非阻塞 select))}Example (select)(示例(select)):
package main
import ( "fmt" "time")
func main() { c1 := make(chan string) c2 := make(chan string)
go func() { time.Sleep(1 * time.Second) c1 <- "one" // (发送 "one" 到 c1) }() go func() { time.Sleep(2 * time.Second) c2 <- "two" // (发送 "two" 到 c2) }()
// Wait for messages from c1 or c2 for up to 3 seconds in total (总共等待 c1 或 c2 的消息最多 3 秒) for i := 0; i < 2; i++ { select { case msg1 := <-c1: fmt.Println("Received from c1:", msg1) // (输出:从 c1 接收到: one) case msg2 := <-c2: fmt.Println("Received from c2:", msg2) // (输出:从 c2 接收到: two) case <-time.After(3 * time.Second): // Timeout case (超时情况) fmt.Println("Timeout waiting for messages") // (输出:等待消息超时) return } }}此 example(示例)演示了 select statement(语句)等待两个 channels(通道),c1 和 c2 上的消息,并设置了 timeout(超时)。“Received from c1”和“Received from c2”的输出顺序可能因哪个 goroutine(协程)先发送消息而异。
Go - 循环
Section titled “Go - 循环”Loops(循环)用于 repetitively(重复地)执行 block of code(代码块)。Go 只有一种 looping construct(循环结构):for loop(循环)。然而,for loop(循环)非常 versatile(多功能),可以以类似于其他语言中的 while 和 infinite loops(无限循环)的方式使用,也可以用于 iterating over collections(迭代集合)。
for 循环
Section titled “for 循环”Go 的 for loop(循环)有几种 forms(形式):
- The complete
forloop (C-style)(完整的 for 循环(C 风格)):for initialization; condition; post_statement { … }initialization:Loop(循环)开始前执行一次。condition:每次 iteration(迭代)前 evaluated(求值)。只要为 true,loop(循环)就继续。post_statement:每次 iteration(迭代)后执行。
for i := 0; i < 5; i++ { fmt.Println(i) // Prints 0, 1, 2, 3, 4 (打印 0, 1, 2, 3, 4)}- The
whileloop equivalent(while 循环等效):for condition { … }initialization(初始化)和 post statements(语句)被 omitted(省略)。
n := 0for n < 5 { fmt.Println(n) n++} // Prints 0, 1, 2, 3, 4 (打印 0, 1, 2, 3, 4)- The infinite loop(无限循环):
for { … }condition(条件)被 omitted(省略),创建了一个 infinite loop(无限循环)。必须使用break或return退出。
sum := 0for { sum++ if sum > 10 { break // Exit the loop (退出循环) }} // sum becomes 11 (sum 变为 11)- The
for…rangeloop(for…range 循环): 用于 iterate over(迭代) data structures(数据结构)中的 elements(元素),如 slices(切片)、arrays(数组)、strings(字符串)、maps(映射)和 channels(通道)。for key, value := range collection { … }for index := range collection { … }(if only key/index is needed - 如果只需要 key/index)for _, value := range collection { … }(if only value is needed - 如果只需要 value)
// Iterating over a slice (迭代切片)nums := []int{10, 20, 30}for index, value := range nums { fmt.Printf("Index: %d, Value: %d\n", index, value) // (输出:索引: %d, 值: %d)}
// Iterating over a map (迭代 map)colors := map[string]string{"red": "#ff0000", "green": "#00ff00"}for key, value := range colors { fmt.Printf("Key: %s, Value: %s\n", key, value) // (输出:键: %s, 值: %s)}Loop Control Statements(循环控制语句)
Section titled “Loop Control Statements(循环控制语句)”Go 提供了 statements(语句)来 control the flow(控制流程) within loops(在循环内):
| 控制语句 | 描述 |
|---|---|
break | 立即 terminates(终止) innermost(最内层)的 for、switch 或 select statement(语句)。 |
continue | Skips(跳过) innermost(最内层)的 for loop(循环)当前 iteration(迭代)的 remainder(剩余部分),并 proceeds to(继续)下一次 iteration(迭代)(如果存在 post statement - 后续语句,则进行 evaluated - 求值)。 |
goto | Transfers control(转移控制)到 labeled statement(标记语句)。Its use is generally discouraged(通常不鼓励使用),因为它会使 code(代码)更难 read(阅读)和 maintain(维护)。 |
Example with break and continue(带有 break 和 continue 的示例):
package main
import "fmt"
func main() { fmt.Println("Using continue:") // (输出:使用 continue:) for i := 0; i < 5; i++ { if i == 2 { // Skip iteration when i is 2 (当 i 为 2 时跳过迭代) continue } fmt.Println(i) // Prints 0, 1, 3, 4 (打印 0, 1, 3, 4) }
fmt.Println("\nUsing break:") // (输出:使用 break:) sum := 0 for i := 1; i <= 10; i++ { if sum > 20 { break // Exit loop if sum exceeds 20 (如果 sum 超过 20,退出循环) } sum += i } fmt.Println("Sum:", sum) // Sum will be the smallest sum > 20 (e.g., 1+2+3+4+5+6=21) (Sum 将是 smallest sum > 20(例如,1+2+3+4+5+6=21)) (输出:总和: 21)}Nested Loops(嵌套循环)
Section titled “Nested Loops(嵌套循环)”您可以在另一个 for loop(循环)内部使用一个或多个 loops(循环)。
package main
import "fmt"
func main() { // Print a 3x3 grid (打印一个 3x3 的网格) for i := 0; i < 3; i++ { for j := 0; j < 3; j++ { fmt.Printf("(%d,%d) ", i, j) } fmt.Println() // Newline after each row (每行后换行) }}Output(输出):(0,0) (0,1) (0,2)(1,0) (1,1) (1,2)(2,0) (2,1) (2,2)Go - 函数
Section titled “Go - 函数”一个 function(函数)是一个 reusable block of code(可重用代码块),用于执行 specific task(特定任务)。每个 Go 程序至少有一个 function(函数):main(),它是 execution(执行)的 entry point(入口点)。Functions(函数)有助于组织 code(代码),使其更加 modular(模块化),并 improve readability(提高可读性)。
Defining a Function(定义函数)
Section titled “Defining a Function(定义函数)”在 Go 中定义 function(函数)的 general syntax(通用语法)是:
func functionName(parameter1 type1, parameter2 type2, ...) (returnType1, returnType2, ...) { // Function body: statements to perform the task (函数体:执行任务的语句) return value1, value2, ... // Optional return statement (可选的 return 语句)}Function definition(函数定义)的关键部分:
funckeyword(关键字):启动 function declaration(函数声明)。functionName:Function(函数)的名称。如果以大写字母开头,则为 exported(导出)的。- Parameters(参数):Function(函数)接受的 input values(输入值)列表。每个 parameter(参数)都有一个 name(名称)和一个 type(类型)。如果 consecutive parameters(连续参数)具有相同的 type(类型),则 type(类型)可以写在末尾一次:
(p1, p2 int, p3 string)。 - Return Types(返回值类型):Function(函数)返回的 values(值)的 types(类型)列表。如果 function(函数)返回 multiple values(多个值),则它们用 parentheses(括号)括起来。如果它什么也不 return(返回),则 return type list(返回值类型列表)被 omitted(省略)。
- Function Body(函数体):包含定义 function(函数)执行内容的 statements(语句)的 code block(代码块)。
returnstatement(语句):用于从 function(函数)中 return values(返回值)。如果 function(函数)定义了 return types(返回值类型),则它必须以returnstatement(语句)结束(除非它 panics 或有 infinite loop - 无限循环)。
Example: A simple max function(示例:一个简单的 max 函数)
// max returns the larger of two integers (max 返回两个整数中较大的那个)func max(num1, num2 int) int { var result int if num1 > num2 { result = num1 } else { result = num2 } return result}Calling a Function(调用函数)
Section titled “Calling a Function(调用函数)”要使用 function(函数),您需要通过其 name(名称)调用它,提供 required arguments(所需的实参)。
package main
import "fmt"
// max returns the larger of two integers (max 返回两个整数中较大的那个)func max(num1, num2 int) int { if num1 > num2 { return num1 } return num2}
func main() { a := 100 b := 200
maxValue := max(a, b) // Call the max function (调用 max 函数) fmt.Printf("Max value is: %d\n", maxValue) // Output: Max value is: 200 (输出:最大值是: 200)}Multiple Return Values(多返回值)
Section titled “Multiple Return Values(多返回值)”Go functions(函数)可以 return multiple values(返回多个值)。这通常用于 return a result(返回结果)和一个 error value(错误值)。
package main
import ( "fmt" "errors")
// divide returns the result of a/b and an error if b is zero (divide 返回 a/b 的结果,如果 b 为零则返回错误)func divide(a, b int) (int, error) { if b == 0 { return 0, errors.New("division by zero") // (返回错误:除以零) } return a / b, nil // nil indicates no error (nil 表示没有错误)}
func main() { result, err := divide(10, 2) if err != nil { fmt.Println("Error:", err) // (输出:错误: division by zero) } else { fmt.Println("10 / 2 =", result) // Output: 10 / 2 = 5 (输出:10 / 2 = 5) }
result, err = divide(10, 0) if err != nil { fmt.Println("Error:", err) // Output: Error: division by zero (输出:错误: division by zero) } else { fmt.Println("10 / 0 =", result) }}Named Return Values(命名返回值)
Section titled “Named Return Values(命名返回值)”Return values(返回值)可以在 function signature(函数签名)中命名。如果是这样,它们被视为 function(函数)顶部定义的 variables(变量)。一个没有 arguments(参数)的 return statement(语句)(a “bare” return - “裸”返回)然后 return(返回)命名返回值变量的当前 values(值)。
func calculate(a, b int) (sum int, difference int) { sum = a + b difference = a - b return // Bare return, returns sum and difference ("裸"返回,返回 sum 和 difference)}
// In main: (在 main 中:)// s, d := calculate(10, 5)// fmt.Println(s, d) // Output: 15 5 (输出:15 5)虽然此 feature(特性)存在,但 unless the function is very short(除非函数非常短),explicitly(显式地)使用 return statements(语句)通常更清晰。
Variadic Functions(可变参数函数)
Section titled “Variadic Functions(可变参数函数)”一个可以以 variable number of arguments(可变数量的实参)调用的 function(函数)是一个 variadic function(可变参数函数)。variadic function(可变参数函数)的最后一个 parameter(参数)在其 type(类型)前使用 … 前缀。在 function(函数)内部,此 parameter(参数)是该 type(类型)的 slice(切片)。
package main
import "fmt"
// sumAll calculates the sum of an arbitrary number of integers (sumAll 计算任意数量整数的总和)func sumAll(numbers ...int) int { total := 0 for _, num := range numbers { total += num } return total}
func main() { fmt.Println(sumAll(1, 2, 3)) // Output: 6 (输出:6) fmt.Println(sumAll(10, 20)) // Output: 30 (输出:30) fmt.Println(sumAll()) // Output: 0 (输出:0)
// If you have a slice, you can pass its elements as arguments using ... (如果你有一个切片,可以使用 ... 将其元素作为实参传递) nums := []int{5, 10, 15} fmt.Println(sumAll(nums...)) // Output: 30 (输出:30)}Call by Value vs. Call by Reference (Simulated)(值传递与引用传递(模拟))
Section titled “Call by Value vs. Call by Reference (Simulated)(值传递与引用传递(模拟))”Go always passes arguments to functions by value(总是将实参传递给函数 按值传递)。这意味着 function(函数)接收 argument(实参) value(值)的 copy(副本)。Modifying the parameter(修改参数) inside the function(在函数内部) does not affect(不影响) the original argument(原始实参)。
要允许 function(函数) modify an original variable(修改原始变量),您需要传递一个 pointer(指针) to that variable(指向该变量)。pointer(指针)本身是被 copied(复制)的(passed by value - 按值传递),但 copy(副本)指向 same memory location(相同的内存位置) as the original pointer(与原始指针相同), allowing modification(允许修改) of the data(数据) at that address(在该地址)。
package main
import "fmt"
// Fails to modify original 'val' because it operates on a copy (无法修改原始 'val',因为它操作的是副本)func noChange(val int) { val = 100}
// Modifies original 'val' by using a pointer (通过使用指针修改原始 'val')func doChange(valPtr *int) { *valPtr = 100 // Dereference the pointer to change the value (解引用指针以改变值)}
func main() { x := 10 noChange(x) fmt.Println("x after noChange:", x) // Output: x after noChange: 10 (输出:noChange 后 x: 10)
y := 20 doChange(&y) // Pass the address of y (传递 y 的地址) fmt.Println("y after doChange:", y) // Output: y after doChange: 100 (输出:doChange 后 y: 100)}Note(注意):Slices(切片)、maps(映射)和 channels(通道)是 reference types(引用类型)。当传递给 function(函数)时,descriptor(描述符)(包含指向 underlying data structure - 底层数据结构的 pointer - 指针)的 copy(副本)被创建。Modifications(修改)对 slice(切片)或 map(映射)的 elements(元素),或在 channel(通道)上发送/接收,对 caller(调用者)是 visible(可见)的。
Functions as Values and Closures(函数作为值和闭包)
Section titled “Functions as Values and Closures(函数作为值和闭包)”在 Go 中,functions(函数)是 first-class citizens(第一等公民)。它们可以 assigned to variables(赋值给变量)、passed as arguments(作为实参传递) to other functions(给其他函数)、and returned from other functions(以及从其他函数返回)。
An anonymous function(匿名函数) 是一个没有 name(名称)的 function(函数)。A closure(闭包) 是一个 anonymous function(匿名函数),它 accesses variables(访问变量) defined outside its own body(定义在其自身体外部)(它 “closes over” those variables - 它 “闭合” 这些变量)。
package main
import "fmt"
// makeGreeter returns a function that greets a specific name (makeGreeter 返回一个 function,用于问候特定名称)func makeGreeter(greeting string) func(string) string { return func(name string) string { return greeting + ", " + name + "!" // The inner function is a closure; it uses 'greeting' (内部函数是一个闭包;它使用了 'greeting') }}
func main() { // Functions as values (函数作为值) add := func(a, b int) int { return a + b } fmt.Println("Sum:", add(5, 3)) // Output: Sum: 8 (输出:总和: 8)
// Closures (闭包) englishGreet := makeGreeter("Hello") spanishGreet := makeGreeter("Hola")
fmt.Println(englishGreet("Alice")) // Output: Hello, Alice! (输出:Hello, Alice!) fmt.Println(spanishGreet("Bob")) // Output: Hola, Bob! (输出:Hola, Bob!)}Methods(方法)
Section titled “Methods(方法)”A method(方法)是一个 function(函数),它与 particular type(特定类型) associated(关联)。它 with a receiver(接收者) argument(实参) defined(定义),该 argument(实参)出现在 method name(方法名)之前。receiver(接收者)将 method(方法)连接到命名 type(类型)。
package main
import ( "fmt" "math")
type Circle struct { Radius float64}
// Area is a method with a value receiver of type Circle (Area 是一个带有类型 Circle 的值接收者的方法)func (c Circle) Area() float64 { return math.Pi * c.Radius * c.Radius}
// Scale is a method with a pointer receiver of type *Circle (Scale 是一个带有类型 *Circle 的指针接收者的方法)// Pointer receivers are used when the method needs to modify the receiver. (当方法需要修改接收者时使用指针接收者。)func (c *Circle) Scale(factor float64) { c.Radius *= factor}
func main() { myCircle := Circle{Radius: 5} fmt.Printf("Initial Area: %.2f\n", myCircle.Area()) // Initial Area: 78.54 (初始面积: 78.54)
myCircle.Scale(2) // Go automatically converts myCircle to &myCircle for pointer receivers (Go 会自动将 myCircle 转换为 &myCircle 以用于指针接收者) fmt.Printf("Radius after scaling: %.2f\n", myCircle.Radius) // Radius after scaling: 10.00 (缩放后半径: 10.00) fmt.Printf("Area after scaling: %.2f\n", myCircle.Area()) // Area after scaling: 314.16 (缩放后面积: 314.16)}Go - 作用域规则
Section titled “Go - 作用域规则”Scope(作用域)定义了 identifier(标识符)(如 variable - 变量或 function name - 函数名)在程序中 accessible(可访问)的区域。Go 使用 lexical (or static) scoping(词法(或静态)作用域),这意味着 identifier(标识符)的 scope(作用域)由其在 source code(源代码)中声明的位置决定。
Scope Levels(作用域级别)
Section titled “Scope Levels(作用域级别)”- Universe Block(宇宙块):包含所有 Go source text(源代码)。在此处声明的 identifiers(标识符)是 predeclared(预声明)的(例如,
true、false、nil、built-in types - 内置类型如int、string、built-in functions - 内置函数如make、len)。 - Package Block (Package Scope)(包块(包作用域)):在 package(包)中顶层(任何 function 外)声明的 identifiers(标识符)在该 package(包)中可见。如果其名称以大写字母开头,则它们是 exported(导出)的,并可从导入此 package(包)的其他 package(包)中可见。
- File Block(文件块):每个文件effectively(实际上)有其 own scope(自己的作用域)用于 imports(导入)(在一个文件中
import “fmt”并不能使fmt在同一 package(包)中的另一个文件中可用,除非该文件也有自己的 import)。 - Function Scope(函数作用域):在 function(函数)内部声明的 Parameters(参数)和 variables(变量)是该 function(函数)的 local(局部)变量,在 function 外部不可见。
- Block Scope(块作用域):在 control structure block(控制结构块)内部声明的 variables(变量)(例如,在
if、for、switch内部)其 scope(作用域)限定在该 block(块)内(包括与if相关联的任何else或else ifblocks - 块)。
Local Variables(局部变量)
Section titled “Local Variables(局部变量)”在 function(函数)或 block(块)内部声明的 variables(变量)是 local variables(局部变量)。它们只存在于该 function(函数)或 block(块)内部。
package main
import "fmt"
func main() { // a, b, c are local to main() (a, b, c 是 main() 的局部变量) var a, b int = 10, 20 c := a + b fmt.Printf("Inside main: a=%d, b=%d, c=%d\n", a, b, c) // (输出:在 main 内部: a=10, b=20, c=30)
if true { // d is local to this if block (d 是此 if 块的局部变量) d := 30 fmt.Printf("Inside if block: d=%d\n", d) // (输出:在 if 块内部: d=30) } // fmt.Println(d) // Error: d is not defined here (错误:d 未在此处定义)}Package-Level (Global) Variables(包级(全局)变量)
Section titled “Package-Level (Global) Variables(包级(全局)变量)”在任何 function(函数)外部声明的 variables(变量)是 package-level variables(包级变量)。它们可以从同一 package(包)中的任何 function(函数)访问。如果 exported(导出),它们可以从其他 package(包)访问。
package main
import "fmt"
var packageVar int = 100 // package-level variable (包级变量)
func printPackageVar() { fmt.Println("From printPackageVar: packageVar =", packageVar) // (输出:从 printPackageVar: packageVar = 100)}
func main() { fmt.Println("From main: packageVar =", packageVar) // Accessing package-level var (访问包级变量) (输出:从 main: packageVar = 100) printPackageVar()
packageVar = 200 // Modifying package-level var (修改包级变量) fmt.Println("From main after modification: packageVar =", packageVar) // (输出:修改后从 main: packageVar = 200)}Shadowing(遮蔽)
Section titled “Shadowing(遮蔽)”如果在一个 inner scope(内部作用域)中声明的 variable(变量)与 outer scope(外部作用域)中的 variable(变量)同名,则 inner variable(内部变量)会 shadow(遮蔽) outer one(外部变量)。在 inner scope(内部作用域)内,该名称指的是 inner variable(内部变量)。
package main
import "fmt"
var shadowedVar int = 10 // Package-level variable (包级变量)
func main() { fmt.Println("Outer scope (before shadowing): shadowedVar =", shadowedVar) // Prints 10 (打印 10)
shadowedVar := 20 // Local variable in main, shadows the package-level one (main 中的局部变量,遮蔽了包级变量) // Note: If we used `shadowedVar = 20` without `:=`, it would assign to the package-level var. (注意:如果我们使用 `shadowedVar = 20` 而不是 `:=`,它会给包级变量赋值。)
fmt.Println("Inner scope (after shadowing): shadowedVar =", shadowedVar) // Prints 20 (打印 20)
if true { shadowedVar := 30 // Further shadowing within the if block (if 块内的进一步遮蔽) fmt.Println("Innermost scope: shadowedVar =", shadowedVar) // Prints 30 (打印 30) }
fmt.Println("Back in main's scope: shadowedVar =", shadowedVar) // Prints 20 (打印 20) printPackageLevelVar()}
func printPackageLevelVar() { // This function always sees the package-level 'shadowedVar' (此函数始终看到包级的 'shadowedVar') fmt.Println("Function printPackageLevelVar: shadowedVar =", shadowedVar) // Prints 10 (or its modified value if changed at package level) (打印 10(如果包级被修改,则打印其修改后的值))}Generally(通常),最好避免 excessive shadowing(过度遮蔽),以保持 code(代码)清晰并 prevent unintended behavior(防止意外行为)。
Formal Parameters(形式参数)
Section titled “Formal Parameters(形式参数)”Function parameters(函数参数)在其 function(函数)的 scope(作用域)内被视为 local variables(局部变量)。它们可以 shadow package-level variables(遮蔽包级变量)。
package main
import "fmt"
var a int = 100 // Package-level 'a' (包级 'a')
func sum(a, b int) int { // 'a' here is a parameter, shadows package-level 'a' (这里的 'a' 是一个参数,遮蔽了包级的 'a') fmt.Printf("Inside sum(): a=%d, b=%d\n", a, b) // (输出:在 sum() 内部: a=10, b=20) return a + b}
func main() { var a int = 10 // Local 'a' in main, shadows package-level 'a' (main 中的局部 'a',遮蔽了包级的 'a') b := 20 fmt.Printf("Inside main(): a=%d, b=%d\n", a, b) // (输出:在 main() 内部: a=10, b=20)
result := sum(a, b) // Passes main's 'a' (10) and 'b' (20) to sum() (将 main 的 'a' (10) 和 'b' (20) 传递给 sum()) fmt.Printf("Result from sum: %d\n", result) // (输出:sum 的结果: 30) fmt.Printf("Package-level a (accessed via another func or if not shadowed): %d\n", getGlobalA()) // (输出:包级 a (通过另一个函数访问或未被遮蔽的情况下): 100)}
func getGlobalA() int { return a // Accesses package-level 'a' (访问包级 'a')}Zero Values and Initialization(零值和初始化)
Section titled “Zero Values and Initialization(零值和初始化)”未显式初始值声明的 variables(变量)会自动 initialized(初始化)为其 zero value(零值)(例如,numbers - 数字为 0,booleans - 布尔型为 false,strings - 字符串为 "",pointers/slices/maps 等为 nil)。这适用于 package-level(包级)和 local variables(局部变量)。
Go - 数组
Section titled “Go - 数组”Go 中的 array(数组)是一个 fixed-size(固定大小)、numbered sequence(有序序列),由 single, specific type(单个特定类型)的 elements(元素)组成。array(数组)的 size(大小)(length - 长度)是其 type(类型)的一部分。例如,[5]int 和 [10]int 是不同的 array types(数组类型)。
Arrays(数组)在 Go 中是 value types(值类型)。当一个 array(数组) assigned to(赋值给)另一个 variable(变量)或 passed to(传递给) function(函数)时,the entire array(整个数组)的 copy(副本)会被 created(创建)。
Declaring Arrays(声明数组)
Section titled “Declaring Arrays(声明数组)”Syntax(语法):var arrayName [size]elementType
// Declare an array of 5 integers (声明一个包含 5 个整数的数组)var numbers [5]int
// Declare an array of 3 strings (声明一个包含 3 个字符串的数组)var names [3]stringInitializing Arrays(初始化数组)
Section titled “Initializing Arrays(初始化数组)”Arrays(数组)可以使用 array literal(数组字面量)进行 initialization(初始化):
// Initialize with values (用值初始化)var primeNumbers = [5]int{2, 3, 5, 7, 11}
// Let the compiler count the elements using ... (使用 ... 让编译器计算元素数量)weekdays := [...]string{"Mon", "Tue", "Wed", "Thu", "Fri"} // type [5]string (类型 [5]string)
// Initialize specific elements (others get zero value) (初始化特定元素(其他元素获得零值))// Indices must be within bounds, order doesn't matter for sparse initialization (索引必须在范围内,稀疏初始化时顺序不重要)customInit := [5]int{0: 10, 4: 50} // [10, 0, 0, 0, 50]如果 array(数组)声明时没有 initialization(初始化),其 elements(元素)会自动设置为其 type(类型)的 zero value(零值)。
Accessing Array Elements(访问数组元素)
Section titled “Accessing Array Elements(访问数组元素)”Elements(元素)使用 zero-based index(零基索引)进行访问:arrayName[index]。
package main
import "fmt"
func main() { var scores [3]int // Elements initialized to 0 (元素初始化为 0)
scores[0] = 90 scores[1] = 85 scores[2] = 78 // scores[3] = 100 // Compile-time error: index out of bounds (编译时错误:索引超出范围)
fmt.Println("Score 1:", scores[0]) // (输出:分数 1: 90) fmt.Println("Score 2:", scores[1]) // (输出:分数 2: 85) fmt.Println("Score 3:", scores[2]) // (输出:分数 3: 78)
fmt.Println("All scores:", scores) // Prints [90 85 78] (打印 [90 85 78])
// Length of an array using len() (使用 len() 获取数组的长度) fmt.Println("Number of scores:", len(scores)) // Prints 3 (打印 3) (输出:分数数量: 3)
// Iterate using a for loop (使用 for 循环迭代) fmt.Println("Iterating scores:") // (输出:迭代分数:) for i := 0; i < len(scores); i++ { fmt.Printf("Score at index %d: %d\n", i, scores[i]) // (输出:索引 %d 处的分数: %d) }
// Iterate using for...range (使用 for...range 迭代) fmt.Println("Iterating scores with range:") // (输出:使用 range 迭代分数:) for index, value := range scores { fmt.Printf("Index: %d, Value: %d\n", index, value) // (输出:索引: %d, 值: %d) }}Multi-dimensional Arrays(多维数组)
Section titled “Multi-dimensional Arrays(多维数组)”Go supports multi-dimensional arrays(多维数组)。一个 common example(常见示例)是 2D array(二维数组)(matrix - 矩阵)。
package main
import "fmt"
func main() { // Declare and initialize a 2x3 integer array (2 rows, 3 columns) (声明并初始化一个 2x3 整数数组(2 行,3 列)) matrix := [2][3]int{ {1, 2, 3}, {4, 5, 6}, }
fmt.Println("Matrix:", matrix) // (输出:矩阵: [[1 2 3] [4 5 6]])
// Access an element (访问一个元素) fmt.Println("Element at [1][1]:", matrix[1][1]) // Output: 5 (输出:位于 [1][1] 的元素: 5)
// Iterate through the matrix (遍历矩阵) for i := 0; i < 2; i++ { for j := 0; j < 3; j++ { fmt.Printf("matrix[%d][%d] = %d\n", i, j, matrix[i][j]) } }}Passing Arrays to Functions(将数组传递给函数)
Section titled “Passing Arrays to Functions(将数组传递给函数)”当 array(数组) passed to(传递给) function(函数)时,a copy of the array(数组的副本) is passed(被传递)。Modifications(修改) to the array(数组) inside the function(在函数内部) do not affect(不影响) the original array(原始数组)。array(数组)的 size(大小)是其 type(类型)的一部分,因此 function(函数)期望接收 [5]int 不能 accept(接受) [10]int。
package main
import "fmt"
// modifyArray receives a copy of the array (modifyArray 接收数组的副本)func modifyArray(arr [3]int) { arr[0] = 100 // Modifies the copy, not the original (修改的是副本,而不是原始数组) fmt.Println("Inside modifyArray:", arr) // (输出:在 modifyArray 内部: [100 20 30])}
// To modify the original array, pass a pointer to it (要修改原始数组,传递指向它的指针)func modifyArrayByPointer(arrPtr *[3]int) { // (*arrPtr)[0] = 100 // Explicit dereference (显式解引用) // Go allows a shortcut for pointers to arrays/structs: (Go 允许对数组/结构体指针使用简写:) arrPtr[0] = 100 // Modifies the original array (修改原始数组) fmt.Println("Inside modifyArrayByPointer:", *arrPtr) // (输出:在 modifyArrayByPointer 内部: [100 20 30])}
func main() { originalArray := [3]int{10, 20, 30} fmt.Println("Original array before modifyArray:", originalArray) // (输出:调用 modifyArray 前的原始数组: [10 20 30]) modifyArray(originalArray) fmt.Println("Original array after modifyArray:", originalArray) // Unchanged (未改变) (输出:调用 modifyArray 后的原始数组: [10 20 30])
fmt.Println("\nOriginal array before modifyArrayByPointer:", originalArray) // (输出:调用 modifyArrayByPointer 前的原始数组: [10 20 30]) modifyArrayByPointer(&originalArray) // Pass a pointer (传递一个指针) fmt.Println("Original array after modifyArrayByPointer:", originalArray) // Changed (已改变) (输出:调用 modifyArrayByPointer 后的原始数组: [100 20 30])}Due to the copying behavior and fixed size, slices are often preferred over arrays in Go when you need dynamic sizing or want to avoid copying large amounts of data.
Go - 指针
Section titled “Go - 指针”A pointer(指针)是一个 variable(变量),它存储 another variable(另一个变量)的 memory address(内存地址)。Pointers(指针)在几个方面很有用,包括允许 functions(函数) modify their arguments(修改它们的实参)(call by reference simulation - 模拟引用传递)以及用于处理 require dynamic memory allocation(需要动态内存分配)或 linking(链接)的数据结构。
Understanding Memory Addresses(理解内存地址)
Section titled “Understanding Memory Addresses(理解内存地址)”Every variable(每个变量)在 memory(内存)中都有一个 address(地址)。& operator(运算符)(address-of operator - 取地址运算符)为您提供了 variable(变量)的 memory address(内存地址)。
package main
import "fmt"
func main() { x := 10 fmt.Printf("Value of x: %d\n", x) // (输出:x 的值: 10) fmt.Printf("Memory address of x: %p\n", &x) // %p formats as a hexadecimal address (%p 格式化为十六进制地址) (输出:x 的内存地址: 0x...)}Declaring Pointers(声明指针)
Section titled “Declaring Pointers(声明指针)”A pointer type(指针类型)由 *T 表示,其中 T 是 pointer(指针)指向的 variable(变量)的 type(类型)。pointer(指针)的 zero value(零值)是 nil。
var p *int // p is a pointer to an integer, currently nil (p 是一个指向整数的指针,当前为 nil)var sPtr *string // sPtr is a pointer to a string, currently nil (sPtr 是一个指向字符串的指针,当前为 nil)Using Pointers(使用指针)
Section titled “Using Pointers(使用指针)”- Assigning an address to a pointer(将地址赋值给指针):使用
&operator(运算符)。 - Dereferencing a pointer(解引用指针):使用
*operator(运算符)(dereference operator - 解引用运算符)访问 stored(存储)在 pointer(指针)持有的 address(地址)处的 value(值)。
package main
import "fmt"
func main() { a := 42 // An integer variable (一个整数变量) var ptrA *int // A pointer to an integer (一个指向整数的指针)
ptrA = &a // Store the address of 'a' in 'ptrA' (将 'a' 的地址存储到 'ptrA' 中)
fmt.Printf("Value of a: %d\n", a) // (输出:a 的值: 42) fmt.Printf("Address of a: %p\n", &a) // (输出:a 的地址: 0x...) fmt.Printf("Value of ptrA (the address it holds): %p\n", ptrA) // (输出:ptrA 的值 (它持有的地址): 0x...) fmt.Printf("Address of ptrA itself: %p\n", &ptrA) // (输出:ptrA 本身的地址: 0x...)
// Access the value 'a' points to using the pointer (使用指针访问 'a' 指向的值) fmt.Printf("Value at address stored in ptrA (*ptrA): %d\n", *ptrA) // (输出:存储在 ptrA 地址处的值 (*ptrA): 42)
// Modify the value of 'a' through the pointer (通过指针修改 'a' 的值) *ptrA = 100 fmt.Printf("New value of a (modified via ptrA): %d\n", a) // (输出:a 的新值 (通过 ptrA 修改): 100)}nil Pointers(nil 指针)
Section titled “nil Pointers(nil 指针)”A pointer(指针) that does not point to any valid memory location(不指向任何有效内存位置) is a nil pointer。Its value(它的值) is nil。Dereferencing a nil pointer(解引用一个 nil 指针) will cause a runtime panic(会导致运行时恐慌)。
package main
import "fmt"
func main() { var p *int fmt.Printf("Value of p: %v\n", p) // Output: Value of p: <nil> (输出:p 的值: <nil>)
if p != nil { fmt.Println("Value pointed to by p:", *p) // This won't run (这不会运行) } else { fmt.Println("p is a nil pointer") // (输出:p 是一个 nil 指针) }
// The following would cause a panic: (以下会导致一个 panic:) // *p = 10}Pointers to Structs(指向结构体的指针)
Section titled “Pointers to Structs(指向结构体的指针)”Pointers(指针) commonly used with structs(通常与结构体一起使用),尤其是在 passing large structs to functions(将大型结构体传递给函数)以 avoid copying(避免复制)时,或者 when methods need to modify the struct’s fields(当方法需要修改结构体的字段)时。
type Vertex struct { X, Y int}
v := Vertex{1, 2}pv := &v // pv is a pointer to Vertex v (pv 是指向 Vertex v 的指针)
// To access fields of a struct through a pointer, you can use (*pv).X (要通过指针访问结构体的字段,可以使用 (*pv).X)// Go provides a shorthand: pv.X automatically dereferences the pointer. (Go 提供了一个简写:pv.X 会自动解引用指针。)fmt.Println(pv.X) // Prints 1 (打印 1)pv.X = 100fmt.Println(v.X) // Prints 100 (v was modified) (打印 100 (v 已被修改))Array of Pointers(指针数组)
Section titled “Array of Pointers(指针数组)”You can create an array where each element is a pointer(您可以创建一个数组,其中每个元素都是一个指针)。
package main
import "fmt"
const MAX = 3
func main() { names := [MAX]string{"Alice", "Bob", "Charlie"} var ptrArray [MAX]*string
for i := 0; i < MAX; i++ { ptrArray[i] = &names[i] // Assign address of each string element (给每个字符串元素赋值地址) }
for i := 0; i < MAX; i++ { fmt.Printf("Value at ptrArray[%d]: %s (Address: %p)\n", i, *ptrArray[i], ptrArray[i]) // (输出:ptrArray[%d] 处的值: %s (地址: %p)) }
// Modify an original value through the pointer array (通过指针数组修改原始值) *ptrArray[0] = "Alicia" fmt.Println("Original names array after modification:", names) // [Alicia Bob Charlie] (输出:修改后的原始 names 数组: [Alicia Bob Charlie])}Pointer to Pointer (Double Pointer)(指针的指针(双指针))
Section titled “Pointer to Pointer (Double Pointer)(指针的指针(双指针))”A pointer(指针) can also point to another pointer(也可以指向另一个指针)。这在 idiomatic Go(地道的 Go)中不如 C/C++ 中常见,但 possible(可能)。
package main
import "fmt"
func main() { var num int = 123 var ptr1 *int // Pointer to int (指向 int 的指针) var ptr2 **int // Pointer to pointer to int (指向指向 int 的指针的指针)
ptr1 = &num ptr2 = &ptr1
fmt.Printf("Value of num = %d\n", num) // (输出:num 的值 = 123) fmt.Printf("Value available at *ptr1 = %d\n", *ptr1) // (输出:*ptr1 处的值 = 123) fmt.Printf("Value available at **ptr2 = %d\n", **ptr2) // (输出:**ptr2 处的值 = 123)}Passing Pointers to Functions(将指针传递给函数)
Section titled “Passing Pointers to Functions(将指针传递给函数)”As shown earlier(如前所示),passing a pointer to a function(将指针传递给函数) allows the function to modify the original variable’s value(允许函数修改原始变量的值)。这是 Go 实现 call-by-reference-like behavior(类似引用传递行为)的方式。
package main
import "fmt"
func swap(x, y *int) { temp := *x *x = *y *y = temp}
func main() { a := 10 b := 20 fmt.Printf("Before swap: a = %d, b = %d\n", a, b) // (输出:交换前: a = 10, b = 20) swap(&a, &b) fmt.Printf("After swap: a = %d, b = %d\n", a, b) // (输出:交换后: a = 20, b = 10)}The new() function(new() 函数)
Section titled “The new() function(new() 函数)”The built-in new() function(内置的 new() 函数)allocates memory(分配内存) for a type(用于某种类型),initializes it to its zero value(将其初始化为其零值),and returns a pointer to it(并返回指向它的指针)。new(T) returns(返回) a *T。
ptr := new(int) // ptr is *int, *ptr is 0 (ptr 是 *int 类型,*ptr 的值是 0)fmt.Println(*ptr) // 0 (输出 0)*ptr = 100fmt.Println(*ptr) // 100 (输出 100)While new() is available(虽然 new() 可用),make() is more commonly used(make() 更常用) for slices(切片)、maps(映射)和 channels(通道),因为它 initializes their internal structures(它初始化它们的内部结构),not just zeroing memory(而不仅仅是清零内存)。
Go - 结构体
Section titled “Go - 结构体”A structure (or struct)(结构体) in Go 是一个 user-defined composite type(用户定义的复合类型),它将零个或多个 potentially different types(可能不同类型)的 values(值)(fields - 字段)分组到 single name(单个名称)下。Structs(结构体)用于 represent records(表示记录)或 complex data entities(复杂数据实体)。
Defining a Structure(定义结构体)
Section titled “Defining a Structure(定义结构体)”您使用 type keyword(关键字)定义一个 struct(结构体),后跟 struct name(结构体名称)和 struct keyword(关键字),以及一个字段定义块。
type StructName struct { FieldName1 type1 FieldName2 type2 // ... FieldNameN typeN}
// Example: A Book struct (示例: 一个 Book 结构体)type Book struct { Title string Author string Subject string BookID int Pages int IsPublished bool}Creating and Initializing Structs(创建和初始化结构体)
Section titled “Creating and Initializing Structs(创建和初始化结构体)”一旦定义了 struct type(结构体类型),您可以创建该 type(类型)的 instances(实例)(variables - 变量)。
// 1. Declare then assign fields (fields get zero values initially) (先声明再赋值字段(字段最初获得零值))var book1 Bookbook1.Title = "Go Programming Essentials"book1.Author = "Tech Author"book1.BookID = 101
// 2. Using a struct literal (order of fields matters if names are omitted) (使用结构体字面量(如果省略名称,字段顺序很重要))book2 := Book{"Learning Go", "Jane Doe", "Programming", 102, 300, true}
// 3. Using a struct literal with field names (recommended for clarity) (使用带字段名称的结构体字面量(建议为了清晰))book3 := Book{ Title: "Advanced Go Patterns", Author: "John Smith", BookID: 103, Pages: 450, IsPublished: true, // Trailing comma is required if literal is multi-line (如果字面量是多行的,需要尾部逗号)}
// 4. Using the new() function (returns a pointer to a zeroed struct) (使用 new() 函数(返回指向零值结构体的指针))bookPtr := new(Book)bookPtr.Title = "Go for Experts" // Go automatically dereferences pointers to structs for field access (Go 会自动解引用结构体指针以访问字段)// equivalent to (*bookPtr).Title = ... (等同于 (*bookPtr).Title = ...)Accessing Struct Members(访问结构体成员)
Section titled “Accessing Struct Members(访问结构体成员)”You access the fields of a struct(您访问结构体的字段) using the dot . operator(使用点运算符)。
package main
import "fmt"
type Book struct { Title string Author string BookID int}
func main() { myBook := Book{ Title: "The Go Programming Language", Author: "Alan A. A. Donovan & Brian W. Kernighan", BookID: 789, }
fmt.Println("Book Title:", myBook.Title) // (输出:书名: The Go Programming Language) fmt.Println("Book Author:", myBook.Author) // (输出:作者: Alan A. A. Donovan & Brian W. Kernighan) fmt.Println("Book ID:", myBook.BookID) // (输出:图书 ID: 789)
// Modify a field (修改字段) myBook.BookID = 800 fmt.Println("Updated Book ID:", myBook.BookID) // (输出:更新后的图书 ID: 800)}Structs as Function Arguments(结构体作为函数实参)
Section titled “Structs as Function Arguments(结构体作为函数实参)”Structs(结构体),like other types(像其他类型一样),can be passed to functions(可以传递给函数)。By default(默认情况下),structs(结构体) are passed by value(按值传递)(a copy is made - 创建一个副本)。To modify the original struct within a function(要在函数内部修改原始结构体),pass a pointer to the struct(传递指向该结构体的指针)。
package main
import "fmt"
type Book struct { Title string Author string BookID int}
// printBook accepts a Book value (a copy) (printBook 接受一个 Book 值(一个副本))func printBook(b Book) { fmt.Printf("Title: %s, Author: %s, ID: %d\n", b.Title, b.Author, b.BookID) b.BookID = 0 // Modifies the copy, not the original (修改的是副本,而不是原始结构体)}
// printBookByPointer accepts a pointer to a Book (printBookByPointer 接受一个指向 Book 的指针)func printBookByPointer(b *Book) { fmt.Printf("Title: %s, Author: %s, ID: %d\n", b.Title, b.Author, b.BookID) b.BookID = 0 // Modifies the original struct via the pointer (通过指针修改原始结构体)}
func main() { book1 := Book{"Go Recipes", "Various Authors", 201} fmt.Println("Before calling printBook:", book1.BookID) // (输出:调用 printBook 前: 201) printBook(book1) fmt.Println("After calling printBook:", book1.BookID) // Remains 201 (仍为 201) (输出:调用 printBook 后: 201)
book2 := Book{"Effective Go", "The Go Team", 202} fmt.Println("\nBefore calling printBookByPointer:", book2.BookID) // (输出:调用 printBookByPointer 前: 202) printBookByPointer(&book2) fmt.Println("After calling printBookByPointer:", book2.BookID) // Changes to 0 (变为 0) (输出:调用 printBookByPointer 后: 0)}Anonymous Structs and Fields(匿名结构体和字段)
Section titled “Anonymous Structs and Fields(匿名结构体和字段)”Anonymous Structs(匿名结构体):Structs(结构体)可以 inline(内联)定义 without a name(没有名称),通常用于 temporary data structures(临时数据结构)。
Anonymous Fields (Embedding)(匿名字段(嵌入)):Struct(结构体)可以 without explicit names(没有显式名称)的 fields(字段),仅 specify the type(指定类型)。这称为 embedding(嵌入)。Embedded fields’ names(嵌入字段的名称) implicitly(隐式地)是 their type names(它们的类型名称)。Embedded types(嵌入类型)的 Methods(方法)被 promoted(提升)到 embedding struct(嵌入结构体),这是 Go 实现 composition(组合)的一种关键方式。
type Person struct { Name string Age int}
type Employee struct { Person // Embedded Person struct (anonymous field) (嵌入 Person 结构体(匿名字段)) EmployeeID string Salary float64}
func main() { emp := Employee{ Person: Person{Name: "Alice", Age: 30}, EmployeeID: "E123", Salary: 75000, } fmt.Println(emp.Name) // Access embedded field directly: Alice (直接访问嵌入字段: Alice) fmt.Println(emp.Person.Age) // Or access via type name: 30 (或者通过类型名称访问: 30)}Go - 切片
Section titled “Go - 切片”A slice(切片)是 Go 中处理 data sequences(数据序列)的一种 powerful(强大)、flexible(灵活)和 convenient(方便)的方式。Slices(切片)是对 underlying array(底层数组) 的 elements(元素)的 dynamically-sized(动态大小)、flexible view(灵活视图)。与 arrays(数组)不同,slice(切片)的 length(长度)可以改变。Slices(切片)在 idiomatic Go(地道的 Go)中比 arrays(数组)更常见。
Internally(内部),a slice(切片) is a small descriptor struct containing three fields(是一个包含三个字段的小型描述符结构体):
- A pointer to the underlying array.(指向底层数组的指针)
- The length of the segment of the array that the slice contains.(切片包含的数组片段的长度)
- The capacity of the segment (maximum length it can reach by re-slicing).(片段的容量(通过重新切片可以达到的最大长度))
Defining and Initializing Slices(定义和初始化切片)
Section titled “Defining and Initializing Slices(定义和初始化切片)”Slices(切片)可以用几种方式创建:
// 1. Using a slice literal (similar to array literal, but without size) (使用切片字面量(类似于数组字面量,但没有大小))numbers := []int{10, 20, 30, 40, 50} // Creates an array and a slice that views it (创建一个数组和一个查看它的切片)
// 2. Using the make() function (使用 make() 函数)// make([]T, length, capacity) - capacity is optional, defaults to length (make([]T, 长度, 容量) - 容量是可选的,默认为长度)slice1 := make([]int, 5) // length 5, capacity 5, elements are zeroed [0,0,0,0,0] (长度 5,容量 5,元素初始化为零 [0,0,0,0,0])slice2 := make([]int, 3, 10) // length 3, capacity 10, elements are zeroed [0,0,0] (长度 3,容量 10,元素初始化为零 [0,0,0])
// 3. Slicing an existing array or slice (对现有数组或切片进行切片)underlyingArray := [5]int{1, 2, 3, 4, 5}subSlice := underlyingArray[1:4] // Creates a slice [2, 3, 4]. Views elements at index 1, 2, 3. (创建一个切片 [2, 3, 4]。查看索引 1, 2, 3 的元素。) // low_bound:high_bound (high_bound is exclusive) (低边界:高边界 (高边界不包含))
// 4. A nil slice (zero value for a slice) (一个 nil 切片(切片的零值))var nilSlice []int // nilSlice is nil, length 0, capacity 0 (nilSlice 是 nil,长度 0,容量 0)// An empty slice can also be created: emptySlice := []int{} or make([]int, 0) (也可以创建一个空切片:emptySlice := []int{} 或 make([]int, 0))len() and cap() functions(len() 和 cap() 函数)
Section titled “len() and cap() functions(len() 和 cap() 函数)”len(s):Returns(返回) the number of elements currently in the slices。(切片 s 中当前元素的数量。)cap(s):Returns(返回) the capacity of the slices(the maximum number of elements it can hold without reallocation of the underlying array)。(切片 s 的容量(在底层数组重新分配之前它可以容纳的最大元素数量)。)
package main
import "fmt"
func printSliceInfo(name string, s []int) { fmt.Printf("%s: len=%d cap=%d slice=%v\n", name, len(s), cap(s), s) // (输出:%s: 长度=%d 容量=%d 切片=%v)}
func main() { s1 := make([]int, 3, 5) printSliceInfo("s1", s1) // s1: len=3 cap=5 slice=[0 0 0]
arr := [...]int{0, 1, 2, 3, 4, 5, 6, 7, 8, 9} s2 := arr[2:5] printSliceInfo("s2", s2) // s2: len=3 cap=8 slice=[2 3 4] // cap is from index 2 of arr to end of arr (容量是从数组 arr 的索引 2 到 arr 的末尾)}Nil Slices vs. Empty Slices(nil 切片与空切片)
Section titled “Nil Slices vs. Empty Slices(nil 切片与空切片)”- A nil slice(nil 切片) has a value of
nil。它有 length(长度)0 和 capacity(容量)0。它不 point to(指向) an underlying array(底层数组)。var s []int - An empty slice(空切片) is a slice with length(长度) 0 and capacity(容量) 0 (or more),but it is not
nil。它可能有或没有 an underlying array(底层数组)(通常是一个 zero-sized array - 零大小数组)。s1 := []int{}ors2 := make([]int, 0)
对于大多数 operations(操作),nil 和 empty slices(空切片)行为类似(例如,len 为 0,range loop(循环)不执行任何操作)。
Slicing (Creating Sub-Slices)(切片操作(创建子切片))
Section titled “Slicing (Creating Sub-Slices)(切片操作(创建子切片))”The slicing operator(切片运算符) s[low:high] creates a new slice(创建一个新切片),该切片 views a portion(查看一部分) of the original slice’s (or array’s) underlying array(原始切片(或数组)的底层数组)。新 slice(切片) shares the same underlying array(共享同一个底层数组)。
Modifications made through a sub-slice(通过子切片进行的修改) affect the original underlying array(会影响原始底层数组),thus other slices sharing that array(从而影响共享该数组的其他切片)。
package main
import "fmt"
func main() { numbers := []int{0, 1, 2, 3, 4, 5, 6, 7, 8} fmt.Println("Original:", numbers) // (输出:原始: [0 1 2 3 4 5 6 7 8])
s1 := numbers[1:4] // [1, 2, 3] fmt.Println("s1 (numbers[1:4]):", s1) // (输出:s1 (numbers[1:4]): [1 2 3])
s2 := numbers[:3] // [0, 1, 2] fmt.Println("s2 (numbers[:3]):", s2) // (输出:s2 (numbers[:3]): [0 1 2])
s3 := numbers[4:] // [4, 5, 6, 7, 8] fmt.Println("s3 (numbers[4:]):", s3) // (输出:s3 (numbers[4:]): [4 5 6 7 8])
// Modifying an element in a sub-slice changes the original (修改子切片中的元素会改变原始数据) s1[0] = 100 // numbers[1] becomes 100 (numbers[1] 变为 100) fmt.Println("Original after s1[0]=100:", numbers) // [0, 100, 2, 3, 4, 5, 6, 7, 8] (输出:s1[0]=100 后原始数据: [0 100 2 3 4 5 6 7 8]) fmt.Println("s2 after s1[0]=100:", s2) // [0, 100, 2] (s2 shares underlying array) (s1[0]=100 后 s2: [0 100 2] (s2 共享底层数组))}append() function(append() 函数)
Section titled “append() function(append() 函数)”The built-in append function(内置的 append 函数)adds elements to the end of a slice(将元素添加到切片的末尾)。If the capacity of the underlying array is insufficient(如果底层数组的容量不足),append allocates a new, larger array(会分配一个新的、更大的数组),copies the existing elements(复制现有元素),and then appends the new elements(然后添加新元素)。The resulting slice(结果切片)(possibly pointing to a new array - 可能指向一个新的数组) is returned(被返回)。
Syntax(语法):slice = append(slice, elem1, elem2, …)
Syntax(语法):slice = append(slice, anotherSlice…) (appends all elements of anotherSlice - 添加 anotherSlice 的所有元素)
package main
import "fmt"
func printSliceInfo(name string, s []int) { fmt.Printf("%s: len=%d cap=%d slice=%v\n", name, len(s), cap(s), s) // (输出:%s: 长度=%d 容量=%d 切片=%v)}
func main() { var s []int // A nil slice (一个 nil 切片) printSliceInfo("s (nil)", s)
s = append(s, 0) printSliceInfo("s (appended 0)", s)
s = append(s, 1) printSliceInfo("s (appended 1)", s)
s = append(s, 2, 3, 4) // Append multiple elements (添加多个元素) printSliceInfo("s (appended 2,3,4)", s)
s = append(s, []int{5,6,7}...) // Append another slice (添加另一个切片) printSliceInfo("s (appended slice)", s)}Output might look like (capacities can vary depending on Go's growth strategy):s (nil): len=0 cap=0 slice=[]s (appended 0): len=1 cap=1 slice=[0]s (appended 1): len=2 cap=2 slice=[0 1]s (appended 2,3,4): len=5 cap=6 slice=[0 1 2 3 4]s (appended slice): len=8 cap=12 slice=[0 1 2 3 4 5 6 7]copy() function(copy() 函数)
Section titled “copy() function(copy() 函数)”The built-in copy function(内置的 copy 函数)copies elements from a source slice to a destination slice(将元素从源切片复制到目标切片)。copy(dst, src) copies min(len(dst), len(src)) elements(复制 min(len(dst), len(src)) 个元素)。It returns the number of elements copied(它返回复制的元素数量)。The source and destination may overlap(源和目标可能重叠)。
package main
import "fmt"
func main() { src := []int{1, 2, 3} dst := make([]int, 2) // Destination slice with length 2 (长度为 2 的目标切片)
numCopied := copy(dst, src) fmt.Println("Destination slice (dst):", dst) // Output: [1 2] (输出:目标切片 (dst): [1 2]) fmt.Println("Number of elements copied:", numCopied) // Output: 2 (输出:复制的元素数量: 2)
dstFull := make([]int, len(src)) copy(dstFull, src) fmt.Println("Full copy (dstFull):", dstFull) // Output: [1 2 3] (输出:完全复制 (dstFull): [1 2 3])}Go - Range
Section titled “Go - Range”The range keyword(关键字)用于在 for loop(循环)中 iterate over elements(迭代元素) of various data structures(各种数据结构),如 arrays(数组)、slices(切片)、strings(字符串)、maps(映射)和 channels(通道)。
How range Works with Different Types(range 如何与不同类型一起工作)
Section titled “How range Works with Different Types(range 如何与不同类型一起工作)”The range keyword(关键字)returns one or two values(返回一个或两个值),取决于 being iterated(正在迭代)的 collection(集合):
| 集合类型 | 第一个值(索引/键) | 第二个值(元素) |
|---|---|---|
Array or Slice ([n]T or []T)(数组或切片) | index (int)(索引) | element (T)(元素) |
String (string)(字符串) | index (int, byte index of UTF-8 codepoint)(索引,UTF-8 码点的字节索引) | rune (int32, Unicode code point)(rune,Unicode 码点) |
Map (map[K]V)(映射) | key (K)(键) | value (V)(值) |
Channel (chan T)(通道) | element (T)(元素) | (没有第二个值。Iterates until channel is closed - 迭代直到通道关闭) |
If you only need the first value(如果您只需要第一个值)(e.g., index or key - 例如,索引或键),you can omit the second(您可以省略第二个):
for indexOrKey := range collection { … }
If you only need the second value(如果您只需要第二个值)(e.g., element - 例如,元素),使用 blank identifier(空白标识符)_ discard the first(丢弃第一个):
for _, element := range collection { … }
Example: Iterating with range(示例:使用 range 迭代)
Section titled “Example: Iterating with range(示例:使用 range 迭代)”package main
import "fmt"
func main() { // --- Iterating over a Slice --- (--- 迭代切片 ---) numbers := []int{10, 20, 30, 40} fmt.Println("Iterating over slice 'numbers':") // (输出:迭代切片 'numbers':) for index, value := range numbers { fmt.Printf("Index: %d, Value: %d\n", index, value) // (输出:索引: %d, 值: %d) } // Summing elements of a slice (对切片元素求和) sum := 0 for _, num := range numbers { // We only need the value, so discard index (我们只需要值,所以丢弃索引) sum += num } fmt.Println("Sum of numbers:", sum) // (输出:numbers 的总和: 100)
// --- Iterating over a Map --- (--- 迭代 map ---) // Note: Iteration order for maps is not guaranteed. (注意:map 的迭代顺序不保证。) countryCapitalMap := map[string]string{ "France": "Paris", "Italy": "Rome", "Japan": "Tokyo", } fmt.Println("\nIterating over map 'countryCapitalMap':") // (输出:迭代 map 'countryCapitalMap':) for country, capital := range countryCapitalMap { fmt.Printf("Capital of %s is %s\n", country, capital) // (输出:%s 的首都是 %s) } // Only keys from a map (仅从 map 中获取键) fmt.Println("\nCountries in map:") // (输出:map 中的国家:) for country := range countryCapitalMap { fmt.Println(country) }
// --- Iterating over a String --- (--- 迭代字符串 ---) // Range over a string iterates through Unicode code points (runes). (对字符串使用 range 迭代 Unicode 码点(rune)。) // The index is the starting byte position of the rune. (索引是 rune 的起始字节位置。) str := "Go语言" fmt.Println("\nIterating over string 'str':") // (输出:迭代字符串 'str':) for index, charRune := range str { fmt.Printf("Byte Index: %d, Character: %c (Rune: %U)\n", index, charRune, charRune) // (输出:字节索引: %d, 字符: %c (Rune: %U)) }}Output (map iteration order might vary):Iterating over slice 'numbers':Index: 0, Value: 10Index: 1, Value: 20Index: 2, Value: 30Index: 3, Value: 40Sum of numbers: 100
Iterating over map 'countryCapitalMap':Capital of France is ParisCapital of Italy is RomeCapital of Japan is Tokyo
Countries in map:FranceItalyJapan
Iterating over string 'str':Byte Index: 0, Character: G (Rune: U+0047)Byte Index: 1, Character: o (Rune: U+006F)Byte Index: 2, Character: 语 (Rune: U+8BED)Byte Index: 5, Character: 言 (Rune: U+8A00)Important Note on range and Value Copying(关于 range 和值复制的重要注意事项):
Section titled “Important Note on range and Value Copying(关于 range 和值复制的重要注意事项):”When ranging over an array or slice(当遍历数组或切片时),range provides a copy of each element(提供每个元素的副本)。If you need to modify the original elements(如果您需要修改原始元素),you must use the index to access them directly(您必须使用索引直接访问它们):collection[index] = newValue。
nums := []int{1, 2, 3}for _, val := range nums { // val is a copy (val 是一个副本) val *= 10 // This modifies the copy, not the original slice element (这修改的是副本,而不是原始切片元素)}fmt.Println(nums) // Output: [1 2 3] (输出: [1 2 3])
for i := range nums { // To modify, use the index (要修改,使用索引) nums[i] *= 10}fmt.Println(nums) // Output: [10 20 30] (输出: [10 20 30])Go - 递归
Section titled “Go - 递归”Recursion(递归)是一种 programming technique(编程技术),function(函数) calls itself(调用自身)来解决 problem(问题)。Recursive function(递归函数)通常有两个 parts(部分):
- Base Case(s)(基本情况):一个或多个 conditions(条件),用于 stop the recursion(停止递归),preventing an infinite loop(防止无限循环)。
- Recursive Step(递归步骤):function(函数)调用自身的部分,通常 with a modified argument(带有修改过的实参),该实参 moves closer to a base case(更接近基本情况)。
A simple conceptual example(一个简单的概念示例):
func recursiveFunction(parameters) { if base_condition_is_met { // (如果基本条件满足) // Solve directly and return (base case) (直接解决并返回(基本情况)) return some_value } else { // Modify parameters towards base case (朝着基本情况修改参数) // Call recursiveFunction with new parameters (recursive step) (使用新参数调用 recursiveFunction(递归步骤)) recursive_result := recursiveFunction(modified_parameters) // Optionally, process recursive_result and return (可选地,处理 recursive_result 并返回) return processed_result }}Go supports recursion(支持递归)。However(然而),programmers must ensure a well-defined base case(程序员必须确保定义良好的基本情况) to avoid infinite recursion(避免无限递归),这 can lead to a stack overflow error(可能导致栈溢出错误)。
Example 1: Factorial Calculation(示例 1:阶乘计算)
Section titled “Example 1: Factorial Calculation(示例 1:阶乘计算)”A non-negative integer n 的 factorial(阶乘)(denoted n! - 表示为 n!)是所有 positive integers less than or equal to n(小于或等于 n 的所有正整数)的 product(乘积)。0! = 1。
package main
import "fmt"
// factorial calculates n! (factorial 计算 n!)// Note: For large n, this can overflow int/uint64. Error handling for n < 0 omitted for simplicity. (注意:对于大的 n,这可能导致 int/uint64 溢出。为了简单起见,省略了对 n < 0 的错误处理。)func factorial(n uint64) uint64 { // Base case: if n is 0 or 1, factorial is 1 (基本情况:如果 n 是 0 或 1,阶乘是 1) if n <= 1 { return 1 } // Recursive step: n * factorial(n-1) (递归步骤:n * factorial(n-1)) return n * factorial(n-1)}
func main() { var num uint64 = 5 fmt.Printf("Factorial of %d is %d\n", num, factorial(num)) // Output: Factorial of 5 is 120 (输出:5 的阶乘是 120)
num = 0 fmt.Printf("Factorial of %d is %d\n", num, factorial(num)) // Output: Factorial of 0 is 1 (输出:0 的阶乘是 1)
// Be cautious with large numbers, e.g., factorial(20) is large (注意大数,例如 factorial(20) 很大) // factorial(65) would overflow uint64 (factorial(65) 会使 uint64 溢出) num = 20 fmt.Printf("Factorial of %d is %d\n", num, factorial(num))}Output for <code>factorial(20)</code>(factorial(20) 的输出):Factorial of 20 is 2432902008176640000Example 2: Fibonacci Series(示例 2:斐波那契数列)
Section titled “Example 2: Fibonacci Series(示例 2:斐波那契数列)”The Fibonacci sequence(斐波那契数列)是一系列 numbers(数字),其中 each number is the sum of the two preceding ones(每个数是前两个数的总和),通常 starting with 0 and 1(从 0 和 1 开始):0, 1, 1, 2, 3, 5, 8, …
package main
import "fmt"
// fibonacci returns the n-th Fibonacci number (fibonacci 返回第 n 个斐波那契数)// This recursive implementation is simple but highly inefficient due to repeated calculations. (这个递归实现简单但由于重复计算效率很低。)func fibonacci(n int) int { if n <= 0 { // Base case 1 (基本情况 1) return 0 } if n == 1 { // Base case 2 (基本情况 2) return 1 } // Recursive step (递归步骤) return fibonacci(n-1) + fibonacci(n-2)}
func main() { fmt.Println("Fibonacci sequence (first 10 terms):") // (输出:斐波那契数列(前 10 项):) for i := 0; i < 10; i++ { fmt.Printf("%d ", fibonacci(i)) } fmt.Println() // Output: 0 1 1 2 3 5 8 13 21 34}Pros and Cons of Recursion(递归的优缺点)
Section titled “Pros and Cons of Recursion(递归的优缺点)”Pros(优点):
- Can lead to elegant and simple solutions for problems that are naturally recursive(对于天然具有递归性质的问题(例如,树遍历,分治算法),可以带来优雅简单的解决方案)。
- Code can be easier to read and understand for such problems(对于这类问题,代码更易于阅读和理解)。
Cons(缺点):
- Can be less efficient than iterative solutions(可能不如迭代解决方案高效),due to function call overhead(由于函数调用开销)。
- Can lead to stack overflow errors for deep recursion levels(对于深层递归,可能导致栈溢出错误),as each function call consumes stack space(因为每次函数调用都会消耗栈空间)。
- Some recursive algorithms(一些递归算法)(like the naive Fibonacci example - 例如幼稚的斐波那契示例)can be very inefficient(可能非常低效),due to recomputing the same values multiple times(由于多次重复计算相同的值)。Memoization(记忆化) or dynamic programming(或动态规划) can mitigate this(可以缓解此问题)。
在 Go 中,虽然 recursion(递归) is supported(支持),但 iterative solutions(迭代解决方案)通常 is often preferred(更受欢迎),出于 performance(性能)和 to avoid stack depth limitations(避免栈深度限制),尤其是如果 recursion is not tail-call optimizable(递归不是尾调用可优化)时(Go 目前不 guarantee tail-call optimization - 不保证尾调用优化)。
Go - 类型转换(Type Conversion)
Section titled “Go - 类型转换(Type Conversion)”Go is a statically-typed language(是一种静态类型语言),generally requires explicit conversions(通常需要显式转换) between different types(不同类型之间)。这 in other languages(在其他语言中)often referred to as “type casting”(通常被称为“类型转换”),but in Go(但在 Go 中),the term type conversion(类型转换) is more accurate(更准确)。Implicit type conversions(隐式类型转换) are rare in Go(在 Go 中很少见)(e.g., assigning an untyped constant to a typed variable - 例如,将无类型常量赋值给有类型变量)。
Syntax for Type Conversion(类型转换语法)
Section titled “Syntax for Type Conversion(类型转换语法)”Syntax(语法) for converting a value v to a type T is T(v)(将值 v 转换为类型 T 的语法是 T(v))。
var myInt int = 10var myFloat float64 = float64(myInt) // Convert int to float64 (将 int 转换为 float64)
var myFloatVal float64 = 3.14// var myIntVal int = myFloatVal // Compile error: cannot use myFloatVal (type float64) as type int in assignment (编译错误:在赋值中不能使用 myFloatVal (类型 float64) 作为类型 int)var myIntVal int = int(myFloatVal) // Convert float64 to int (truncates decimal part) (将 float64 转换为 int(截断小数部分))Common Type Conversions(常见类型转换)
Section titled “Common Type Conversions(常见类型转换)”Numeric Conversions(数值转换):
Converting between different numeric types(在不同数值类型之间进行转换)(e.g., int to float64, int32 to int64)。注意 potential data loss(潜在数据丢失)(e.g., converting a large float64 to int truncates the decimal part - 例如,将大型 float64 转换为 int 会截断小数部分;converting a large int64 to int32 might overflow if the value doesn’t fit - 将大型 int64 转换为 int32 如果值不 fit,可能溢出)。Behavior on overflow(溢出时的行为) is platform-dependent(平台相关) or wraps around(或环绕)。
package main
import ( "fmt" "math")
func main() { var i int = 42 var f float64 = float64(i) // int to float64 var u uint = uint(f) // float64 to uint (decimal part truncated) (float64 到 uint(小数部分截断))
fmt.Printf("i: %T, %v\n", i, i) // (输出:i: int, 42) fmt.Printf("f: %T, %v\n", f, f) // (输出:f: float64, 42) fmt.Printf("u: %T, %v\n", u, u) // (输出:u: uint, 42)
sum := 17 count := 5 // var mean float32 = sum / count // Error: integer division, then type mismatch (错误:整数除法,然后类型不匹配) var mean float32 = float32(sum) / float32(count) fmt.Printf("Value of mean: %f\n", mean) // Output: Value of mean: 3.400000 (输出:平均值: 3.400000)
// Potential data loss (潜在数据丢失) largeFloat := 12345.6789 smallInt := int(largeFloat) // smallInt will be 12345 (truncated) (smallInt 将是 12345(截断)) fmt.Printf("largeFloat: %f, smallInt: %d\n", largeFloat, smallInt) // (输出:largeFloat: 12345.678900, smallInt: 12345)
bigInt64 := int64(math.MaxInt32 + 10) smallInt32 := int32(bigInt64) // Potential overflow if bigInt64 > MaxInt32 or < MinInt32 (如果 bigInt64 > MaxInt32 或 < MinInt32,潜在溢出) // Behavior on overflow is platform-dependent or wraps around. (溢出时的行为平台相关或环绕。) fmt.Printf("bigInt64: %d, smallInt32: %d\n", bigInt64, smallInt32) // (输出:bigInt64: 2147483657, smallInt32: -2147483639)}String Conversions(字符串转换):
- Integer to String(整数转字符串):使用
strconv.Itoa()(Integer to ASCII) 或fmt.Sprintf()。 - String to Integer(字符串转整数):使用
strconv.Atoi()(ASCII to Integer) 或strconv.ParseInt()。 - Byte Slice to String(字节切片转字符串):
string(byteSlice)。 - String to Byte Slice(字符串转字节切片):
[]byte(str)。
package main
import ( "fmt" "strconv")
func main() { num := 123 strNum := strconv.Itoa(num) fmt.Printf("String from int: %s (type %T)\n", strNum, strNum) // (输出:从 int 转字符串: 123 (类型 string))
strVal := "456" numVal, err := strconv.Atoi(strVal) if err != nil { fmt.Println("Error converting string to int:", err) // (输出:将字符串转换为 int 时出错: ...) } else { fmt.Printf("Int from string: %d (type %T)\n", numVal, numVal) // (输出:从字符串转 int: 456 (类型 int)) }
byteSlice := []byte{ 'h', 'e', 'l', 'l', 'o' } strFromBytes := string(byteSlice) fmt.Printf("String from byte slice: %s\n", strFromBytes) // (输出:从字节切片转字符串: hello)
originalStr := "world" bytesFromStr := []byte(originalStr) fmt.Printf("Byte slice from string: %v\n", bytesFromStr) // (输出:从字符串转字节切片: [119 111 114 108 100])}Type Conversions vs. Type Assertions(类型转换与类型断言)
Section titled “Type Conversions vs. Type Assertions(类型转换与类型断言)”It’s important to distinguish type conversions from type assertions(区分类型转换和类型断言很重要):
- Type Conversion (
T(v))(类型转换):Creates a new value of typeTfrom valuev(从值 v 创建一个类型 T 的新值)。它 applies between types that are explicitly convertible(应用于可显式转换的类型之间)(e.g., different numeric types, string and byte/rune slices - 例如,不同的数值类型,字符串和字节/rune 切片)。 - Type Assertion (
v.(T))(类型断言):Used with interface types(与接口类型一起使用)。它 checks if the dynamic type of the interface valuevisTand extracts the underlying value of typeT(检查接口值 v 的动态类型是否为 T,并提取类型 T 的底层值)。它 doesn’t convert the value(不转换值);it reveals its actual underlying type(它揭示了其实际的底层类型)。
Go’s emphasis on explicit type conversion helps prevent subtle bugs that can arise from unintended implicit conversions in other languages.
Go - 错误处理
Section titled “Go - 错误处理”Go 对 error handling(错误处理)有一种 distinctive approach(独特方法)。Instead of exceptions(而不是异常)(like try-catch blocks in Java or Python - 就像 Java 或 Python 中的 try-catch 块),functions that can fail in Go typically return an additional value of type error(Go 中可能失败的函数通常返回一个额外的 error 类型的值)。An error value of nil indicates success(一个值为 nil 的 error 表示成功),while a non-nil value indicates failure(而非 nil 的值表示失败)。
The error Type(error 类型)
Section titled “The error Type(error 类型)”error 是 Go 中的一个 built-in interface type(内置接口类型),定义如下:
type error interface { Error() string}Any type that implements the Error() string method(任何实现了 Error() string 方法的类型) satisfies the error interface(满足 error 接口)。The Error() method should return a human-readable description of the error(Error() 方法应返回错误的易读描述)。
Returning and Checking Errors(返回和检查错误)
Section titled “Returning and Checking Errors(返回和检查错误)”By convention(按照惯例),if a function can produce an error(如果一个函数可能产生错误),它 returns the error as its last return value(将 error 作为其最后一个返回值)。The caller then checks if the returned error is nil(调用者然后检查返回的 error 是否为 nil)。
package main
import ( "fmt" "os")
func main() { file, err := os.Open("non_existent_file.txt") // (尝试打开一个不存在的文件) if err != nil { // Handle the error (处理错误) fmt.Println("Error opening file:", err) // (输出:打开文件错误: ...) // Often, you might want to log the error and exit, or return it further up the call stack. (通常,您可能希望记录错误并退出,或者将其进一步返回到调用堆栈。) // For this example, we'll just print and continue. (对于此示例,我们只打印并继续。) } else { // Work with the file (使用文件) fmt.Println("File opened successfully:", file.Name()) // (输出:文件成功打开: non_existent_file.txt) file.Close() }
// Example of a function that returns an error (返回错误的函数示例) result, err := processData("") // (尝试处理空字符串) if err != nil { fmt.Println("Error processing data:", err) // (输出:处理数据错误: input cannot be empty) } else { fmt.Println("Processed data:", result) }}
// Example dummy function that might return an error (可能返回错误的示例模拟函数)func processData(data string) (string, error) { if data == "" { return "", fmt.Errorf("input cannot be empty") // (输入不能为空) } // Simulate processing (模拟处理) processed := "Processed: " + data return processed, nil}Creating Errors(创建错误)
Section titled “Creating Errors(创建错误)”Go provides several ways to create error values(提供几种创建 error 值的方法):
errors.New(message string) error:From theerrorspackage(来自 errors 包)。Creates a simple error with the given message(创建一个带有给定消息的简单错误)。
import "errors"
func doSomething(input string) error { if input == "" { return errors.New("input cannot be empty") // (输入不能为空) } // ... do something ... (... 做一些事 ...) return nil}fmt.Errorf(format string, args …interface{}) error:From thefmtpackage(来自 fmt 包)。Creates an error with a formatted message(创建一个带有格式化消息的错误),similar tofmt.Sprintf(类似于 fmt.Sprintf)。
import "fmt"
func divide(a, b float64) (float64, error) { if b == 0 { return 0, fmt.Errorf("cannot divide %.2f by zero", a) // (不能用 %.2f 除以零) } return a / b, nil}- Custom Error Types(自定义错误类型):You can define your own struct type that implements the
errorinterface(您可以定义自己的实现了 error 接口的结构体类型)。这 allows you to include more contextual information in your errors(允许您在错误中包含更多上下文信息)。
package main
import "fmt"
// MyError is a custom error type (MyError 是一个自定义错误类型)type MyError struct { Operation string Message string ErrorCode int}
func (e *MyError) Error() string { return fmt.Sprintf("Operation '%s' failed: %s (Code: %d)", e.Operation, e.Message, e.ErrorCode) // (输出:操作 '%s' 失败: %s (代码: %d))}
func performOperation(shouldFail bool) error { if shouldFail { return &MyError{ Operation: "Data Fetch", // (操作: 数据获取) Message: "Network timeout", // (消息: 网络超时) ErrorCode: 504, } } return nil}
func main() { err := performOperation(true) if err != nil { fmt.Println("Custom Error:", err) // (输出:自定义错误: Operation 'Data Fetch' failed: Network timeout (Code: 504)) // Type assertion to access custom fields (类型断言以访问自定义字段) if myErr, ok := err.(*MyError); ok { fmt.Println("Specific Error Code:", myErr.ErrorCode) // (输出:特定错误代码: 504) } }}Error Wrapping (Go 1.13+)(错误包装(Go 1.13+))
Section titled “Error Wrapping (Go 1.13+)(错误包装(Go 1.13+))”Go 1.13 introduced error wrapping(引入了错误包装),allowing an error to contain another error(允许一个错误包含另一个错误),creating a chain of errors(创建错误链)。这 helps preserve context from lower-level errors(有助于保留较低级别错误的上下文)。
fmt.Errorfwith the%wverb wraps an error(使用 %w 动词包装错误)。errors.Unwrap(err error) errorretrieves the wrapped error(检索包装的错误)。errors.Is(err, target error) boolchecks iferr(or any error in its chain) matchestarget(检查 err(或其链中的任何错误)是否与 target 匹配)。errors.As(err error, target interface{}) boolchecks iferr(or any error in its chain) is of a specific custom type and assigns it totarget(检查 err(或其链中的任何错误)是否为特定自定义类型,并将其赋值给 target)。
package main
import ( "errors" "fmt" "os")
var ErrCustom = errors.New("a custom sentinel error") // (一个自定义的哨兵错误)
func readFileAndProcess() error { data, err := os.ReadFile("somefile.txt") if err != nil { // Wrap the original error from os.ReadFile (包装来自 os.ReadFile 的原始错误) return fmt.Errorf("failed to read and process file: %w", err) // (读取和处理文件失败: %w) } // Process data... (处理数据...) if len(data) == 0 { return ErrCustom // (返回自定义错误) } return nil}
func main() { err := readFileAndProcess() if err != nil { fmt.Println("Original error:", err) // (输出:原始错误: failed to read and process file: open somefile.txt: no such file or directory)
// Check if the error is os.ErrNotExist (检查错误是否是 os.ErrNotExist) if errors.Is(err, os.ErrNotExist) { fmt.Println("File does not exist.") // (输出:文件不存在。) }
// Check if the error is our custom sentinel error (检查错误是否是我们的自定义哨兵错误) if errors.Is(err, ErrCustom) { fmt.Println("Encountered our custom error.") // (输出:遇到我们的自定义错误。) }
// Unwrap to get the underlying error (if any) (解包以获取底层错误(如果存在)) underlyingErr := errors.Unwrap(err) if underlyingErr != nil { fmt.Println("Underlying error:", underlyingErr) // (输出:底层错误: open somefile.txt: no such file or directory) } }}panic and recover
Section titled “panic and recover”Go also has panic and recover mechanisms(机制),but they are used for truly exceptional situations(但它们用于真正异常的情况),not for normal error handling(不用于正常的错误处理)。
panic(v interface{}):Stops the normal execution of the current goroutine(停止当前 goroutine 的正常执行)。The panic continues up the call stack(panic 沿着调用栈向上继续),running any deferred functions along the way(沿途运行所有 deferred 函数)。recover() interface{}:Regains control of a panicking goroutine(恢复一个 panicking 的 goroutine)。recoveris only useful inside deferred functions(仅在 deferred 函数内部有用)。If the current goroutine is not panicking(如果当前 goroutine 没有 panicking),recoverreturnsnil(返回 nil)。
Idiomatic Go strongly prefers explicit error returns over panic for most error conditions.