虽然它非常方便,但我很少(如果有的话)遇到在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编译器是否可以执行任何此类操作,如果他们不能,那么我猜这可能是针对结构返回的真正参数,对于性能非常关键的程序.
原因主要是历史原因.Rob Pike 在他的论文"文本编辑器sam"中写道
编程风格的相关问题:
sam经常按值传递结构,这简化了代码.传统上,C程序通过引用传递结构,但堆栈上的隐式分配更容易使用.结构传递是C的一个相对较新的特性(它不在C 14的标准参考手册中),并且在大多数商业C编译器中得不到很好的支持.但它既方便又富有表现力,通过完全避免分配器并消除指针别名来简化内存管理.
话虽如此,该技术存在缺陷; 返回淫秽的大型结构机会堆栈溢出.
C中的返回是通过将返回值存储在堆栈中来实现的.
返回结构或联合将导致在堆栈上放置可能非常大的数据,这可能导致堆栈溢出.
仅返回指向struct/union的指针非常安全,因为您只在堆栈中放入少量数据(通常为4个字节).
AC函数可以返回一个结构(C++也是如此,它很常见).也许在C的最初几年,它不可能.
适用于Linux及相关系统的x86-64 ABI规范(第21页)甚至表示,适合两个-64位字的结构通常可以在两个寄存器中返回,而无需通过内存(甚至是堆栈).很可能这比通过堆栈更快.
正如unwind在他的回答中所回答的那样,ABI经常要求将结构结果静默地转换为不可见的指针.
甚至可以定义另一个调用约定,它在更多寄存器中返回更多数据.但是这样的新约定会破坏所有目标代码并需要重新编译所有内容(甚至包括Linux上的系统库等libc.so.6),当然还需要更改编译器.
当然,ABI约定与处理器,系统和编译器有关.
我不知道Windows,我不知道Windows定义为什么是ABI.