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 |