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

时间:2012-01-04 14:51:13

标签: c return

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

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

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

7 个答案:

答案 0 :(得分: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;
}

成:

void point_new(struct Point *return_value, int x, int y)
{
  struct Point p;
  p.x = x;
  p.y = y;
  *return_value = p;
}

消除了结构值的(可能是堆栈饥饿的)“true”返回。我想这会更好,不确定他们是否那么聪明:

void point_new(struct Point *return_value, int x, int y)
{
  return_value->x = x;
  return_value->y = y;
}

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

答案 1 :(得分:8)

原因主要是历史性的。在他的论文"The Text Editor sam"中,Rob Pike写道

  

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

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

答案 2 :(得分:7)

C中的返回值是将返回值存储在stack中。
返回结构或联合将导致在堆栈上放置可能非常大的数据,这可能导致stack overflow

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

答案 3 :(得分:6)

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

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

his answer中回复展开时,ABI通常要求将结构结果静默转换为不可见指针。

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

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

我不知道Windows,我不知道Windows定义为ABI。

答案 4 :(得分:4)

在ANSI C之前,您无法返回结构类型的对象,也无法传递结构类型的参数。

来自Chris Torek的报道comp.lang.c:

  

请注意,V6 C也不支持结构值参数和   结构值返回值。

现在它们使用不当的原因是人们更喜欢返回指向只涉及指针副本而不是整个结构对象副本的结构的指针。

答案 5 :(得分:4)

除了可能存在性能损失或者在标准前几天内通常不支持按值返回结构的想法之外,C函数不使用结构的值返回的另一个原因是,如果你返回一个结构,你不能轻易返回成功/失败指标。我知道我偶尔会开始设计一个函数来返回在函数中初始化的结构,但是后来我遇到了如何指示函数是否成功的问题。你几乎有以下选择:

  • 保证成功(有时这是可能的)
  • 传入指向错误代码
  • 位置的指针
  • 在结构中具有指示成功/失败的字段或标记值

只有选项1可以防止界面成为污垢。第二种选择类型失败了按值返回结构的目的,实际上使得函数更难以用于处理失败。第三种选择在几乎所有情况下都显然不是一个好的设计。

答案 6 :(得分:0)

通常,Windows函数不返回任何内容或错误代码,特别是在返回结构或类时。

效率可能是一个问题,尽管RVO 应该消除开销。

我认为主要原因是保持方法与之前使用的编码风格内联。