V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
jiekeop
V2EX  ›  程序员

新人求教一个 Go 语言问题

  •  
  •   jiekeop · 2023-06-19 10:29:35 +08:00 · 1211 次点击
    这是一个创建于 510 天前的主题,其中的信息可能已经有所发展或是发生改变。
    package main
    
    import "fmt"
    
    type MyService struct {
    	Name string
    }
    
    func (s *MyService) PName() {
    	fmt.Printf("name1 	%s\n", s.Name)
    	s.Name = "zhangsan"
    	fmt.Printf("name2 	%s\n", s.Name)
    
    }
    
    func main() {
    	var service MyService
    	service.PName()
    	fmt.Printf("name3 	%s\n", service.Name)
    }
    ///output
    name1   
    name2   zhangsan
    name3   zhangsan
    

    代码很简单,无法理解的是 var service MyService 。根据我目前看的学习文档,这个只是声明了一个变量,其类型为 MyService ,而没有绑定到任何内存区域,为什么后续的方法调用没出现空指针报错呢.赋值 Name 也成功了。百思不得其解

    zxdstyle
        1
    zxdstyle  
       2023-06-19 10:37:28 +08:00   ❤️ 1
    renyijiu
        2
    renyijiu  
       2023-06-19 10:38:15 +08:00   ❤️ 1
    var 定义之后没有赋值,默认是一个 zero value, 其中 name 字段是 string ,所以是个空字符串。
    serco
        3
    serco  
       2023-06-19 10:38:16 +08:00
    没用过 go ,随便看了一下,你说的这句实际上已经是实例化了一个 struct

    看这个的案例 https://www.tutorialsteacher.com/go/struct#:~:text=In%20Go%20lang%2C%20struct%20(structure,the%20type%20and%20struct%20keywords.

    You can declare an instance and assign values later on using a dot, as shown below.

    ```
    var s4 Student
    s4.id = 4
    s4.name = "Abdul"
    s4.grade = 8

    fmt.Println(s4) //output: {4 Abdul 8}

    var s5 Student
    s5.name = "Sachin"

    fmt.Println(s5) //output: {0 Sachin 0}
    ```
    NX2023
        4
    NX2023  
       2023-06-19 10:41:39 +08:00
    在我的印象里声明一个变量的时候都会自动初始化为零值,完全不要操心
    Nazz
        5
    Nazz  
       2023-06-19 10:42:04 +08:00
    go 有空值安全特性
    dobelee
        6
    dobelee  
       2023-06-19 10:44:55 +08:00   ❤️ 1
    这个不是指针类型。下面这个才是。你的写法是初始化零值。
    var service *MyService
    moqimoqide
        7
    moqimoqide  
       2023-06-19 10:45:29 +08:00
    帮问了一下 gpt:

    在 Go 语言中,如果结构体类型的实例被声明为值类型变量(而不是指针类型变量)时,在调用该结构体类型的方法时,会自动进行值拷贝( pass by value )。换言之,值类型变量作为一个参数传递给方法时,方法会获取该值类型变量的一份拷贝,当方法需要修改该结构体类型实例的值时,它修改的只是拷贝中的相应值,而不是原来的变量。所以调用顺序如下:

    1. 执行 service.PName() 方法时,会将 MyService 类型的 service 的一份拷贝传递给 PName() 方法。

    2. 在 PName() 方法中,首先打印出 s.Name 的值,由于此时 Name 字段还没有被初始化,因此打印出的结果为空字符串。

    3. 接下来,将 s.Name 的值更改为 "zhangsan"。

    4. 继而,打印出 s.Name 的值,此时 Name 字段的值已经被更改为 "zhangsan",因此打印出的结果为 "zhangsan"。

    5. PName() 方法执行结束,返回到 main() 函数中。

    6. 在 main() 函数中,打印出 service.Name 的值,此时打印出的是 "zhangsan"。

    因此,在你的代码中,var service MyService 声明了一个值类型的 MyService 变量,该结构体实例会被分配内存空间并初始化(其中包括 Name 字段),但在调用 PName() 方法时,会将 service 的一份拷贝传递给该方法,因此执行 PName() 方法会修改拷贝中的 Name 字段的值,但不会影响原来的 service 变量。
    buffzty
        8
    buffzty  
       2023-06-19 10:51:23 +08:00   ❤️ 1
    var service MyService
    等于 var service = MyService{}
    等于 service:= MyService{}
    jiekeop
        9
    jiekeop  
    OP
       2023-06-19 10:54:06 +08:00
    谢谢各位解答,明白了
    LeegoYih
        10
    LeegoYih  
       2023-06-19 10:58:25 +08:00
    Go 反直觉的地方多了去了,学 Go 不能代入其他语言的思维。
    无忌,你忘干净了吗?
    lilei2023
        11
    lilei2023  
       2023-06-19 11:28:52 +08:00
    这个我刚学也比较懵
    Cola98
        12
    Cola98  
       2023-06-19 13:31:06 +08:00
    因为这是一个浅拷贝,第一个 name1 没有赋值,所以就给了默认值就是"",接着你 name2 为 zhangsan ,name3 会接着用之前的值
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   2643 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 33ms · UTC 06:03 · PVG 14:03 · LAX 22:03 · JFK 01:03
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.