Go中链表节点必须用*Node声明指针类型,否则因值类型嵌套导致无限递归编译错误;操作时需警惕nil指针解引用,头空需更新头指针,遍历用current!=nil,删除靠调整Next跳过节点而非置nil。

用 *Node 声明节点指针,不是用 Node
Go 里没有隐式指针转换,Node 是值类型,直接嵌套会导致无限递归定义(编译报错:invalid recursive type Node)。必须显式用指针类型声明字段:
<pre class="brush:php;toolbar:false;">type Node struct {
Data int
Next *Node // ✅ 正确:指针指向另一个 Node
}
常见错误是写成 Next Node,这会让 Go 认为 <code>Node 包含自身完整副本,无法计算大小。
插入节点时要小心 nil 指针解引用
链表操作中,遍历到末尾或头节点为空时,current.Next 可能是 nil。直接赋值没问题,但读取前必须判空:
-
head == nil→ 插入新节点需更新头指针本身(传二级指针或返回新 head) -
current.Next == nil→ 到达尾部,可安全赋值current.Next = newNode - 误写
current.Next.Data = 42且current.Next为nil→ panic:invalid memory address or nil pointer dereference
示例(尾插):
立即学习“go语言免费学习笔记(深入)”;
<pre class="brush:php;toolbar:false;">func Append(head *Node, data int) *Node {
newNode := &Node{Data: data}
if head == nil {
return newNode // 头为空,新节点即新 head
}
current := head
for current.Next != nil {
current = current.Next
}
current.Next = newNode
return head
}
遍历链表用 for current != nil,别用 <code>for current.Next != nil
后者会漏掉最后一个节点。正确模式是「先处理当前节点,再跳到下一个」:
<pre class="brush:php;toolbar:false;">func PrintList(head *Node) {
current := head
for current != nil {
fmt.Print(current.Data)
if current.Next != nil {
fmt.Print(" → ")
}
current = current.Next
}
fmt.Println()
}
这个循环条件也决定了:如果函数参数是 *Node,修改 <code>current 本身(如 current = current.Next)不影响外部指针,只影响局部变量 —— 这正是我们想要的。
删除节点时注意「绕过」而非「置 nil」
Go 没有手动内存释放,删除本质是让某段链失去引用。关键不是把字段设为 nil,而是调整前驱节点的 Next 指向被删节点的后继:
- 删头节点:返回
head.Next,原head若无其他引用会被 GC - 删中间节点:设
prev.Next = current.Next,跳过current - 不要写
current = nil—— 这只是把局部变量清空,不改变链结构
真正起作用的是修改了某个 Next 字段的值,让链表“跳过”该节点。
*Node 展开;最容易忽略的是头节点为空时的边界处理,以及误把指针变量赋值当成修改链结构。


















