V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
The Go Programming Language
http://golang.org/
Go Playground
Go Projects
Revel Web Framework
whitehack
V2EX  ›  Go 编程语言

求大佬们看一下,通过指针强行转换类型,运行时会不会被教育.

  •  
  •   whitehack ·
    whtiehack · 2023-02-02 14:34:54 +08:00 · 972 次点击
    这是一个创建于 666 天前的主题,其中的信息可能已经有所发展或是发生改变。

    有个非常大的结构而且是数组. 走 grpc 返回结果要一个一个赋值

    想直接通过指针转换过来, 看起来可行 但是怕运行时被教育

    https://go.dev/play/p/v5vJ53sXiEs

    // You can edit this code!
    // Click here and start typing.
    package main
    
    import (
    	"fmt"
    	"sync"
    	"unsafe"
    )
    
    // NoUnkeyedLiterals can be embedded in a struct to prevent unkeyed literals.
    type NoUnkeyedLiterals struct{}
    
    // DoNotCompare can be embedded in a struct to prevent comparability.
    type DoNotCompare [0]func()
    
    // DoNotCopy can be embedded in a struct to help prevent shallow copies.
    // This does not rely on a Go language feature, but rather a special case
    // within the vet checker.
    //
    // See https://golang.org/issues/8005.
    type DoNotCopy [0]sync.Mutex
    
    // Requirements:
    //   - The type M must implement protoreflect.ProtoMessage.
    //   - The address of m must not be nil.
    //   - The address of m and the address of m.state must be equal,
    //     even though they are different Go types.
    type MessageState struct {
    	NoUnkeyedLiterals
    	DoNotCompare
    	DoNotCopy
    
    	// atomicMessageInfo *MessageInfo
    }
    
    type (
    	UnknownFields  = unknownFieldsA // TODO: switch to unknownFieldsB
    	unknownFieldsA = []byte
    )
    
    type JuDianUpdateTeamReq struct {
    	state         MessageState
    	sizeCache     int32
    	unknownFields UnknownFields
    
    	Id      string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"`
    	CurHp   uint64 `protobuf:"varint,2,opt,name=curHp,proto3" json:"curHp,omitempty"`
    	TotalHp uint64 `protobuf:"varint,3,opt,name=totalHp,proto3" json:"totalHp,omitempty"`
    }
    
    type TeamData struct {
    	Id      string `json:"id,omitempty"`
    	CurHp   uint64 `json:"curHp,omitempty"`
    	TotalHp uint64 `json:"totalHp,omitempty"`
    }
    
    func main() {
    	juDianUpdateTeamReq := &JuDianUpdateTeamReq{
    		Id:      "team1",
    		CurHp:   100,
    		TotalHp: 200,
    	}
    	teamData := (*TeamData)(unsafe.Pointer(&juDianUpdateTeamReq.Id))
    	fmt.Println("teamData", teamData)
    }
    
    
    16 条回复    2023-02-08 18:27:33 +08:00
    yicixin
        1
    yicixin  
       2023-02-03 17:18:30 +08:00
    就这个例子来讲,直接指针转换可以省去内存分配和拷贝,写法也更方便,但是要注意`juDianUpdateTeamReq`和`teamData `指向同一片内存了,它们是会相互影响的,小心不要出现内存竞争。
    whitehack
        2
    whitehack  
    OP
       2023-02-03 18:54:34 +08:00
    @yicixin #1 感谢回复. `juDianUpdateTeamReq`这个是 grpc 返回的,所以不用担心内存竟争

    teamData 是否有被垃圾回收的风险?

    https://go.dev/play/p/q3gwp2mERvj

    这边有个详细一点的例子.
    leonshaw
        3
    leonshaw  
       2023-02-04 10:57:28 +08:00
    肯定是未定义行为,但是实践上应该没太大问题。
    lysS
        4
    lysS  
       2023-02-06 15:17:15 +08:00
    你既然有内存中的 juDianUpdateTeamReq 了,可以对它任意读写,转换成 TeamData 有啥意义呢?
    lysS
        5
    lysS  
       2023-02-06 15:20:37 +08:00
    不知大直接这样定义行不行 https://go.dev/play/p/phb2AxTdvyN
    whitehack
        6
    whitehack  
    OP
       2023-02-06 16:21:38 +08:00
    @lysS #4 因为要通过另一套结构返回给别的接口. 那个接口定义的 TeamData


    @lysS #5 这样不满足我的需求. grpc 的结构是通过 proto 自动生成的. teamdata 结构也是自动生成的 是两套系统
    xuyang2
        7
    xuyang2  
       2023-02-08 11:22:45 +08:00
    对内存细节没理解清楚的话,不要乱用 unsafe
    xuyang2
        8
    xuyang2  
       2023-02-08 11:25:21 +08:00
    为什么不用最简单的写法呢

    ```
    teamData := TeamData{
    Id: juDianUpdateTeamReq.Id,
    // CurHp: 0,
    // TotalHp: 0,
    }
    fmt.Println("teamData", &teamData)
    ```
    xuyang2
        9
    xuyang2  
       2023-02-08 11:25:43 +08:00
    string 本身就是个 fat pointer
    whitehack
        10
    whitehack  
    OP
       2023-02-08 13:52:27 +08:00
    @xuyang2 #8 是这样的,这个只是示例结构.实际结构 都是数组,而且字段非常多.

    所以用 unsafe 是为了减少内存分配.并且提高性能.
    xuyang2
        11
    xuyang2  
       2023-02-08 15:40:19 +08:00
    - 过早优化是万恶之源
    - 不要滥用指针 包括指针数组 减轻 GC 的负担
    - 你计算过这个 struct 的字节大小吗
    whitehack
        12
    whitehack  
    OP
       2023-02-08 16:21:03 +08:00
    @xuyang2 #11 > 过早优化是万恶之源

    大佬说的太对了. 只是一个一个字段的赋值也是很麻

    指针数组倒是没有用,.另外这 proto-go-gen 生成的结构都是指针,在 issue 查了一下,说是就是这样设计的.


    一个 struct 大概 10-30 个左右的字段吧
    xuyang2
        13
    xuyang2  
       2023-02-08 16:25:30 +08:00
    一个 struct 大概 10-30 个左右的字段
    常见的 数字 string slice map 类型,都很小
    这样一个 struct 也就几十 /上百字节

    slice []MyStruct 本身也是胖指针,没记个字节
    数组 [n]MyStruct 的大小是 n * MyStruct 大小

    这些在栈上传递都很快的
    xuyang2
        14
    xuyang2  
       2023-02-08 16:33:23 +08:00
    自己用 unsafe 处理指针,全靠自己记住内存布局,编译器帮不上忙

    如果源 struct 后面有调整,旧代码也不会报错,可能会得到莫名其妙的结果
    而且这种代码搜索和修改也很麻烦
    whitehack
        15
    whitehack  
    OP
       2023-02-08 17:46:25 +08:00
    @xuyang2 #14

    写了个结构体大小的比较.计算不对直接 panic, 后面的事后面再说 哈哈
    xuyang2
        16
    xuyang2  
       2023-02-08 18:27:33 +08:00
    不能只比较大小的

    瞎玩 unsafe 是作死行为
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   1203 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 26ms · UTC 23:53 · PVG 07:53 · LAX 15:53 · JFK 18:53
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.