Golang新手入门:数组、可变参数函数和指针的使用指南

Golang新手入门:数组、可变参数函数和指针的使用指南 大家好,

我是Go语言的新手,目前刚了解到硬拷贝值在某些情况下是不好的。这里有一个小例子,我想知道:

  1. 为什么我必须在代码中标出的位置使用 *(解引用)?
  2. 我在这里尝试实现的方式是否高效,或者是否有更好的方法(消耗更少的内存)来达到相同的结果?
type SomeObject struct {
    a string
    b string
    c string
}

type SomeInterface interface {
    setup(name string, unknownAmountOfObjects ...*SomeObject)
}

type InterfaceTest struct {
    ExaxtAmountOfObjects *[3]SomeObject
    name string
}

// 通过实现此方法来实现接口
func (interfacetest *InterfaceTest) setup(name string, somename ...*SomeObject) {
    interfacetest.name = name
    
    for i, value := range rotors {
        // 首先检查大小
        if i < len(interfacetest.ExaxtAmountOfObjects) {
            // !!!! 为什么我需要在这里解引用??!
            interfacetest.ExaxtAmountOfObjects[i] = *value
        }
    }
}

提前感谢您的帮助和建议。


更多关于Golang新手入门:数组、可变参数函数和指针的使用指南的实战教程也可以访问 https://www.itying.com/category-94-b0.html

4 回复

1.) @kync 谢谢,我有点把可变参数类型和某种数组搞混了。但你是对的,它不是 ;) 谢谢

2.) @lutzhorn “rotors” 是个笔误…… 那里当然应该是指 SomeObject

我的错误还在于这两者之间存在区别:

*[3]Object

and

[3]*Object

更多关于Golang新手入门:数组、可变参数函数和指针的使用指南的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html


这行代码

for i, value := range rotors {

遍历了 rotors,但在这个代码片段的其他地方都没有提到它。所以我们不知道 value 是什么。它与 SomeInterface.setup(...) 签名中的 somename 无关。rotors 是什么?

  1. 为什么我必须在代码中标记的位置使用 *(解引用)?

ExaxtAmountOfObjects *[3]SomeObject 持有一个 SomeObject 的值,但你传递给函数的是指向对象的指针(*SomeObject),这就是你需要解引用的原因。

我在这里尝试实现的方式是否高效?或者是否有更好的选择来实现相同的结果(同时消耗更少的内存)?

ExaxtAmountOfObjects 是一个数组,这意味着它是固定大小的。setup 是一个可变参数函数,你可以传递任意数量的参数。如果我在没有看到其实现的情况下看到你的函数,我会期望向函数传递 4 个项目,并期望数组中的所有元素都被处理。

这是一个很好的问题,涉及到Go语言中指针、数组和切片的核心概念。让我们直接解答你的疑问。

1. 为什么需要解引用(*)?

在你的代码中,ExaxtAmountOfObjects 是一个指向数组的指针:*[3]SomeObject。这意味着 interfacetest.ExaxtAmountOfObjects[i] 已经是在访问数组的元素了。

然而,somename 参数是 ...*SomeObject,这是一个指向 SomeObject 的指针的切片。所以 value 的类型是 *SomeObject(指针),而 interfacetest.ExaxtAmountOfObjects[i] 的类型是 SomeObject(值类型)。

你需要解引用 *value 来获取指针指向的实际 SomeObject 值,然后将其赋值给数组元素。

示例说明:

// 假设 value 是一个指针:*SomeObject
var value *SomeObject = &SomeObject{a: "test"}

// 这行代码会报类型错误:
// interfacetest.ExaxtAmountOfObjects[i] = value
// 错误:不能将 *SomeObject 赋值给 SomeObject

// 正确的做法是解引用:
interfacetest.ExaxtAmountOfObjects[i] = *value
// 现在是把 SomeObject 值赋值给 SomeObject

2. 更高效的内存使用方式

你当前的实现确实有内存拷贝,因为解引用 *value 会创建值的副本。如果你想要避免拷贝并直接使用原始对象,可以修改设计:

方案A:存储指针而不是值

type InterfaceTest struct {
    // 改为存储指针数组
    ExaxtAmountOfObjects *[3]*SomeObject  // 注意这里的改变
    name string
}

func (interfacetest *InterfaceTest) setup(name string, somename ...*SomeObject) {
    interfacetest.name = name
    
    for i, value := range somename {
        if i < len(interfacetest.ExaxtAmountOfObjects) {
            // 现在直接存储指针,无需解引用
            interfacetest.ExaxtAmountOfObjects[i] = value
        }
    }
}

// 使用示例:
obj1 := &SomeObject{a: "a1", b: "b1", c: "c1"}
obj2 := &SomeObject{a: "a2", b: "b2", c: "c2"}
obj3 := &SomeObject{a: "a3", b: "b3", c: "c3"}

test := &InterfaceTest{
    ExaxtAmountOfObjects: &[3]*SomeObject{},
}
test.setup("test", obj1, obj2, obj3)

方案B:使用切片而不是数组

type InterfaceTest struct {
    // 使用切片,更灵活
    Objects []*SomeObject  // 切片而不是固定数组
    name string
}

func (interfacetest *InterfaceTest) setup(name string, somename ...*SomeObject) {
    interfacetest.name = name
    interfacetest.Objects = somename  // 直接赋值,无拷贝
}

// 或者如果你需要限制数量:
func (interfacetest *InterfaceTest) setupLimited(name string, somename ...*SomeObject) {
    interfacetest.name = name
    max := 3
    if len(somename) < max {
        max = len(somename)
    }
    interfacetest.Objects = make([]*SomeObject, max)
    for i := 0; i < max; i++ {
        interfacetest.Objects[i] = somename[i]  // 仅复制指针
    }
}

方案C:如果你想完全避免指针

type InterfaceTest struct {
    ExaxtAmountOfObjects [3]SomeObject
    name string
}

func (interfacetest *InterfaceTest) setup(name string, somename ...SomeObject) {  // 注意:不是指针
    interfacetest.name = name
    
    for i, value := range somename {
        if i < len(interfacetest.ExaxtAmountOfObjects) {
            interfacetest.ExaxtAmountOfObjects[i] = value  // 值拷贝
        }
    }
}

// 使用示例:
test := &InterfaceTest{}
test.setup("test", 
    SomeObject{a: "a1", b: "b1", c: "c1"},
    SomeObject{a: "a2", b: "b2", c: "c2"},
)

性能考虑:

  • 存储指针:内存占用小(仅指针大小),修改原始对象会影响存储的对象
  • 存储值:有拷贝开销,但数据独立,修改安全
  • 切片 vs 数组:切片更灵活,数组大小固定但可能有轻微性能优势

选择哪种方案取决于你的具体需求:是否需要修改原始对象、数据大小、性能要求等。

回到顶部