跳至主要內容
  • Hostloc 空間訪問刷分
  • 售賣場
  • 廣告位
  • 賣站?

4563博客

全新的繁體中文 WordPress 網站
  • 首頁
  • 在 Go 的 struct 中使用指针的坑
未分類
2 1 月 2022

在 Go 的 struct 中使用指针的坑

在 Go 的 struct 中使用指针的坑

資深大佬 : WadeLaunch 23

go version: go1.17.3 linux/amd64 
package main  import (  "fmt" )  // 注意 Row 的字段是指针类型 type Row struct {  Id   *int  Open *bool }  type Source struct {  Id   int  Open bool }  func main() {  sourceList := []Source{   {Id: 1, Open: true},   {Id: 2, Open: false},  }   var rows []Row  for _, v := range sourceList {   fmt.Println("sourceList.v:")   fmt.Printf("      &v.%%p: %pn", &v)   fmt.Println("         &v:", &v)   fmt.Println("          v:", v)   fmt.Println("      &v.Id:", &v.Id)   fmt.Println("       v.Id:", v.Id)   fmt.Println("    &v.Open:", &v.Open)   fmt.Println("     v.Open:", v.Open)    rows = append(rows, Row{Id: &v.Id, Open: &v.Open})  }   fmt.Println("nrows, len", rows, len(rows))   for _, v := range rows {   fmt.Println("rows.v:")   fmt.Printf("     &v.%%p: %pn", &v)   fmt.Println("      v.Id:", v.Id)   fmt.Println("     *v.Id:", *v.Id)   fmt.Println("    v.Open:", v.Open)   fmt.Println("   *v.Open:", *v.Open)  } } 

先不看下面的答案,猜猜 rows 的结果是什么?

先不看下面的答案,猜猜 rows 的结果是什么?

先不看下面的答案,猜猜 rows 的结果是什么?

答案揭晓:

sourceList.v:       &v.%p: 0xc0000aa000          &v: &{1 true}           v: {1 true}       &v.Id: 0xc0000aa000        v.Id: 1     &v.Open: 0xc0000aa008      v.Open: true sourceList.v:       &v.%p: 0xc0000aa000          &v: &{2 false}           v: {2 false}       &v.Id: 0xc0000aa000        v.Id: 2     &v.Open: 0xc0000aa008      v.Open: false  rows, len [{0xc0000aa000 0xc0000aa008} {0xc0000aa000 0xc0000aa008}] 2 rows.v:      &v.%p: 0xc00008c240       v.Id: 0xc0000aa000      *v.Id: 2     v.Open: 0xc0000aa008    *v.Open: false rows.v:      &v.%p: 0xc00008c240       v.Id: 0xc0000aa000      *v.Id: 2     v.Open: 0xc0000aa008    *v.Open: false 

问题一:为什么 rows 两行值是一样的?(我自己有一个答案,可能是错的,先不写出来,想先问问大家的看法)

问题二:初学 Go ,请问在 struct 中用指针是不推荐的用法吗?还是我不会用?

这个用法是在某个框架中看到的,用指针可能是为了通过 if Row.Id != nil 来区分请求参数不存在(domain/path?name=x)与请求参数的值是 0 (domain/path?name=x&id=0) 的情况。

问题三:怎么区分这种参数不存在与参数值是 0 的情况?

大佬有話說 (7)

  • 資深大佬 : zzn

    没有细看,盲猜是在说 例如 `for _, v := range sourceList`的 `v` 共用一块内存空间了
    其实这在实现上是合理的,不然 for 循环会导致很多的内存分配。

  • 資深大佬 : soap520

    for i, v := range sourceList 的 v 是 sourceList 的一份 copy
    也许你想写成 rows = append(rows, Row{Id: &sourceList[i].Id, Open: &sourceList[i].Open})

  • 資深大佬 : soap520

    @soap520 * v 是 sourceList[i] 的一份 copy

  • 資深大佬 : tiedan

    问题一:这就和定义了 []*Source ,for _, v := range sourceList 往里面 append &v 是一类问题。
    for range 是将元素的副本复制给 v (值传递),但循环变量是复用的,地址不会变
    问题二:struct 中用指针需要区分情况,比如判断参数是零值还是没传。
    问题三:就是用指针,gin 框架的 issue 里面有提到,用指针来区分 0 值和参数不存在

  • 資深大佬 : fgwmlhdkkkw

    是你 for 的问题。

  • 主 資深大佬 : WadeLaunch

    @zzn @tiedan 我想的也是循环时 v 分配到了同一块内存空间导致的。
    @fgwmlhdkkkw 请问这种情况要怎么 “for” 才不会分配到了同一块内存空间?
    我想到的就是把 v 赋值到一个临时变量再 append ,例如:
    “`
    vv := v
    rows = append(rows, Row{Id: &vv.Id, Open: &vv.Open})
    “`
    请问有其它方法吗?

  • 資深大佬 : djFFFFF

    for _, v := range sourceList {
    改成
    for i := range sourceList {
    然后之前用 v 的地方改成用 sourceList[i]
    就行了。

文章導覽

上一篇文章
下一篇文章

AD

其他操作

  • 登入
  • 訂閱網站內容的資訊提供
  • 訂閱留言的資訊提供
  • WordPress.org 台灣繁體中文

51la

4563博客

全新的繁體中文 WordPress 網站
返回頂端
本站採用 WordPress 建置 | 佈景主題採用 GretaThemes 所設計的 Memory
4563博客
  • Hostloc 空間訪問刷分
  • 售賣場
  • 廣告位
  • 賣站?
在這裡新增小工具