
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: %p\n", &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: %p\n", &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 的情况?
1 zzn 2021-11-26 23:47:20 +08:00 没有细看,盲猜是在说 例如 `for _, v := range sourceList`的 `v` 共用一块内存空间了 其实这在实现上是合理的,不然 for 循环会导致很多的内存分配。 |
2 soap520 2021-11-26 23:47:20 +08:00 for i, v := range sourceList 的 v 是 sourceList 的一份 copy 也许你想写成 rows = append(rows, Row{Id: &sourceList[i].Id, Open: &sourceList[i].Open}) |
4 tiedan 2021-11-27 01:02:02 +08:00 问题一:这就和定义了 []*Source ,for _, v := range sourceList 往里面 append &v 是一类问题。 for range 是将元素的副本复制给 v (值传递),但循环变量是复用的,地址不会变 问题二:struct 中用指针需要区分情况,比如判断参数是零值还是没传。 问题三:就是用指针,gin 框架的 issue 里面有提到,用指针来区分 0 值和参数不存在 |
5 fgwmlhdkkkw 2021-11-27 10:17:17 +08:00 via Android 是你 for 的问题。 |
6 WadeLaunch OP @zzn @tiedan 我想的也是循环时 v 分配到了同一块内存空间导致的。 @fgwmlhdkkkw 请问这种情况要怎么 “for” 才不会分配到了同一块内存空间? 我想到的就是把 v 赋值到一个临时变量再 append ,例如: ``` vv := v rows = append(rows, Row{Id: &vv.Id, Open: &vv.Open}) ``` 请问有其它方法吗? |
7 djFFFFF 2021-11-28 20:14:16 +08:00 for _, v := range sourceList { 改成 for i := range sourceList { 然后之前用 v 的地方改成用 sourceList[i] 就行了。 |
8 superfatboy 2021-12-03 11:21:26 +08:00 这不是我之前遇到的问题么 |
9 xpyusrs 2021-12-13 23:17:02 +08:00 有点迷, 我复制了你的代码 ``` sourceList.v: &v.%p: 0xc00000c0a0 &v: &{1 true} v: {1 true} &v.Id: 0xc00000c0a0 v.Id: 1 &v.Open: 0xc00000c0a8 v.Open: true sourceList.v: &v.%p: 0xc00000c0a0 &v: &{2 false} v: {2 false} &v.Id: 0xc00000c0a0 v.Id: 2 &v.Open: 0xc00000c0a8 v.Open: false rows, len [{0xc00000c0a0 0xc00000c0a8} {0xc00000c0a0 0xc00000c0a8}] 2 rows.v: &v.%p: 0xc000042260 v.Id: 0xc00000c0a0 *v.Id: 2 v.Open: 0xc00000c0a8 *v.Open: false rows.v: &v.%p: 0xc000042260 v.Id: 0xc00000c0a0 *v.Id: 2 v.Open: 0xc00000c0a8 *v.Open: false ``` go version go1.17.1 windows/amd64 |