【问题标题】:Returning an array from a function BY VALUE, and what happens when you return a struct?从函数 BY VALUE 返回一个数组,当你返回一个结构时会发生什么?
【发布时间】:2018-12-21 09:59:51
【问题描述】:

现在我正在学习 C 和 C++ 的来龙去脉。我知道当您在函数内创建一个数组时,它会存储在该函数的堆栈框架中。您可以返回数组的基地址,它实际上是指向该数组中第一个元素的指针。返回的指针值被存储到 EAX/RAX 寄存器中,然后来自寄存器的值被移动到调用函数的本地指针变量中。问题是当函数返回时,该函数的堆栈帧会从被调用的堆栈中弹出,并且在该函数的堆栈帧内声明的任何数据都会过期。指针现在指向一个无效的内存位置。

我希望能够从被调用函数中按值返回数组,而不是按指针。该数组必须在函数内部创建并存储在堆栈中。我想按值返回一个数组,就像返回一个在被调用函数中声明的 int 一样。

int f() {
   int a = 5;
   return a;  // returned by value
}

int main() {
    int b = f();
    return 0;
}

这里的 int 值被移动到 EAX/RAX 寄存器中,所以它是一个副本。被调用函数的堆栈帧从调用堆栈中清除,但没有问题,因为返回值现​​在存储在寄存器中,就在将其复制到 int b 之前。

我知道在 C++ 中我可以在被调用函数中创建一个向量,然后按值返回它。但是我不想使用这种更高级别的抽象来支持学习一种“hacky”的方式来做到这一点。稍后我会回到向量。

好吧,我意识到可以从函数中按值返回结构对象。所以我按值返回数组的解决方案非常简单:把它放在一个结构体中,然后按值返回那个结构体!

struct String {
    char array[20];
};

struct String f() {
    struct String myString;
    strcpy(myString.array, "Hello World");
    return myString;  // Is this returned by value?
}

int main() {
    struct String word = f();
    printf("%s\n", word.array);
}

如果我正确理解代码,请澄清。该结构对象在被调用函数的堆栈帧中创建,“Hello World”被复制到其中包含的数组中,然后呢?

struct String word 是一个左值,f() 返回一个 rvlaue。当一个结构被分配给另一个结构时,它的所有数据成员都会被一一复制。

在结构体从被调用函数按值返回之后和分配给main()函数内的结构体之前之间会发生什么? EAX/RAX 寄存器是返回值的目的地。它是 64 位还是 32 位,具体取决于您使用的是 64 位还是 32 位计算机。您究竟如何将结构对象放入寄存器中?我想这个数组可能不仅是 20 个字节,而且是 100 个字节!结构是否从函数中逐个复制到寄存器中?或者它是一次从堆栈上的一个内存位置按值复制到另一个内存位置?以及在被调用函数内部创建的原始结构对象会发生什么?这些都是我想知道答案的问题。

另外,关于从函数中按值返回向量。 C++ 中的向量是类,类类似于结构。你能回答这个问题,当你从一个函数中按值返回一个向量时会发生什么?当您将类/结构对象传递给函数作为参数时会发生什么?

我可以想象按值传递如何处理小数据类型。我什至不知道它如何处理复杂的数据类型和数据结构。

【问题讨论】:

  • 这是一个问题还是几个问题?请务必提出一个具体问题。
  • 本来想问一个问题的,结果太走神了!
  • 请做一个。
  • C is not C++ is not C,只选择一种语言
  • 这里有很多事情要做。您想知道是否可以在 C 中返回结构吗?在 C++ 中你想知道在 C 中执行它的汇编代码是什么吗?在 C++ 中?在特定的编译器中?想知道参数是如何复制的吗?锄头 C++ 向量被复制?缩小范围。

标签: c++ c arrays struct parameter-passing


【解决方案1】:

确切的机制取决于平台。但最常见的机制是调用者在其堆栈上为要返回的结构分配空间,并将该空间的地址作为额外参数传递,通常在所有实际参数之前。

在许多平台上,小到可以放入寄存器的结构将被返回,就好像它们是单个值一样。对于由两个 32 位 ints 组成的结构,这将适用于 x86-64,因为它们可以在单个 64 位寄存器中返回。以这种方式可以处理多大的结构因平台而异。

通过复制省略可以改善按值传递较大结构的成本。例如,如果你写

struct MyThingy blob = blobMaker();

编译器很可能会将变量blob 的地址传递给blobMaker,而不是分配一个临时变量,然后在函数返回后将临时变量复制到blob。被调用的函数也可以避免复制:

struct MyThingy blobMaker(void) {
  struct MyThingy retval;
  // ...
  retval.member1 = some_calc(42);
  // ...
  retval.member2 = "Hello";
  // ...
  return retval;

在这里,编译器可能选择不在被调用函数的堆栈帧中分配retval,而是直接使用不可见参数中传递的存储空间,从而避免在return 处进行复制。这两种优化的组合(如果可能)使返回结构几乎免费。

C++ 标准通过显式允许这些优化来提供这些优化,即使在省略的副本可能在对象的复制构造函数中触发了副作用的情况下也是如此。 (显然这种情况在 C 中是不存在的。)

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-05-29
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-06-23
    相关资源
    最近更新 更多