Go中用指针实现链表是唯一自然方式,因其无引用类型且值默认拷贝传递,若next为Node而非Node,赋值会复制整个节点导致链表断裂;故Next字段必须为Node,操作也需用*Node。

为什么用指针实现链表是 Go 的唯一自然方式
Go 没有引用类型(如 C++ 的 & 引用),也没有隐式地址传递;所有值默认按拷贝传递。若不用指针,Node 结构体每次传参或赋值都会复制整个节点(含数据和“下一个”字段),导致 next 指向的是副本而非原链上节点——链表直接断裂。所以 Go 中实现链表,next 字段必须是 *Node,操作节点也几乎总得用 *Node。
定义链表节点与基础操作的典型写法
一个最小可用的单向链表节点通常长这样:
type Node struct {
Data int
Next *Node
}
注意三点:
-
Next字段类型是*Node,不是Node;设为nil表示链尾 - 插入、删除、遍历等操作函数的接收者一般用
*Node或额外传入**Node(尤其涉及修改头节点时) - 不要在结构体里嵌入
*Node做“头指针”,应单独维护head *Node变量;否则易引发循环引用或误判空链表
插入节点时最容易踩的坑:忘记解引用或错用地址取值
比如在链表头部插入新节点,常见错误写法:
立即学习“go语言免费学习笔记(深入)”;
// ❌ 错误:newNode.Next = head —— head 是 *Node,但这里想让它指向原头节点 // 正确应是 newNode.Next = head,这本身没错,但常有人写成: newNode.Next = &head // 多余取地址,head 已是指针
更典型的翻车点在遍历时修改 next:
- 写
cur = cur.Next是对的(cur是*Node,移动指针) - 写
cur = &cur.Next就错了——cur.Next是*Node,再取地址得到的是**Node,类型不匹配 - 插入到第 k 个位置前,需用
for i := 0; i 控制,漏掉cur != nil会导致 panic: runtime error: invalid memory address
是否该封装成结构体类型(如 type List struct { Head *Node })
可以,但要注意封装粒度:
- 如果只做简单 CRUD,直接操作
*Node更轻量,避免无谓包装 - 若需记录长度、支持双向遍历、或统一管理内存(如池化节点),用
List封装更清晰 - 别把
Head设为值字段(Head Node),否则每次赋值都复制整个节点;必须是Head *Node - 实现
Insert方法时,若允许插到空链表头部,方法接收者必须是*List,否则无法修改l.Head
链表本身不复杂,但 Go 的指针语义干净而严格——没地址就没连接,没解引用就动不了数据,稍一混淆,nil pointer dereference 就立刻报给你看。










