Files
cs-note/hzh/GolangStar/Go语言基础/Go语言结构体.md
T

152 lines
3.6 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
---
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` 可以避免不必要的内存拷贝。即使你不打算修改它,传指针也是常见的性能优化手段。