C:何时按值返回或传递引用

时间:2022-09-10 23:22:41

Although the subject is discussed many times, I haven't found any satisfying answer so far. When to return data from a function by return or to pass a reference to change the data on address? The classic answer is to pass a variable as reference to a function when it becomes large (to avoid stack copying). This looks true for anything like a structure or array. However returning a pointer from a function is not uncommon. In fact some functions from the C library to the exact thing. For example:

虽然这个主题已经多次讨论过,但到目前为止我还没有找到任何令人满意的答案。何时通过返回从函数返回数据或传递引用以更改地址上的数据?经典的答案是在变量变大时将变量作为函数的引用传递(以避免堆栈复制)。这看起来像结构或数组。但是,从函数返回指针并不罕见。实际上有些函数从C库到确切的东西。例如:

char *strcat(char *dst, const char *src);

Always returns a pointer to destination even in case of an error. In this case we can just use the passed variable and leave the return for what it is (as most do).

即使出现错误,也始终返回指向目标的指针。在这种情况下,我们可以只使用传递的变量并保留返回值(与大多数情况一样)。

When looking at structures I see the same thing happening. I often return pointers when functions only need to be used in variable initialization.

在看结构时,我发现同样的事情正在发生。当函数只需要在变量初始化中使用时,我经常返回指针。

char *p = func(int i, const char *s);

Then there is the argument that stack coping variables is expensive, and so to use pointers instead. But as mentioned here some compilers are able to decide this themselves (assuming this goes for C as well). Is there a general rule, or at least some unwritten convention when to use one or the other? I value performance above design.

再有就是堆栈变量应对价格昂贵,所以改用指针参数。但正如这里提到的,一些编译器能够自己决定(假设这也适用于C)。是否有一般规则,或者至少有一些不成文的惯例何时使用其中一个?我重视高于设计的性能。

2 个解决方案

#1


12  

Start by deciding which approach makes the most sense at the logical level, irrespective of what you think the performance implications might be. If returning a struct by value most clearly conveys the intent of the code, then do that.

首先,决定哪种方法在逻辑层面最有意义,而不管您认为性能影响可能是什么。如果按值返回结构最清楚地表达了代码的意图,那么就这样做。

This isn't the 1980s anymore. Compilers have gotten a lot smarter since then and do a really good job of optimizing code, especially code that's written in a clear, straightforward manner. Similarly, parameter passing and value return conventions have become fairly sophisticated as well. The simplistic stack-based model doesn't really reflect the reality of modern hardware.

这不再是20世纪80年代了。从那时起,编译器变得更加智能,并且在优化代码方面做得非常好,特别是以清晰,直接的方式编写的代码。类似地,参数传递和值返回约定也变得相当复杂。简单的基于堆栈的模型并没有真正反映现代硬件的现实。

If the resulting application doesn't meet your performance criteria, then run it through a profiler to find the bottlenecks. If it turns out that returning that struct by value is causing a problem, then you can experiment with passing by reference to the function.

如果生成的应用程序不符合您的性能标准,则通过分析器运行它以找到瓶颈。如果事实证明按值返回该结构导致问题,那么您可以尝试通过引用传递给函数。

Unless you're working in a highly constrained, embedded environment, you really don't have to count every byte and CPU cycle. You don't want to be needlessly wasteful, but by that same token you don't want to obsess over how things work at the low level unless a) you have really strict performance requirements and b) you are intimately familiar with the details of your particular platform (meaning that you not only know your platform's function calling conventions inside and out, you know how your compiler uses those conventions as well). Otherwise, you're just guessing. Let the compiler do the hard work for you. That's what it's there for.

除非您在高度受限的嵌入式环境中工作,否则您实际上不必计算每个字节和CPU周期。你不想浪费不必要的东西,但同样的道理,除非a)你有非常严格的性能要求,并且你非常熟悉您的特定平台(意味着您不仅知道平台的内部和外部函数调用约定,还知道您的编译器如何使用这些约定)。否则,你只是在猜测。让编译器为您努力工作。这就是它的用途。

#2


-2  

Rules of thumb:

经验法则:

  1. If sizeof(return type) is bigger than sizeof(int), you should probably pass it by pointer to avoid the copy overhead. This is a performance issue. There's some penalty for dereferencing the pointer, so there are some exceptions to this rule.
  2. 如果sizeof(返回类型)大于sizeof(int),则应该通过指针传递它以避免复制开销。这是一个性能问题。取消引用指针会有一些惩罚,所以这个规则有一些例外。

  3. If the return type is complex (containing pointer members), pass it by pointer. Copying the local return value to the stack will not copy dynamic memory, for example.
  4. 如果返回类型很复杂(包含指针成员),则通过指针传递它。例如,将本地返回值复制到堆栈不会复制动态内存。

  5. If you want the function to allocate the memory, it should return a pointer to the newly allocated memory. It's called the factory design pattern.
  6. 如果你想让函数分配内存,它应该返回一个指向新分配的内存的指针。它被称为工厂设计模式。

  7. If you have more than one thing you want to return from a function - return one by value, and pass the rest by pointers.
  8. 如果你想从函数返回多个东西 - 按值返回一个,并通过指针传递其余的东西。

  9. If you have a complex/big data type which is both input and output, pass it by pointer.
  10. 如果您有一个既输入又输出的复杂/大数据类型,则通过指针传递它。

#1


12  

Start by deciding which approach makes the most sense at the logical level, irrespective of what you think the performance implications might be. If returning a struct by value most clearly conveys the intent of the code, then do that.

首先,决定哪种方法在逻辑层面最有意义,而不管您认为性能影响可能是什么。如果按值返回结构最清楚地表达了代码的意图,那么就这样做。

This isn't the 1980s anymore. Compilers have gotten a lot smarter since then and do a really good job of optimizing code, especially code that's written in a clear, straightforward manner. Similarly, parameter passing and value return conventions have become fairly sophisticated as well. The simplistic stack-based model doesn't really reflect the reality of modern hardware.

这不再是20世纪80年代了。从那时起,编译器变得更加智能,并且在优化代码方面做得非常好,特别是以清晰,直接的方式编写的代码。类似地,参数传递和值返回约定也变得相当复杂。简单的基于堆栈的模型并没有真正反映现代硬件的现实。

If the resulting application doesn't meet your performance criteria, then run it through a profiler to find the bottlenecks. If it turns out that returning that struct by value is causing a problem, then you can experiment with passing by reference to the function.

如果生成的应用程序不符合您的性能标准,则通过分析器运行它以找到瓶颈。如果事实证明按值返回该结构导致问题,那么您可以尝试通过引用传递给函数。

Unless you're working in a highly constrained, embedded environment, you really don't have to count every byte and CPU cycle. You don't want to be needlessly wasteful, but by that same token you don't want to obsess over how things work at the low level unless a) you have really strict performance requirements and b) you are intimately familiar with the details of your particular platform (meaning that you not only know your platform's function calling conventions inside and out, you know how your compiler uses those conventions as well). Otherwise, you're just guessing. Let the compiler do the hard work for you. That's what it's there for.

除非您在高度受限的嵌入式环境中工作,否则您实际上不必计算每个字节和CPU周期。你不想浪费不必要的东西,但同样的道理,除非a)你有非常严格的性能要求,并且你非常熟悉您的特定平台(意味着您不仅知道平台的内部和外部函数调用约定,还知道您的编译器如何使用这些约定)。否则,你只是在猜测。让编译器为您努力工作。这就是它的用途。

#2


-2  

Rules of thumb:

经验法则:

  1. If sizeof(return type) is bigger than sizeof(int), you should probably pass it by pointer to avoid the copy overhead. This is a performance issue. There's some penalty for dereferencing the pointer, so there are some exceptions to this rule.
  2. 如果sizeof(返回类型)大于sizeof(int),则应该通过指针传递它以避免复制开销。这是一个性能问题。取消引用指针会有一些惩罚,所以这个规则有一些例外。

  3. If the return type is complex (containing pointer members), pass it by pointer. Copying the local return value to the stack will not copy dynamic memory, for example.
  4. 如果返回类型很复杂(包含指针成员),则通过指针传递它。例如,将本地返回值复制到堆栈不会复制动态内存。

  5. If you want the function to allocate the memory, it should return a pointer to the newly allocated memory. It's called the factory design pattern.
  6. 如果你想让函数分配内存,它应该返回一个指向新分配的内存的指针。它被称为工厂设计模式。

  7. If you have more than one thing you want to return from a function - return one by value, and pass the rest by pointers.
  8. 如果你想从函数返回多个东西 - 按值返回一个,并通过指针传递其余的东西。

  9. If you have a complex/big data type which is both input and output, pass it by pointer.
  10. 如果您有一个既输入又输出的复杂/大数据类型,则通过指针传递它。