为什么C代码不返回结构?

Meh*_*dad 18 c return

虽然它非常方便,但我很少(如果有的话)遇到在C 中返回structs(或unions)的函数,无论它们是动态链接函数还是静态定义函数.
它们通过指针参数返回数据.

(Windows中的动态示例是GetSystemInfo.)

这背后的原因是什么?
是因为性能问题,ABI兼容性问题还是其他问题?

unw*_*ind 12

我会说"性能",加上有时甚至有可能让C程序员感到惊讶.它不是......在C的一般"味道"中,对许多人来说,扔掉诸如结构之类的大事物就像它们仅仅是价值一样.根据语言,他们确实是.

同样,许多C程序员似乎memcpy()在需要复制结构时自动求助,而不仅仅是使用赋值.

至少在C++中,有一种称为"返回值优化"的东西能够以这种方式静默转换代码:

struct Point { int x, y; };

struct Point point_new(int x, int y)
{
  struct Point p;
  p.x = x;
  p.y = y;
  return p;
}
Run Code Online (Sandbox Code Playgroud)

成:

void point_new(struct Point *return_value, int x, int y)
{
  struct Point p;
  p.x = x;
  p.y = y;
  *return_value = p;
}
Run Code Online (Sandbox Code Playgroud)

它消除了结构值的(可能是堆栈饥饿的)"真实"返回.我想更好的是这个,不确定他们是否那么聪明:

void point_new(struct Point *return_value, int x, int y)
{
  return_value->x = x;
  return_value->y = y;
}
Run Code Online (Sandbox Code Playgroud)

我不确定C编译器是否可以执行任何此类操作,如果他们不能,那么我猜这可能是针对结构返回的真正参数,对于性能非常关键的程序.


Dav*_*ave 8

原因主要是历史原因.Rob Pike 在他的论文"文本编辑器sam"中写道

编程风格的相关问题:sam经常按值传递结构,这简化了代码.传统上,C程序通过引用传递结构,但堆栈上的隐式分配更容易使用.结构传递是C的一个相对较新的特性(它不在C 14的标准参考手册中),并且在大多数商业C编译器中得不到很好的支持.但它既方便又富有表现力,通过完全避免分配器并消除指针别名来简化内存管理.

话虽如此,该技术存在缺陷; 返回淫秽的大型结构机会堆栈溢出.


Céd*_*ien 7

C中的返回是通过将返回值存储在堆栈中来实现的.
返回结构或联合将导致在堆栈上放置可能非常大的数据,这可能导致堆栈溢出.

仅返回指向struct/union的指针非常安全,因为您只在堆栈中放入少量数据(通常为4个字节).

  • 我不认为这可能是全部原因.通过任何方式在堆栈上创建非常大的数据都有可能导致堆栈溢出,但C程序员似乎在不避免创建与局部变量相同的结构的情况下避免使用结构*作为返回值*.例如,使用`struct tm`,你经常将一个放在堆栈上并将指针传递给`localtime_r`.如果他们真的害怕堆栈溢出,程序员就不会这样做.我想可能是`localtime_r`接口设计得很巧妙,可以支持9个单词很多的平台,你可以使用malloc. (3认同)
  • 而且这使得必须冗余复制整个返回数据. (2认同)
  • 不完全正确.请参阅我的答案http://stackoverflow.com/a/8728932/841108 (2认同)

Bas*_*tch 6

AC函数可以返回一个结构(C++也是如此,它很常见).也许在C的最初几年,它不可能.

适用于Linux及相关系统的x86-64 ABI规范(第21页)甚至表示,适合两个-64位字的结构通常可以在两个寄存器中返回,而无需通过内存(甚至是堆栈).很可能这比通过堆栈更快.

正如unwind他的回答中所回答的那样,ABI经常要求将结构结果静默地转换为不可见的指针.

甚至可以定义另一个调用约定,它在更多寄存器中返回更多数据.但是这样的新约定会破坏所有目标代码并需要重新编译所有内容(甚至包括Linux上的系统库等libc.so.6),当然还需要更改编译器.

当然,ABI约定与处理器,系统和编译器有关.

我不知道Windows,我不知道Windows定义为什么是ABI.