Go函数声明基本结构为func name(参数列表) 返回类型 {函数体},必须显式声明返回类型;多返回值需用括号包裹类型列表;所有参数均为值传递;main函数必须在main包中且签名严格为func main()。

Go函数声明的基本结构长什么样
Go语言里函数必须显式声明返回类型,没有隐式推导。最简形式是 func name() {},但只要函数有返回值,就得在括号后写明类型,哪怕只返回一个值也要加括号包裹:func add(a, b int) int,不能写成 func add(a, b int) int(这其实是合法的,但注意:多个参数同类型可省略重复类型,如 a, b int 是对的;常见误写是漏掉最后的返回类型或错用逗号分隔返回类型)。
常见错误现象:missing function body(忘了写花括号)、non-name function parameter(参数没起名,比如写成 func(int, int) int 而没给参数命名,虽然匿名参数语法合法,但绝大多数场景需要命名才能在函数体内使用)。
多个返回值怎么写才不报错
Go支持多返回值,但必须用括号包裹返回类型列表,例如 func split(n int) (int, int)。括号不能省,否则编译器会认为只返回一个类型为 int, int 的非法类型。
- 命名返回值更安全:写成
func split(n int) (x, y int),这样函数体里可以直接用x、y赋值,且return可以无参数(即“裸 return”),但要注意——裸 return 在闭包或带 defer 的函数中容易引发意外行为 - 未命名多返回值必须显式 return 多个值:
return a, b,顺序和声明顺序严格一致 - 如果只想要其中一个返回值,用下划线丢弃:
_, err := os.Open("x"),但不能写成err := os.Open("x")(类型不匹配)
函数参数是值传递还是引用传递
Go所有参数都是值传递,包括 slice、map、chan、interface、指针。关键在于:传的是“变量的值”,而这个值可能是地址(如指针)或结构体副本(如 struct)。所以 func modify(s []int) 看似传 slice,实际传的是包含指针、长度、容量的 header 副本——修改元素会影响原 slice,但 append 后若底层数组扩容,原 slice 不会变。
立即学习“go语言免费学习笔记(深入)”;
容易踩的坑:
- 想修改原始 struct 字段?传指针:
func update(p *Person),否则改的是副本 - 传 map 或 slice 时误以为“传引用就能扩容影响原变量”,其实不能——扩容后新 header 不会回写到调用方
- 接口类型参数(如
io.Reader)也是值传递,但内部可能持有所需的指针,行为取决于具体实现
main 函数为什么必须在 main 包里
main 函数不是关键字,而是 Go 运行时约定的入口标识符。它必须满足两个硬性条件:所在包为 main,且函数签名为 func main()(无参数、无返回值)。其他任何变体,比如 func main(args []string) 或 func main() int,都会导致 runtime: no main function 或链接失败。
常见混淆点:
- 写测试时用
func TestXxx(t *testing.T),这是 testing 包约定,和 main 无关 - 命令行参数通过
os.Args获取,不是 main 的参数 - 想让某个函数被外部调用?别叫它 main,改成普通函数名,并确保它在非-main 包中导出(首字母大写)
函数签名看似简单,但参数命名、返回值括号、值传递语义、main 的约束这四点,任一疏忽都直接卡在编译或运行时行为上。尤其当从其他语言转来时,习惯性加 return 类型推导或默认引用传递,最容易掉进坑里。









