遇到一段无法理解的代码,求解惑!

go版本 1.22.0

package main

import (
    "fmt"
    "unsafe"
)

func main() {
    var a int64 = 1
    var b int64 = 1
    fmt.Println(&a, &b)
    NilIsNil(a)
    var d = make([]int, 10)
    var c int64 = 1
    NilIsNil2(b)
    fmt.Println(a, b, c, d)

    var e int64 = 1

    // 打开行代码fmt.Println(&a)输出 10 10 10 [0 0 0 0 0 0 0 0 0 0] 10
    // 注释fmt.Println(&a)输出 1 1 1 [0 0 0 0 0 0 0 0 0 0] 1
    fmt.Println(a, b, c, d, e)
}

func NilIsNil(a any) bool {
    // 打开行代码输出 10 10 10 [0 0 0 0 0 0 0 0 0 0] 10
    // 注释输出 1 1 1 [0 0 0 0 0 0 0 0 0 0] 1
    fmt.Println(&a)

    type rtype struct {
        t    unsafe.Pointer
        data unsafe.Pointer
    }
    t := (*rtype)(unsafe.Pointer(&a))
    *(*int)(t.data) = 10
    return a == nil
}

func NilIsNil2(a any) bool {
    type rtype struct {
        t    unsafe.Pointer
        data unsafe.Pointer
    }
    t := (*rtype)(unsafe.Pointer(&a))

    fmt.Println("val = ", *(*int)(t.data))
    return a == nil
}

在一个群里看见的一段代码,发代码的人说是因为golang缓存了128以下的数字,然后这段代码修改的缓存。但是还是有一些地方不理解。

  1. 为什么fmt.Println(&a)这个行代码会影响输出结果,这行代码不就是一个打印地址的吗?
  2. golang真的会缓存128以下的数字吗,内部是如何实现的?
  3. type rtype struct {
         t    unsafe.Pointer
         data unsafe.Pointer
     }
    这个结构体有什么含义吗
本作品采用《CC 协议》,转载必须注明作者和本文链接
讨论数量: 14

GPT的回答不知道对不对:

file

file

Go

Go

1周前 评论
don178 (楼主) 1周前

不是a的问题,不知道为啥只要这里面有输出语句就会输出10

func NilIsNil(a any) bool {
    // 打开行代码输出 10 10 10 [0 0 0 0 0 0 0 0 0 0] 10
    // 注释输出 1 1 1 [0 0 0 0 0 0 0 0 0 0] 1
    //fmt.Println(&a)
    //fmt.Println(a)
    type rtype struct {
        t    unsafe.Pointer
        data unsafe.Pointer
    }
    out := bufio.NewWriter(os.Stdout)
    defer out.Flush()
    t := (*rtype)(unsafe.Pointer(&a))
    fmt.Fprintln(out, t)
    //fmt.Println(&t)
    //fmt.Println(t)
    *(*int)(t.data) = 10
    //fmt.Println(&t)
    //fmt.Println(t)
    //fmt.Println(a)
    return a == nil
}
1周前 评论
don178 (楼主) 1周前

猜测使用了NilIsNil 方法修改值后可能需要调用一下a或者&a才能生效
在只输出普通的字符串时不输出a,a的结果是1,在输出了a之后结果变成了10

file

file

这种也会修改a的值

Go

1周前 评论

file

造成这种现象有2个原因:

1.fmt.Println的问题,打印变量时,变量会转为接口变量

2.接口类型的元数据类型有两个指针,其中data的取值调用了runtime.convT64(),convT64()中所有小于256数值有同一个staticuint64s的数组,可以认为是一种缓存,主要是为了避免非空接口数值类型频繁的堆分配,staticuint64s数组下标等于值,而NilIsNil()通过unsafe把staticuint64s的数组下标为1的空间指向的值改成了10,会导致其他变量转接口变量赋值时下标为1的指向空间的值都是10。

接口类型有个规定是存储在接口的值类型是不可寻址的,而用了unsafe.Pointer强制修改就会导致这种现象

file

1周前 评论
迷途的羔羊 (作者) 1周前
迷途的羔羊 (作者) 1周前
don178 (楼主) 1周前
don178 (楼主) 1周前
迷途的羔羊 (作者) 1周前

comate.baidu.com/?inviteCode=1k1z4... 装一个 comate 插件,然后直接问插件,帮你问是这样的结果

file

1周前 评论
don178 (楼主) 6天前

讨论应以学习和精进为目的。请勿发布不友善或者负能量的内容,与人为善,比聪明更重要!