简体   繁体   English

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

[英]Output parameter or return struct in C11?

I know C++11 has move semantics, which mean you can directly return a struct from a function and not worry about it being copied (assuming a simple struct), as opposed to writing the struct through an output parameter. 我知道C ++ 11具有移动语义,这意味着您可以直接从函数返回结构,而不必担心它会被复制(假设是简单的结构),而不是通过输出参数编写结构。

Does C11 have anything like this? C11有这样的东西吗? Or do returned structs still get copied every time? 还是返回的结构仍然每次都被复制? Are output parameters still the "best practice" here? 输出参数仍然是“最佳实践”吗?

I think there is some confusion here which should be clarified. 我认为这里有些混乱应予以澄清。 The semantics and the implementation of C++ are different. C ++的语义实现是不同的。

  • "Move" versus "copy" in C++ is just a question of which constructor (or operator= ) you are invoking. 在C ++中,“移动”与“复制”只是您要调用哪个构造函数(或operator= )的问题。

  • The question of whether the representation of the structure's members is copied is an entirely separate question. 是否复制结构成员的表示形式的问题是一个完全独立的问题。 In other words, "does the processor have to move these bytes around?" 换句话说,“处理器是否必须移动这些字节?” is not part of the language semantics. 不是语言语义的一部分。

Semantics 语义学

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

This returns using move semantics if available, but even prior to C++11, return value optimization was available. 如果可用,将使用移动语义返回,但即使在C ++ 11之前,返回值优化也是可用的。

The reason why we prefer to use move semantics is because moving an object doesn't cause a deep copy, eg, if you move a std::vector<T> you don't have to copy all of the T . 我们之所以喜欢使用移动语义的原因是因为移动对象不会引起深层复制,例如,如果移动std::vector<T> ,则不必复制所有T However, you are still copying data! 但是,您仍在复制数据! So, std::move(x) is semantically speaking a move operation (think of it as using linear rather than classical logic) but it is still implemented by copying data in memory. 因此, std::move(x)语义上讲是一个移动操作(认为它是使用线性而不是经典的逻辑),但是仍然可以通过将数据复制到内存中来实现。

Unless your ABI lets you avoid the copy. 除非您的ABI允许您避免复制。 Which brings us to... 这带我们去...

Implementation 实作

When you call a function that returns a large structure (the term "large" is relative, it might only be a few words), most ABIs will call for that structure to be passed by reference to the function. 当您调用返回大结构的函数时(术语“大”是相对的,可能只是几个词),大多数ABI都会要求通过引用该函数来传递该结构。 So when you write something like this: 因此,当您编写这样的内容时:

MyClass func() { ... }

Once you look at it in assembly, it might look something more like this: 一旦在组装中查看它,它看起来可能会像这样:

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

Of course, this is a simplification! 当然,这是一种简化! The pointer is usually implicit. 指针通常是隐式的。 But the important point is that we are already avoiding a copy, sometimes . 但是重要的一点是, 有时我们已经避免了复制。

Case study 案例分析

Here is a simple example: 这是一个简单的示例:

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];
}

When I compile this with gcc -O2 on x64, I get the following assembly output: 在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

You can see that nowhere is struct big copied. 您可以看到, 没有任何地方可以复制struct big The result from func2() simply gets placed on the stack for func1() , and then the stack gets moved so the next result gets placed elsewhere. func2()的结果只是放在func1()的堆栈上,然后堆栈被移动,因此下一个结果将放在其他位置。

However 然而

  • In common ABIs, large function results won't get threaded through multiple function stacks. 在常见的ABI中,大型函数的结果不会通过多个函数栈传递。 Returning x or y from func2() above will result in the structure being copied. 从上面的func2()返回xy将导致结构被复制。

  • But remember! 但要记住! This has nothing to do with move semantics versus copy semantics , because the structure data getting copied is just an implementation detail rather than language semantics. 这与移动语义复制语义无关,因为要复制的结构数据只是实现细节,而不是语言语义。 In C++, using std::move() may still result in the structure getting copied, it just won't invoke the copy constructor. 在C ++中,使用std::move()可能仍会导致结构被复制,只是不会调用复制构造函数。

The conclusion: returning a large structure from either C or C++ may result in copying the structure, depending on the particulars of the ABI, how the function is optimized, and the code in question. 结论:从C或C ++返回大型结构可能会导致复制结构, 具体取决于 ABI的详细信息,函数的优化方式以及所涉及的代码。 However, I wouldn't worry about it if the structure is only a few words long. 但是,只要结构只有几句话,我就不会担心。

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

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