在C++中通过引用传递结构体数组

3
所以,我对编程/C ++仍然很陌生,仍在努力理解指针、按引用传递等方面的知识。现在,我正在尝试弄清楚一个程序,需要将结构数组传递给另一个函数。我已经通过直接将数组传递给那里来使它工作。看起来一切都正常。然而,我担心的是,我认为我是按值传递的,我知道最好是通过引用传递结构体,这样就不必每次都复制结构体了... 无论如何,这是我正在做的基本示例:
struct GoldenHelmet {
    int foo;
    string bar;
    };

void pass (GoldenHelmet ofMambrino[], int size);

int main () {
    GoldenHelmet ofMambrino[10];
    int size = sizeof(ofMambrino) / sizeof(ofMambrino[0]);
    ofMambrino[1].foo = 1;
    pass(ofMambrino, size);
    cout << ofMambrino[2].foo << endl;
    return 0;
}

void pass (GoldenHelmet ofMambrino[], int size) {
    ofMambrino[2].foo = 100;
    ofMambrino[2].bar = "Blargh";
}

据我所知,它之所以有效是因为数组已经是指针,对吧?但是,按照我配置的方式,我仍然会将结构体和所有内容的副本传递给 pass() 函数吗?我尝试通过引用传递,但无论我如何尝试,它似乎都不想工作。


4
数组不是指针。反复对自己说这句话,直到你晕倒,然后回到这个网站上阅读那时可能已经发布的答案。 - Kerrek SB
3
不,答案将用C++编写 :) - Sergey Kalinichenko
@Kerrek SB,感谢您的澄清。我正在阅读这篇帖子,它似乎暗示数组是指针。我想我需要继续研究一下。无论如何,现在要去阅读回复了,感谢大家的帮助。 - Nate
@Nate:与其读那个,你应该读一篇文章,证明你必须把所有的钱都给我……无论如何,不要相信网络上听到的一切。如果有疑问,最好拿起一本好书,或者在语言标准(从GitHub免费获取)中查找,一旦你对这门语言足够熟悉。 - Kerrek SB
1
@Nate:个人意见:如果你刚开始学习C++,你根本不应该涉及指针。这些应该留到以后,当你实现自己的内存管理、未格式化的I/O或历史奇怪问题时再去讨论。 - Kerrek SB
显示剩余2条评论
5个回答

7

C++方式:

#include <array>

typedef std::array<GoldenHelmet, 10> Helmets;

void pass(Helmets &);

int main()
{
   Helmets h;
   h[1].foo = 1;
   pass(h);
   //...
}

void pass(Helmets & h)
{
   h[2].foo = 100;
   // ...
}

实际上,我们通过引用传递数组。


5
这个语法:
void pass (GoldenHelmet ofMambrino[], int size)

这其实相当令人困惑。因为你传递的不是一个数组,而是一个指针。虽然它们不是同一种东西,但不要混淆。这种奇怪的情况只适用于函数参数。上面的代码与下面的代码完全相同:

void pass (GoldenHelmet * ofMambrino, int size)

实际上,除非它是另一个对象的子对象,否则无法通过值传递数组。你可以通过引用传递它们,但需要包括大小,不过你可以使用模板来完成:

template<int N>
void pass (GoldenHelmet (&ofMambrino)[N])

1
为什么我们需要模板?void pass(GoldenHelmet (&h)[10])有什么问题吗? - Kerrek SB
@Kerrek:我没有说你需要一个模板。我说“你需要包含大小,但你可以使用一个模板来完成”。你选择明确说明它[大小],而我选择使用一个模板。 - Benjamin Lindley
1
@Benjamin 谢谢,现在开始有点明白了。所以如果我传递的是指针而不是整个数组,我真的需要担心通过引用传递吗?如果我只是传递结构体本身,我可以看到通过引用传递的好处。但是如果我只是传递指针,那么通过引用传递真的有任何好处吗? - Nate
@Nate:1)调用语法更简单。无需计算大小,只需传递数组即可。模板类型推导会为您计算出大小。2)很难出现大小错误。3)很难传递无效参数。(例如,您不能只传递NULL) - Benjamin Lindley

4

这些都是可能的,但它们都不是按值传递。只需要将ofMambrino想象成数组开头的地址,这就是你所传递的。

void pass (GoldenHelmet ofMambrino[], int size)
void pass (GoldenHelmet ofMambrino[10], int size)
void pass (GoldenHelmet *ofMambrino, int size)
void pass (GoldenHelmet (&ofMambrino)[10], int size)

太好了,这解释清楚了很多。这也解释了为什么我需要传递大小,因为如果我只是指向数组开头的地址,我需要跟踪数组的长度。 - Nate
@Nate:没错,我刚开始学的时候总是想知道为什么没有人告诉我“它只是一个地址”。 - Jesse Good

1

数组被表示并作为指针传递,因此在这里不会复制任何内容。相反,如果您要传递一个单一的struct,它将按值传递。

以下是一个代码片段,用于说明这个最后一点:

void passByVal (GoldenHelmet ofMambrino) {
    ofMambrino.foo = 100;
    ofMambrino.bar = "Blargh";
}

void passByRef (GoldenHelmet& ofMambrino) {
    ofMambrino.foo = 100;
    ofMambrino.bar = "Blargh";
}

int main() {
    GoldenHelmet h;
    passByVal(h); // h does not change
    passByRef(h); // fields of h get assigned in the call
}

0
首先,数组不是指针。我们在参数列表中将其称为指针,因为当我们使用它们作为函数参数时,它们会自动转换为指向数组的指针。
int x[ ]

x实际上是指向数组开头的const指针。当您将其传递给函数时,您发送的是数组开头的内存地址。这就是为什么当您在函数中进行更改时,实际上是在调用者部分的变量地址进行更改。这实际上是通过引用模拟的调用而不是通过引用调用。但是,由于您正在处理内存位置,因此效果与通过引用调用相同。因此,当您发送结构体数组时,实际上是传递了结构体数组的地址。这就是为什么当您更改此值时,实际上是更改了您的结构体。

要使用通过引用调用,您必须做的一件事是定义函数原型,如下所示:

void f(int &param)

当调用函数时,它与其他函数相同。

总结:

int main()
{
     int x;

     // simulated call by reference that use adress of variable, 
     // lets say adress of x is 19ff 
     f(&x);     // actually you send 19ff

     f(x);      // call by reference that use reference of variable

}

// simulated call by reference
void f(const int *y)
{
    // when you use like *y=10, you are writing on memory area 19ff, you actually 
    // change memory area that is belong to x in the main

}

// call by reference
void f(const int &y)
{
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接