Skip to content

作者

novlan1

2026.08.23

Go 为什么一定要 package main 和 func main

2026-08-24

实测结果:

=== package foo(非 main)===
package command-line-arguments is not a main package

=== package main 但没有 func main ===
function main is undeclared in the main package

两条都是硬报错。想用 go run 跑,package mainfunc main() 缺一不可。

为什么

根本原因是 Go 是编译型语言,而 Node/Python 是解释型(脚本)语言,它们"跑一个文件"的本质完全不同:

Node / PythonGo
node xx.js 的本质逐行解释执行,入口就是文件顶层代码编译成可执行程序再运行
入口在哪文件第一行开始func main()(Go runtime 从这里启动)
需要声明吗不需要,脚本天然有顶层执行流必须 package main + func main()

Go 的可执行程序必须满足两条硬规则:

  1. package main —— 只有 main 包会被编译器链接成可执行文件,其他包(package foo)只能编译成库(被 import 用),不能独立运行。
  2. func main() —— 这是 Go runtime 约定的程序入口,编译器在 package main 里找 main 函数作为启动点,找不到就报 main is undeclared

类比理解

go
package main   // ← 相当于告诉编译器"这是个能跑的程序,不是库"

func main() {  // ← 相当于 Node/Python 的"文件顶层代码",从这里开始执行
    fmt.Println("hello")
}
  • package main ≈ "这是主程序"
  • func main() ≈ "这是 main 入口,等价于脚本的第一行"

所以 Go 里没法"不写 main 直接跑"——这跟它"显式、编译、类型安全"的语言哲学是一体的。想随手跑,就必须包这一层壳,没有脚本模式。

对你 leetcode 场景的影响:每个题文件必须保留 package main + func main(),这层壳是 Go 的"入场券",省不掉。之前那个 DuplicateDecl 红波浪线,根源不是这层壳,而是"同目录多文件"的问题——壳要保留,但文件要各自独立目录。

Go 的运行方式(和 Node/Python 的区别)

2026-08-24

语言直接运行命令说明
Nodenode xxx.js解释执行,直接跑源码
Pythonpython3 xxx.py解释执行,直接跑源码
Gogo run xxx.go编译 + 运行(临时编译,不留产物)

Go 是编译型语言,没有 go xxx.go 这种命令。两种等价方式:

bash
# 方式 1:临时编译并运行(适合随手验证,不留产物)
go run 01-quick-sort.go

# 方式 2:先编译成二进制,再执行(适合正式使用)
go build 01-quick-sort.go
./01-quick-sort

Go 有文件模式和包模式之分

2026-08-24

这是 Go 的 "同目录一个包" 模型限制:同目录下所有 .go 文件必须属于同一个 package,而 package main 在一个包里只能有一个 func main()

这是什么问题

截图里的报错 main redeclared in this block (compiler DuplicateDecl)Go 语言服务器(gopls,也就是 VSCode 的 Go 插件) 报的,根源是:

同一个目录下,所有 .go 文件必须属于同一个 package,而 package main 里只能有一个 func main()

你的 leetcode/04-tree/ 目录下现在有 3 个文件,每个都是 package main + func main()

01-level-order.go      → func main()
02-max-depth.go        → func main()
09-preorder-traversal.go → func main()

gopls 把整个目录当成一个包来静态检查,发现 3 个 main 互相冲突,就标红了。

关键:命令行其实能跑,报错只是编辑器

我实测过了,命令行 go run 单个文件.go 完全正常

$ cd leetcode/04-tree && go run 01-level-order.go
[[3] [9 20] [15 7]]      ← 正常运行,没有报错

原因是 Go 有"文件模式"和"包模式"之分:

模式触发方式编译范围会报错吗
文件模式go run 01-level-order.go只编译这一个文件✅ 不报错
包模式go build / go test / gopls 静态检查整个目录的所有 .go❌ 报 DuplicateDecl

所以:这个红波浪线不影响你用 go run xxx.go 跑题,纯属编辑器层面的提示。

如果想彻底消除红波浪线,两个方案

方案 A(推荐):每个题独立子目录

leetcode/04-tree/
  01-level-order/main.go
  02-max-depth/main.go
  09-preorder-traversal/main.go

这样每个子目录是一个独立的 package main,gopls 不再报错,go run ./01-level-ordergo run ./01-level-order/main.go 都能跑。这是 Go 生态的标准做法,代价是目录会多一层。

方案 B(最省事):不管它

命令行照常用 go run xxx.go 跑,忽略编辑器的红波浪线。缺点是你以后在 VSCode 里点"运行"按钮(coderunner 插件)时可能用包模式,会报错。

专有名词

2026-08-24

company 架构专有名词

  • IDC: Internet Data Center
  • GSLB:Global Server Load Balance
  • LVS:Linux Virtual Server
  • TVS:Tencent Virtual Server
  • TSV:专门处理服务器发向客户端的数据,是反过来的tvs,故取名TSV
  • RS:Real Server
  • LD:Load Balance Director
  • TGW:Tencent Gateway
  • CMDB:Config Manage Database
  • OIDB:Open Interface of Database
  • CMLB:Comm Load Balance System

网络

  • DNS:Domain Name System
  • CGI:Common Gateway Interface

HTTPS

  • SSL: Secure Socket Layer
  • TLS: Transport Layer Security
  • PKI: Public Key Infrastructure
  • CSR: Certificate Signing Request
  • DA: Distinguished Name
  • CRL: Certificate Revocation List
  • SNI: Server name Indication

Go channel 使用环形缓冲区(环形队列)核心原因

2026-08-24

底层本质:一块连续定长数组 + sendx/recvx 读写索引 + qcount 计数,逻辑上形成环,并不是物理上的环形内存

对比几个备选方案,看为什么不选它们

  1. 普通线性数组队列:出队要整体前移,大数据下拷贝开销巨大 ❌
  2. 链表队列:频繁节点分配、内存碎片、GC 压力大,Cache 不友好 ❌
  3. 动态 slice 队列:会无限扩容,失去有界阻塞语义,无法限流 ❌

一句话总结:环形缓冲区用固定连续内存、O (1) 读写、无数据搬迁,兼顾 CPU 缓存效率 + 低 GC 开销 + 有界阻塞语义,是并发生产者消费者场景下非常适合的队列实现

反射

2026-08-23

Go 语言中的 reflect 包提供了反射这一种强大且灵活的机制,它允许程序在运行时动态地检查和操作对象的类型和值。换句话说,反射提供了在编译时不知道具体类型的情况下,这些类型相关信息的访问和操作能力。通过反射,我们可以获得关于结构体、变量类型和值的信息,还可以修改这些值。

扩展知识

反射在 Go 语言中主要通过三个标准库函数来实现:

  1. reflect.TypeOf:获取变量的类型信息。
  2. reflect.ValueOf:获取变量的值信息。
  3. reflect.New:创建一个指定类型的新变量。

go funcs(){}()

2026-08-23

go
// go +【匿名函数 + ()调用】
go func() {
    time.Sleep(time.Second)
    cancel()
}()

go 后面必须跟函数调用,开启一个新 goroutine 并发执行。

  • ✅ go 函数调用()
  • ❌ go func(){} 错误!这只是传函数本身,不是调用,编译报错。

*p++

2026-08-23

等同于

go
// 先读取指针 p 指向的值,然后进行自增
*p = *p + 1

复合字面量

2026-08-23

Go 字面量语法对比:[]string{"1","2"}

注意:你写的 []string{'1','2'}错误语法!单引号在 Go 里是 rune(int32),不能直接放 string 数组,正确写法:[]string{"1", "2"}

一、切片(Slice)字面量(最常用,你这个就是)

go
// 字符串切片
var s = []string{"1", "2"}
// int切片
var nums = []int{1, 2, 3}
// 空切片
var empty = []int{}

二、数组(Array)字面量(固定长度,和切片容易混淆)

go
// [2]string 长度固定为2的数组
var arr = [2]string{"1", "2"}
// 自动推导长度 [...]
var arr2 = [...]string{"a", "b", "c"}

三、map 字面量

go
// map[键类型]值类型{ k:v, ... }
m := map[string]int{"a": 1, "b": 2}
// 空map
emptyMap := map[string]int{}

四、结构体 struct 字面量

go
type User struct {
    Name string
    Age  int
}
// 方式1:按顺序赋值
u1 := User{"zhangsan", 18}
// 方式2:命名字段赋值(推荐)
u2 := User{Name: "lisi", Age: 20}
// 结构体指针字面量
u3 := &User{Name: "wangwu"}

五、指针字面量(配合复合字面量)

Go 没有 &int{1} 这种基础类型复合字面量,但结构体/数组/切片复合字面量可以直接取地址

go
p := &User{Name: "test"}

六、通道 channel(没有复合字面量,只能 make)

❌ 不能 chan int{1},必须 make

go
ch := make(chan int, 2)

七、复合字面量通用简写规则(Go 核心语法)

T{ ... } 叫做 复合字面量(composite literal),支持:

  1. []T{...} → 切片
  2. [N]T{...} / [...]T{...} → 数组
  3. map[K]V{...} → map
  4. StructType{...} → 结构体
  5. &T{...} → 取复合字面量地址(返回指针)

八、其他常见简写

1. short variable declaration 短变量 :=
go
s := []string{"1", "2"} // 自动推导类型,等价 var s = []string{"1","2"}
2. 函数复合字面(函数类型变量)
go
f := func(a int) int { return a + 1 }
3. 接口:没有字面量,靠赋值实现自动满足

✅ 容易踩坑补充

  1. 'a' → rune(int32),双引号 "a" → string
    go
    []rune{'1','2'} // ✅ 合法 rune切片
    []string{'1','2'} // ❌ 编译报错!类型不匹配
  2. 切片 vs 数组:
    • []string{} → 切片(动态长度)
    • [2]string{} → 数组(固定长度)

完整汇总表

写法类型名称
[]int{1,2}[]int切片复合字面
[3]int{1,2}[3]int数组复合字面
[...]int{1,2}[2]int自动长度数组
map[string]int{"a":1}map[string]intmap复合字面
User{Name:"xx"}User结构体复合字面
&User{}*User结构体指针复合字面

字面量

2026-08-23

JS/TS 中的字面量

  • 1 → 数字字面量
  • "hi" → 字符串字面量
  • [1,2] → 数组字面量(Array Literal)
  • {a:1} → 对象字面量(Object Literal)

字面量核心

  1. 字面量:代码直接写出值,不用运行时计算;基础字面量是单个值,复合字面量 = 用 Type{...} 一次性组装多个子字面量,生成数组 / 切片 /map/ 结构体。
  2. TS、Python 都有等价写法([]、{}),但是没有 Go “复合字面量” 这个统一术语,分别叫数组 / 对象 / 列表 / 字典字面量。
  3. Go 的特色:统一用 T{} 语法,并且支持 &T{} 直接取地址。