当前位置: 首页 > 面试题库 >

结构字段中使用指针的区别

冷翼
2023-03-14
问题内容

我们可以用这种方式在golang中创建结构。下面的示例:两者之间有什么区别?

// Usual way
type Employee struct {
    firstName string    `json:"name"`
    salary    int       `json:"salary"`
    fullTime  bool      `json:"fullTime"`
    projects  []Project `json:"projects"`
}

// Un-usal way with pointers
type Employee struct {
    firstName *string    `json:"name"`
    salary    *int       `json:"salary"`
    fullTime  *bool      `json:"fullTime"`
    projects  *[]Project `json:"projects"`
}

是否有诸如内存之类的取舍?

更新:

假设以下功能:

// this function consumes MORE memory
func printEmployeeWithoutPointer(employee Employee) {
    // print here
}

// this function consumes LESS memory
func printEmployeeWithPointer(employee *Employee) {
    // print here
}

问题答案:

是的,有很多事情要考虑。首先:让我们从指针示例中明显的语法错误开始:

type Employee struct {
    FirstName *string `json:"name"`
    Salary    *int    `json:"salary"`
    FullTime  *bool   `json:"fullTime"`
}

因此,我已将星号移至该类型,并已将字段强制化。该encoding/json包使用反射来设置字段的值,因此需要将其导出。

看到您正在使用json标签时,让我们从简单的事情开始:

type Foo struct {
    Bar string  `json:"bar"`
    Foo *string `json:"foo,omitempty"`
}

当我解组没有bar价值的消息时,该Bar字段将只是一个空字符串。这使得很难确定是否发送了该字段。尤其是在处理整数时:如何区分未发送的字段与发送的值为0的字段之间的区别?
使用指针字段,并指定omitempty可以执行此操作。如果未在JSON数据中指定该字段,则结构中的字段将为nil,如果未指定:它将指向值为0的整数。

当然,必须检查指针是否为n可能很乏味,这会使代码更容易出错,因此,只有在有实际原因要区分未设置的字段时,才需要这样做。零值。

指针使您可以更改其指向的值

让我们继续探讨指针所固有的风险。假设您的Employee结构体具有指针字段,并且类型EmployeeV相同,但具有值字段,请考虑以下功能:

func (e Employee) SetName(name string) {
    if e.Firstname == nil {
        e.Firstname = &name
        return
    }
    *e.Firstname = name
}

现在,此功能仅在一半时间内起作用。您正在呼叫SetName价值接收者。如果Firstname为nil,则将指针设置在原始变量的副本上,并且变量将不会反映您在函数中所做的更改。如果Firstname
设置,但是,副本将指向相同的字符串作为你的原始变量,而指针指向的值 得到更新。那很糟。

在上实现相同的功能EmployeeV

func (e EmployeeV) SetName(name string) {
    e.Firstname = name
}

它根本不会 永远 工作。您将始终更新一个副本,并且所做的更改不会影响调用该SetName函数的变量。因此,执行类似操作的惯用方式是:

type Employee struct {
    Firstname string
    // other fields
}

func (e *Employee) SetName(name string) {
    e.Firstname = name
}

因此,我们正在更改使用指针接收器的 方法

数据竞赛

与往常一样:如果您使用指针,则实际上是在允许代码操纵直接指向的内存。鉴于golang是一种众所周知的促进并发的语言,访问相同的内存意味着您有创建数据争用的风险:

func main() {
    n := "name"
    e := Employee{
        Firstname: &n,
    }
    go func() {
         *e.Firstname = "foo"
    }()
    race(e)
}

func race(e Employee) {
    go race(e)
    go func() {
        *e.Firstname = "in routine"
    }()
    *e.Firstname = fmt.Sprintf("%d", time.Now().UnixNano())
}

Firstname可通过许多不同的例程访问此字段。最终的价值是什么?你还不知道吗 golang竞赛检测器很可能将此代码标记为潜在的数据竞赛。

就内存使用而言:int或bool之类的单个字段实际上并不是您应该担心的事情。如果您要传递一个相当大的结构,并且知道它是安全的,那么传递一个指向所述结构的指针可能是一个好主意。再说一次,通过指针访问值而不是直接访问值不是免费的:间接添加会增加少量开销。



 类似资料:
  • 我正在学习链表,以及如何在C中使用结构和指针创建链表。下面我举一个例子。据我所知,被调用的将头节点所在的结构的开始内存位置作为参数传递。push()函数的参数将结构节点作为指向指针的指针,因此它作为引用传递,而不是实际副本。因此,我们的的第一个指针只是指向头部节点的内存位置的指针,第二个指针指向该值,该值是头部节点指向的下一个内存位置。我们通过为结构节点分配一些内存,在结构节点内创建一个名为new

  • 问题内容: 我不理解以下代码的行为。在创建作为结构指针切片的匹配结构列表时,代码始终会打印原始数组的最后一个元素(实际上不是匹配项),它会打印12和12。但是,如果将匹配项更改为[]窗口小部件代替[] * Widget,然后将输出10和11。 为什么是这样? 问题答案: 那是因为当您使用指针时,您将添加到数组。 请注意,实际上这是循环中使用的局部变量,因此,这不是您要添加到数组中的地址。 (即使变

  • 我现在正在实现模拟N体问题的Barnes-Hut算法。我只想问关于建筑树的部分。 我做了两个函数来为它构建树。 我递归地构建树,并在构建时打印每个节点的数据,一切看起来都是正确的,但当程序返回到主函数时,只有树的根和根的子节点存储值。其他节点的值没有被存储,这很奇怪,因为我在递归过程中打印了它们,它们应该被存储。 这是经过修改的代码的一部分,我认为问题可能在哪里: 下面是函数set_root_an

  • Go 语言中指向结构体的指针和 C 语言一样 结构体和指针 创建结构体指针变量有两种方式 package main import "fmt" type Student struct { name string age int } func main() { // 创建时利用取地址符号获取结构体变量地址 var p1 = &Student{"lnj", 33}

  • 本文向大家介绍详解C++中的指针结构体数组以及指向结构体变量的指针,包括了详解C++中的指针结构体数组以及指向结构体变量的指针的使用技巧和注意事项,需要的朋友参考一下 C++结构体数组 一个结构体变量中可以存放一组数据(如一个学生的学号、姓名、成绩等数据)。如果有10个学生的数据需要参加运算,显然应该用数组,这就是结构体数组。结构体数组与以前介绍过的数值型数组的不同之处在于:每个数组元素都是一个结

  • 问题内容: Go Newbie问题:我正在尝试使用默认值初始化以下结构。我知道,如果“ Uri”是字符串而不是指向字符串(* string)的指针,则可以使用。但是我需要这个指针来比较结构的两个实例,如果未设置Uri,则Uri将为nil,例如,当我从json文件解组内容时。但是,如何正确地将这样的结构初始化为“静态默认值”呢? 上面的代码因 问题答案: 无法获得(指向)常数值的地址,这就是初始化失