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


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

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

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

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

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

然后有一个论点,堆栈应对变量是昂贵的,所以使用指针代替。但如上所述 这里 一些编译器能够自己决定(假设这也适用于C)。是否有一般规则,或者至少有一些不成文的惯例何时使用其中一个?我重视高于设计的性能。


6542
2018-03-11 13:34


起源

听起来更像是一个 programmers.stackexchange.com 题 - cup
返回指针通常会导致对象指向的生命周期管理问题,因为90%的时间,指针对象是由函数新构造的。 - user3528438
用的例子 strcat 适用于旧式API。新的替换返回错误代码: errno_t strcat_s( char *strDestination, size_t numberOfElements, const char *strSource )。 - i486
@ i486 - 是普遍支持的strcat_s()(et.al _s函数)吗? - ryyker
@ryyker AFAIK就是Windows API。 glibc没有安全功能(还) - Yorick de Wid


答案:


首先确定哪种方法最有意义 合乎逻辑 不管您认为性能影响可能是什么水平。如果回来了 struct 按价值最清楚地传达了 意图 代码,然后这样做。

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

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

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


12
2018-03-11 15:33





经验法则:

  1. 如果 sizeof(return type) 大于 sizeof(int),您应该通过指针传递它以避免复制开销。这是一个性能问题。取消引用指针会有一些惩罚,所以这个规则有一些例外。
  2. 如果返回类型很复杂(包含指针成员),则通过指针传递它。例如,将本地返回值复制到堆栈不会复制动态内存。
  3. 如果你想让函数分配内存,它应该返回一个指向新分配的内存的指针。它被称为  设计模式。
  4. 如果你想从函数返回多个东西 - 按值返回一个,并通过指针传递其余的东西。
  5. 如果您有一个既输入又输出的复杂/大数据类型,则通过指针传递它。

-2
2018-03-11 13:47



它们来自哪里?我的意思是你的 Rules of thumb。你能介绍参考链接吗? - chain ro
我的经验,一些常识和良好的行业惯例。 - liorda
No 5适用于任何数据类型......这是您在这里给出的最不完整的答案.... - Pandrei
我会选择sizeof(return)<sizeof(int *)。另外,如果返回类型的复制构造函数/赋值运算符/等做了非标准的事情,即使类的大小很小,也值得通过引用来做。我见过发起网络连接的复制ctor。人们把一些疯狂的东西放在陌生的地方 - LawfulEvil
@Pandrei关于5 - 如果它是一个POD,用指针传递它将没有多大意义。除非适用其中一条规则,否则应始终将结果作为返回类型。 - liorda