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

时间:2022-03-15 21:00:28

While it's very handy, I very rarely, if ever, come across functions that return structs (or unions) in C, whether they are dynamically linked functions or statically defined functions.
They instead return the data through a pointer parameter.

虽然非常方便,但我很少遇到在C中返回结构体(或联合)的函数,无论它们是动态链接的函数还是静态定义的函数。相反,它们通过指针参数返回数据。

(An dynamic example in Windows is GetSystemInfo.)

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

What's the reason behind this?
Is it because of a performance issue, an ABI compatibility issue, or something else?

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

7 个解决方案

#1


12  

I would say "performance", plus the fact that it's even possible sometimes seems to surprise C programmers. It's not ... in the general "flavor" of C, to many, to throw around large things such as structs as if they were mere values. Which, according to the language, they really are.

我想说的是“性能”,加上它有时甚至是可能的事实,这似乎让C程序员感到惊讶。这不是……一般来说,C的“味道”,对很多人来说,是把诸如结构之类的大东西扔到一边,就好像它们只是一种价值观。根据语言,它们确实是。

Along the same lines, many C programmers seem to automatically resort to memcpy() when the need to copy structs arises, rather than just using assignment, too.

沿着同样的思路,许多C程序员似乎在需要复制结构时自动地使用memcpy(),而不仅仅是使用赋值。

In C++ at least, there is something called "return value optimization" which is able to silently transform code like this:

至少在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;
}

into:

成:

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

which does away with the (potentially stack-hungry) "true" return of a struct value. I guess even better would be this, not sure if they're that smart:

它消除了(潜在的堆栈饥渴)结构值的“true”返回。我想最好是这样,不确定他们是否有那么聪明:

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

I'm not sure if C compilers can do any of this, if they can't then I guess that might be a real argument against struct returns, for very performance-critical programs.

我不确定C编译器是否能做到这些,如果他们做不到,我想这可能是一个反对struct return的论据,对于性能非常关键的程序。

#2


7  

Returns in C are made by storing the return value in the stack.
Returning a struct or an union would lead to put potentially very big data on the stack, and this could lead to a stack overflow.

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

Returning only a pointer to the struct/union is pretty much safier because you only put a little quantity of data (4 bytes in general) in the stack.

只返回struct/union的指针非常容易,因为您只在堆栈中放入少量的数据(通常是4个字节)。

#3


6  

The reasons are mostly historical. In his paper, "The Text Editor sam", Rob Pike writes

原因大多是历史原因。在他的论文中,“文本编辑sam”,Rob Pike写道。

A related matter of programming style: sam frequently passes structures by value, which simplifies the code. Traditionally, C programs have passed structures by reference, but implicit allocation on the stack is easier to use. Structure passing is a relatively new feature of C (it is not in the standard reference manual for C14), and is poorly supported in most commercial C compilers. It’s convenient and expressive, though, and simplifies memory management by avoiding the allocator altogether and eliminating pointer aliases.

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

That being said, there are pitfalls to the technique; returning obscenely large structures chances stack overflow.

话虽如此,这种技术还是有缺陷的;返回异常大的结构时,堆栈可能溢出。

#4


6  

A C function can return a structure (and so does a C++ one, where it is quite common). Perhaps in the very first years of C, it could not.

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

The x86-64 ABI specification for Linux and related systems (page 21) even says that structures fitting into two -64 bits- words can often be returned in two registers without going thru the memory (even the stack). Very probably this is faster than going thru the stack.

Linux和相关系统的x86-64 ABI规范(第21页)甚至说,结构适合于两个-64位——通常可以在两个寄存器中返回,而无需经过内存(甚至堆栈)。很可能这比通过堆栈要快。

As unwind replied in his answer, the ABI often requires structure results to be silently converted to an invisible pointer.

正如unwind在回答中所回答的那样,ABI通常需要将结构结果静默地转换为一个不可见的指针。

One could even define another calling convention which returns more data in more registers. But such new conventions would break all object code and require recompilation of everything (including even, on Linux, system libraries like libc.so.6), and of course require changing the compiler.

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

Of course, ABI conventions are processor, system and compiler related.

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

I don't know Windows and I don't know what Windows define as its ABI.

我不知道Windows,也不知道Windows的ABI定义是什么。

#5


4  

In pre-ANSI C, you couldn't return object of structure type and you could also not pass arguments of structure types.

在前ansi C中,不能返回结构类型的对象,也不能传递结构类型的参数。

From Chris Torek quote in comp.lang.c:

请听克里斯·托雷克的报道。

Note that V6 C also did not support struct-valued arguments and struct-valued return values.

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

The reason nowadays they are not very used is people prefer to return pointer to structure which involves only a pointer copy instead of the copy of a whole structure object.

现在它们不太常用的原因是人们更喜欢返回指向结构的指针,它只涉及一个指针拷贝而不是整个结构对象的拷贝。

#6


4  

In addition to the idea that there might be a performance hit or that returning structs by value might not have been commonly supported in pre-standard days another reason for C functions to not use return-by-value for structs is that if you return a struct you can't easily return a success/failure indicator. I know that I occasionally would start out designing a function to return a struct that was initialized in the function, but then I'd run into the problem of how to indicate whether or not the function succeeded. You pretty much have the following options:

除了可能有一个性能下降或返回结构体的值可能不是通常支持pre-standard天C函数的另一个原因为结构是,如果你不使用return-by-value返回一个结构体你不能轻松地返回一个成功/失败的指标。我知道,我偶尔会开始设计一个函数来返回在函数中初始化的结构体,但之后我会遇到如何指示函数是否成功的问题。你基本上有以下选择:

  • guarantee success (sometimes this is possible)
  • 保证成功(有时这是可能的)
  • pass in a pointer to a location for the error code
  • 传递一个指向错误代码位置的指针
  • have a field or sentinel value in the struct that indicates success/failure
  • 在表示成功/失败的结构中有字段或标记值吗?

Only option 1 keeps the interface from being a kludge. The second option kind of defeats the purpose of returning the struct by value, and actually makes the function more difficult to use for handling failures. The third option is just plainly not a good design in nearly all cases.

只有选项1可以防止接口成为杂凑。第二个选项有点违背按值返回结构体的目的,实际上使函数更难于用于处理失败。第三种选择显然不是一个好的设计。

#7


0  

In general, Windows functions either return nothing or error codes, especially when it comes to returning structures or classes.

一般来说,Windows函数要么返回任何东西,要么返回错误代码,特别是返回结构或类时。

Efficiency might be an issue, although RVO should remove the overhead.

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

I think the main reason was to keep the methods inline with the coding style used before.

我认为主要的原因是使方法与以前使用的编码风格保持一致。

#1


12  

I would say "performance", plus the fact that it's even possible sometimes seems to surprise C programmers. It's not ... in the general "flavor" of C, to many, to throw around large things such as structs as if they were mere values. Which, according to the language, they really are.

我想说的是“性能”,加上它有时甚至是可能的事实,这似乎让C程序员感到惊讶。这不是……一般来说,C的“味道”,对很多人来说,是把诸如结构之类的大东西扔到一边,就好像它们只是一种价值观。根据语言,它们确实是。

Along the same lines, many C programmers seem to automatically resort to memcpy() when the need to copy structs arises, rather than just using assignment, too.

沿着同样的思路,许多C程序员似乎在需要复制结构时自动地使用memcpy(),而不仅仅是使用赋值。

In C++ at least, there is something called "return value optimization" which is able to silently transform code like this:

至少在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;
}

into:

成:

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

which does away with the (potentially stack-hungry) "true" return of a struct value. I guess even better would be this, not sure if they're that smart:

它消除了(潜在的堆栈饥渴)结构值的“true”返回。我想最好是这样,不确定他们是否有那么聪明:

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

I'm not sure if C compilers can do any of this, if they can't then I guess that might be a real argument against struct returns, for very performance-critical programs.

我不确定C编译器是否能做到这些,如果他们做不到,我想这可能是一个反对struct return的论据,对于性能非常关键的程序。

#2


7  

Returns in C are made by storing the return value in the stack.
Returning a struct or an union would lead to put potentially very big data on the stack, and this could lead to a stack overflow.

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

Returning only a pointer to the struct/union is pretty much safier because you only put a little quantity of data (4 bytes in general) in the stack.

只返回struct/union的指针非常容易,因为您只在堆栈中放入少量的数据(通常是4个字节)。

#3


6  

The reasons are mostly historical. In his paper, "The Text Editor sam", Rob Pike writes

原因大多是历史原因。在他的论文中,“文本编辑sam”,Rob Pike写道。

A related matter of programming style: sam frequently passes structures by value, which simplifies the code. Traditionally, C programs have passed structures by reference, but implicit allocation on the stack is easier to use. Structure passing is a relatively new feature of C (it is not in the standard reference manual for C14), and is poorly supported in most commercial C compilers. It’s convenient and expressive, though, and simplifies memory management by avoiding the allocator altogether and eliminating pointer aliases.

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

That being said, there are pitfalls to the technique; returning obscenely large structures chances stack overflow.

话虽如此,这种技术还是有缺陷的;返回异常大的结构时,堆栈可能溢出。

#4


6  

A C function can return a structure (and so does a C++ one, where it is quite common). Perhaps in the very first years of C, it could not.

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

The x86-64 ABI specification for Linux and related systems (page 21) even says that structures fitting into two -64 bits- words can often be returned in two registers without going thru the memory (even the stack). Very probably this is faster than going thru the stack.

Linux和相关系统的x86-64 ABI规范(第21页)甚至说,结构适合于两个-64位——通常可以在两个寄存器中返回,而无需经过内存(甚至堆栈)。很可能这比通过堆栈要快。

As unwind replied in his answer, the ABI often requires structure results to be silently converted to an invisible pointer.

正如unwind在回答中所回答的那样,ABI通常需要将结构结果静默地转换为一个不可见的指针。

One could even define another calling convention which returns more data in more registers. But such new conventions would break all object code and require recompilation of everything (including even, on Linux, system libraries like libc.so.6), and of course require changing the compiler.

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

Of course, ABI conventions are processor, system and compiler related.

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

I don't know Windows and I don't know what Windows define as its ABI.

我不知道Windows,也不知道Windows的ABI定义是什么。

#5


4  

In pre-ANSI C, you couldn't return object of structure type and you could also not pass arguments of structure types.

在前ansi C中,不能返回结构类型的对象,也不能传递结构类型的参数。

From Chris Torek quote in comp.lang.c:

请听克里斯·托雷克的报道。

Note that V6 C also did not support struct-valued arguments and struct-valued return values.

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

The reason nowadays they are not very used is people prefer to return pointer to structure which involves only a pointer copy instead of the copy of a whole structure object.

现在它们不太常用的原因是人们更喜欢返回指向结构的指针,它只涉及一个指针拷贝而不是整个结构对象的拷贝。

#6


4  

In addition to the idea that there might be a performance hit or that returning structs by value might not have been commonly supported in pre-standard days another reason for C functions to not use return-by-value for structs is that if you return a struct you can't easily return a success/failure indicator. I know that I occasionally would start out designing a function to return a struct that was initialized in the function, but then I'd run into the problem of how to indicate whether or not the function succeeded. You pretty much have the following options:

除了可能有一个性能下降或返回结构体的值可能不是通常支持pre-standard天C函数的另一个原因为结构是,如果你不使用return-by-value返回一个结构体你不能轻松地返回一个成功/失败的指标。我知道,我偶尔会开始设计一个函数来返回在函数中初始化的结构体,但之后我会遇到如何指示函数是否成功的问题。你基本上有以下选择:

  • guarantee success (sometimes this is possible)
  • 保证成功(有时这是可能的)
  • pass in a pointer to a location for the error code
  • 传递一个指向错误代码位置的指针
  • have a field or sentinel value in the struct that indicates success/failure
  • 在表示成功/失败的结构中有字段或标记值吗?

Only option 1 keeps the interface from being a kludge. The second option kind of defeats the purpose of returning the struct by value, and actually makes the function more difficult to use for handling failures. The third option is just plainly not a good design in nearly all cases.

只有选项1可以防止接口成为杂凑。第二个选项有点违背按值返回结构体的目的,实际上使函数更难于用于处理失败。第三种选择显然不是一个好的设计。

#7


0  

In general, Windows functions either return nothing or error codes, especially when it comes to returning structures or classes.

一般来说,Windows函数要么返回任何东西,要么返回错误代码,特别是返回结构或类时。

Efficiency might be an issue, although RVO should remove the overhead.

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

I think the main reason was to keep the methods inline with the coding style used before.

我认为主要的原因是使方法与以前使用的编码风格保持一致。