golang中可触发panic的几种情况汇总
<div id="navCategory"><h5 class="catalogue">目录</h5><ul class="first_class_ul"><li><a href="#_label0">一. 触发panic 的场景</a></li><ul class="second_class_ul"><li><a href="#_lab2_0_0">1. 显式调用panic函数</a></li><li><a href="#_lab2_0_1">2. 运行时错误</a></li><ul class="third_class_ul"><li><a href="#_label3_0_1_0">2.1 数组/切片越界</a></li><li><a href="#_label3_0_1_1">2.2 空指针解引用</a></li><li><a href="#_label3_0_1_2">2.3 类型断言失败</a></li><li><a href="#_label3_0_1_3">2.4 除数为 0</a></li><li><a href="#_label3_0_1_4">2.5 字符串越界</a></li></ul><li><a href="#_lab2_0_2">3. Channel 操作错误</a></li><ul class="third_class_ul"><li><a href="#_label3_0_2_5">3.1 向已关闭的 channel 发送数据</a></li><li><a href="#_label3_0_2_6">3.2 关闭 nil channel</a></li></ul><li><a href="#_lab2_0_3">4. 内存或资源耗尽</a></li><ul class="third_class_ul"><li><a href="#_label3_0_3_7">4.1 内存分配失败</a></li><li><a href="#_label3_0_3_8">4.2 堆栈溢出(无限递归)</a></li></ul><li><a href="#_lab2_0_4">5. 并发问题</a></li><ul class="third_class_ul"><li><a href="#_label3_0_4_9">5.1 无锁的并发读写 map</a></li></ul><li><a href="#_lab2_0_5">6. 其他场景</a></li><ul class="third_class_ul"><li><a href="#_label3_0_5_10">6.1 调用未实现的方法</a></li><li><a href="#_label3_0_5_11">6.2 错误的defer使用</a></li></ul></ul><li><a href="#_label1">二. 如何处理 panic?</a></li><ul class="second_class_ul"></ul><li><a href="#_label2">三. 总结</a></li><ul class="second_class_ul"></ul></ul></div><p>在 Go 语言中,<strong>panic</strong> 是一种运行时错误处理机制,当程序遇到无法恢复的严重错误时会触发 panic。以下是常见的触发 panic 的场景及其示例:</p><p class="maodian"><a name="_label0"></a></p><h2>一. 触发panic 的场景</h2>
<p class="maodian"><a name="_lab2_0_0"></a></p><h3>1. 显式调用panic函数</h3>
<p>开发者可以主动调用 <code>panic</code> 函数来触发 panic,通常用于标记程序中无法继续执行的错误(如逻辑错误、资源缺失等)。</p>
<div class="jb51code"><pre class="brush:go;">func main() {
if err := someCriticalCheck(); err != nil {
panic("Critical error: " + err.Error())
}
}
</pre></div>
<p class="maodian"><a name="_lab2_0_1"></a></p><h3>2. 运行时错误</h3>
<p>Go 运行时检测到某些非法操作时会自动触发 panic。</p>
<p class="maodian"><a name="_label3_0_1_0"></a></p><h4>2.1 数组/切片越界</h4>
<p>访问数组或切片的索引超出其长度时会触发 panic。</p>
<div class="jb51code"><pre class="brush:go;">func main() {
arr := []int{1, 2, 3}
fmt.Println(arr) // panic: index out of range
}
</pre></div>
<p class="maodian"><a name="_label3_0_1_1"></a></p><h4>2.2 空指针解引用</h4>
<p>访问未初始化的指针或 <code>nil</code> 指针指向的值时会触发 panic。</p>
<div class="jb51code"><pre class="brush:go;">func main() {
var p *int
fmt.Println(*p) // panic: runtime error: invalid memory address or nil pointer dereference
}
</pre></div>
<p class="maodian"><a name="_label3_0_1_2"></a></p><h4>2.3 类型断言失败</h4>
<p>当接口类型断言的目标类型与实际类型不匹配时,会触发 panic。</p>
<div class="jb51code"><pre class="brush:go;">func main() {
var i interface{} = "hello"
s := i.(int) // panic: interface conversion: string is not int
}
</pre></div>
<p class="maodian"><a name="_label3_0_1_3"></a></p><h4>2.4 除数为 0</h4>
<p>在运行时进行除法操作时,如果除数为 0,会触发 panic。</p>
<div class="jb51code"><pre class="brush:go;">func main() {
result := 10 / 0 // panic: runtime error: integer divide by zero
fmt.Println(result)
}
</pre></div>
<p class="maodian"><a name="_label3_0_1_4"></a></p><h4>2.5 字符串越界</h4>
<p>访问字符串的索引超出其长度时会触发 panic。</p>
<div class="jb51code"><pre class="brush:go;">func main() {
s := "abc"
fmt.Println(s) // panic: string index out of range
}
</pre></div>
<p class="maodian"><a name="_lab2_0_2"></a></p><h3>3. Channel 操作错误</h3>
<p>对 channel 的非法操作会触发 panic。</p>
<p class="maodian"><a name="_label3_0_2_5"></a></p><h4>3.1 向已关闭的 channel 发送数据</h4>
<p>关闭 channel 后,如果再向其发送数据,会触发 panic。</p>
<div class="jb51code"><pre class="brush:go;">func main() {
ch := make(chan int)
close(ch)
ch <- 1 // panic: send on closed channel
}
</pre></div>
<p class="maodian"><a name="_label3_0_2_6"></a></p><h4>3.2 关闭 nil channel</h4>
<p>关闭一个未初始化的 <code>nil</code> channel 会触发 panic。</p>
<div class="jb51code"><pre class="brush:go;">func main() {
var ch chan int
close(ch) // panic: close of nil channel
}
</pre></div>
<p class="maodian"><a name="_lab2_0_3"></a></p><h3>4. 内存或资源耗尽</h3>
<p>当程序运行时内存分配失败或发生堆栈溢出时,会触发 panic。</p>
<p class="maodian"><a name="_label3_0_3_7"></a></p><h4>4.1 内存分配失败</h4>
<p>创建过大的数据结构可能导致内存不足。</p>
<div class="jb51code"><pre class="brush:go;">func main() {
arr := make([]int, 1<<30) // 可能触发 panic: runtime error: cannot allocate
}
</pre></div>
<p class="maodian"><a name="_label3_0_3_8"></a></p><h4>4.2 堆栈溢出(无限递归)</h4>
<p>递归深度过大时会导致堆栈溢出。</p>
<div class="jb51code"><pre class="brush:go;">func main() {
func() {
defer func() { recover() }()
infiniteRecursion() // panic: runtime error: stack overflow
}()
}
func infiniteRecursion() {
infiniteRecursion()
}
</pre></div>
<p class="maodian"><a name="_lab2_0_4"></a></p><h3>5. 并发问题</h3>
<p>某些并发场景下会触发 panic。</p>
<p class="maodian"><a name="_label3_0_4_9"></a></p><h4>5.1 无锁的并发读写 map</h4>
<p>对 <code>map</code> 的并发读写(未加锁)会导致 panic。</p>
<div class="jb51code"><pre class="brush:go;">func main() {
m := make(mapint)
for i := 0; i < 100; i++ {
go func() {
m = 1 // panic: concurrent map write
}()
}
time.Sleep(time.Second)
}
</pre></div>
<p class="maodian"><a name="_lab2_0_5"></a></p><h3>6. 其他场景</h3>
<p class="maodian"><a name="_label3_0_5_10"></a></p><h4>6.1 调用未实现的方法</h4>
<p>如果接口类型变量调用了未实现的方法,会触发 panic。</p>
<div class="jb51code"><pre class="brush:go;">type MyInterface interface {
Method()
}
type MyStruct struct{}
func (m *MyStruct) Method() {} // 如果没有实现 Method,调用时会 panic
func main() {
var i MyInterface = MyStruct{} // panic: MyStruct does not implement MyInterface (Method method has pointer receiver)
i.Method()
}
</pre></div>
<p class="maodian"><a name="_label3_0_5_11"></a></p><h4>6.2 错误的defer使用</h4>
<p>在 <code>defer</code> 中返回值可能被修改时,如果逻辑错误可能导致 panic。</p>
<div class="jb51code"><pre class="brush:go;">func main() {
defer func() {
if r := recover(); r != nil {
fmt.Println("Recovered:", r)
}
}()
defer func() {
panic("defer panic") // panic: defer panic
}()
panic("main panic")
}
</pre></div>
<p class="maodian"><a name="_label1"></a></p><h2>二. 如何处理 panic?</h2>
<ol><li>使用 <code>recover</code> 和 <code>defer</code> 可以捕获 panic,防止程序崩溃:</li></ol>
<div class="jb51code"><pre class="brush:go;">func safeFunc() {
defer func() {
if r := recover(); r != nil {
fmt.Println("Recovered from panic:", r)
}
}()
// 可能触发 panic 的代码
panic("something went wrong")
}
func main() {
safeFunc()
fmt.Println("Program continues after recovery")
}
</pre></div>
<p class="maodian"><a name="_label2"></a></p><h2>三. 总结</h2>
<table><thead><tr><th>触发场景</th><th>示例</th><th>解决方法</th></tr></thead><tbody><tr><td>显式调用 panic</td><td>panic("error")</td><td>根据业务逻辑合理使用</td></tr><tr><td>数组/切片越界</td><td>arr</td><td>检查索引范围</td></tr><tr><td>空指针解引用</td><td>*ptr</td><td>检查指针是否为 nil</td></tr><tr><td>类型断言失败</td><td>i.(int)</td><td>使用 i.(type) 安全断言</td></tr><tr><td>Channel 操作错误</td><td>向已关闭的 channel 发送数据</td><td>检查 channel 状态</td></tr><tr><td>内存/资源耗尽</td><td>创建超大数据结构</td><td>优化内存使用,避免无限递归</td></tr><tr><td>并发读写 map</td><td>无锁的并发写入</td><td>使用 sync.Map 或加锁</td></tr><tr><td>其他运行时错误(除零、溢出等)</td><td>10/0、无限递归</td><td>检查输入合法性,合理设计逻辑</td></tr></tbody></table>
<p>通过合理使用 <code>defer</code> 和 <code>recover</code>,可以有效捕获 panic,避免程序崩溃。但应优先使用 <code>error</code> 类型处理可恢复的错误,仅在严重错误时使用 panic。</p>
頁:
[1]