--- tags: [go, golang, go基础语法, 结构体] create time: 2026-06-07 15:00 --- # Go 语言结构体 ## 概述 Go 没有类(class),但通过**结构体(struct)**实现数据的组合。本文介绍结构体的定义、初始化、成员访问,以及嵌入(匿名字段)机制。 ## 正文 ### 为什么 Go 不用 class? 在 Java / C++ 中,你用 `class` 同时定义**数据**和**行为**。Go 把它们拆开了:`struct` 只负责数据,`method`(方法)是后来绑定到 struct 上的。这种设计让数据结构和方法可以独立演化。 > [!question] ❓ 思考 > 如果一个 Student 有 Name、Age、Score 三个属性,用基本类型能表示吗?为什么需要组合? ### 定义与初始化 #### 定义 ```go type Student struct { ID int Name string Age int Score int } ``` 字段声明格式为 `名称 类型`,同类型的可以合并: ```go type Student struct { ID int Name string Age int Score int } // 等价于 type Student struct { ID, Age int Name string Score int } ``` #### 初始化方式 **键值对初始化**(推荐,清晰明了): ```go st := Student{ ID: 100, Name: "zhangsan", Age: 18, Score: 98, } fmt.Printf("%v\n", st) // {100 zhangsan 18 98} ``` **位置初始化**(按字段顺序,字段少时可用): ```go st := Student{101, "lisi", 20, 97} ``` > [!tip] 💡 零值初始化 > 只写 `Student{}` 会创建一个所有字段为零值的结构体: > ```go > st := Student{} // {0 "" 0 0} > ``` **new() vs 直接初始化**: ```go st1 := &Student{} // ✅ 推荐:简洁,可直接赋值字段 st2 := new(Student) // 等价于 &Student{},返回 *Student ``` > [!note] 📝 new(T) 的作用 > `new(T)` 分配零值内存并返回 `*T`。它与 `&T{}` 功能等价,但后者更灵活——可以直接内联初始化部分字段。 ### 成员访问 使用 `.` 操作符。指针类型的 struct 也可以直接用 `.` 访问字段(Go 自动解引用): ```go st := Student{Name: "zhangsan", Age: 18} fmt.Println(st.Name) // zhangsan ptr := &st fmt.Println(ptr.Name) // zhangsan(Go 自动解引用,等价于 (*ptr).Name) ``` ### 结构体嵌套(组合) Go 没有继承,但通过**组合**实现类似效果: ```go type Address struct { City string Postal string } type Person struct { Name string Age int Address // 匿名嵌入(anonymous field) } p := Person{ Name: "Alice", Address: Address{City: "Beijing"}, } fmt.Println(p.City) // 直接访问嵌入字段! ``` > [!info] ℹ️ 嵌入 vs 普通字段 > - **匿名嵌入**(无字段名):外层可以直接访问内层字段,模拟"继承" > - **命名字段**:需要通过字段名访问,如 `p.Address.City` ```mermaid graph LR A["Person"] --> B["Name: string"] A --> C["Age: int"] A --> D["Address ← 嵌入"] D --> E["City: string"] D --> F["Postal: string"] ``` ### 结构体作为函数参数 > [!warning] ⚠️ 值拷贝陷阱 > Go 的结构体是**值类型**,传入函数时会被完整拷贝。修改函数内的参数不会影响原始 struct。 ```go func ChangeName(s Student) { s.Name = "modified" // 只修改了副本 } func ChangeNamePtr(s *Student) { s.Name = "modified" // 通过指针修改原值 } ``` > [!tip] 💡 大结构体传指针 > 结构体较大时,传递 `*T` 而非 `T` 可以避免不必要的内存拷贝。即使你不打算修改它,传指针也是常见的性能优化手段。