繁体   English   中英

在C11中输出参数还是返回struct?

[英]Output parameter or return struct in C11?

我知道C ++ 11具有移动语义,这意味着您可以直接从函数返回结构,而不必担心它会被复制(假设是简单的结构),而不是通过输出参数编写结构。

C11有这样的东西吗? 还是返回的结构仍然每次都被复制? 输出参数仍然是“最佳实践”吗?

我认为这里有些混乱应予以澄清。 C ++的语义实现是不同的。

  • 在C ++中,“移动”与“复制”只是您要调用哪个构造函数(或operator= )的问题。

  • 是否复制结构成员的表示形式的问题是一个完全独立的问题。 换句话说,“处理器是否必须移动这些字节?” 不是语言语义的一部分。

语义学

MyClass func() {
    MyClass x;
    x.method(...);
    ...
    return x;
}

如果可用,将使用移动语义返回,但即使在C ++ 11之前,返回值优化也是可用的。

我们之所以喜欢使用移动语义的原因是因为移动对象不会引起深层复制,例如,如果移动std::vector<T> ,则不必复制所有T 但是,您仍在复制数据! 因此, std::move(x)语义上讲是一个移动操作(认为它是使用线性而不是经典的逻辑),但是仍然可以通过将数据复制到内存中来实现。

除非您的ABI允许您避免复制。 这带我们去...

实作

当您调用返回大结构的函数时(术语“大”是相对的,可能只是几个词),大多数ABI都会要求通过引用该函数来传递该结构。 因此,当您编写这样的内容时:

MyClass func() { ... }

一旦在组装中查看它,它看起来可能会像这样:

void func(MyClass *ptr) { ... }

当然,这是一种简化! 指针通常是隐式的。 但是重要的一点是, 有时我们已经避免了复制。

案例分析

这是一个简单的示例:

struct big {
    int x[100];
};

struct big func1(void);

int func2() {
    struct big x = func1();
    struct big y = func1();
    return x.x[0] + y.x[0];
}

在x64上使用gcc -O2进行编译时,得到以下程序集输出:

    subq    $808, %rsp
    movq    %rsp, %rdi
    call    func1
    leaq    400(%rsp), %rdi
    call    func1
    movl    400(%rsp), %eax
    addl    (%rsp), %eax
    addq    $808, %rsp
    ret

您可以看到, 没有任何地方可以复制struct big func2()的结果只是放在func1()的堆栈上,然后堆栈被移动,因此下一个结果将放在其他位置。

然而

  • 在常见的ABI中,大型函数的结果不会通过多个函数栈传递。 从上面的func2()返回xy将导致结构被复制。

  • 但要记住! 这与移动语义复制语义无关,因为要复制的结构数据只是实现细节,而不是语言语义。 在C ++中,使用std::move()可能仍会导致结构被复制,只是不会调用复制构造函数。

结论:从C或C ++返回大型结构可能会导致复制结构, 具体取决于 ABI的详细信息,函数的优化方式以及所涉及的代码。 但是,只要结构只有几句话,我就不会担心。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM