有效的多个返回值[重复]
Posted
技术标签:
【中文标题】有效的多个返回值[重复]【英文标题】:Effective multiple return values [duplicate] 【发布时间】:2014-01-21 14:43:54 【问题描述】:假设我们有一个应该返回两个返回值的函数。
例如,我们有一些函数返回 char*
及其长度。 Char 是在该特定函数中分配的。
我可以想象以下方法:
int foo(char **result); // Passing pointer to char*, returning int
char* bar(int *len); // Passing pointer to int, returning char*
struct char_and_len foobar(); // Returning struct that contains both values
还有其他方法可以实现多个值吗?最有效的方法是什么?
考虑到性能、内存对齐或任何其他隐藏的 C 功能,我非常感谢详细的解释。
【问题讨论】:
为什么比您提供的选项更聪明?这样做只会混淆你的代码。 @meagar。为什么?我很好奇这样做的其他方法是什么。这样做可能有一些非常有趣的原因。此外,我有强烈的感觉,在 C 中做同一件事的不同方式可能会产生不同的效果和考虑。 【参考方案1】:这里有两种情况。如果您使用的代码库/框架(例如 Glib)具有贯穿整个应用程序的标准字符串结构,请使用第三个选项:
struct string function();
如果您的代码库没有在任何地方都使用一个标准结构作为字符串,我建议不要使用结构。来回转换的麻烦并不值得。
否则,约定(至少我见过)是返回 char*
并将长度作为指针参数:
char* function(int* length);
【讨论】:
请您详细说明为什么char* f(int*)
vs int f(char **)
@Sigurd:至少在我见过的大多数代码和库函数中,char**
用于调用者负责内存分配并且已经确定了长度,或者正在提供一个最大长度。例如,在 Linux 系统调用 read(2)
中,您提供了一个预分配的缓冲区和该缓冲区的最大数据长度。
"否则,约定是返回 char* 并将长度作为指针参数:" -- 断言约定 is 这有点苛刻。还有其他同样有效的约定。
@H2CO3:是的。见编辑。【参考方案2】:
另一种方式:
void foo(char **result, int *len);
我认为最糟糕的是:
struct char_and_len foobar();
我更喜欢我给你看的那个,因为我不喜欢在参数和有效返回中混合返回值。
【讨论】:
【参考方案3】:您可以只返回一个数组并将其包装在一个结构中:
typedef struct
char *strings[2];
RetType;
RetType func()
return (RetType) "foo", "bar" ;
另一个惯用的解决方案是 C 将数组传递给您的函数并填充它:
void func(char *strings[2])
strings[0] = "foo";
strings[1] = "bar";
第三种解决方案是返回一个值并通过指针传递另一个值(尽管如果您有不同类型的值,这更有意义):
char *func(char **outparm)
*outparm = "foo";
return "bar";
另外,请原谅我没有const
-正确。
【讨论】:
我的问题可能不太清楚。我需要返回 int 和 char *。假设 char 没有以 \0 结尾,所以我们必须返回它的长度。 @Sigurd 然后您可以将struct
更改为包含char *
和int
。您也可以将第三个示例中的返回值之一更改为int
。忘记第二种方法。
我知道如何返回多个值 :) 我的问题是什么是最有效的方法以及为什么。
@Sigurd 在什么意义上有效?我列出了所有实际可行的解决方案。选一个。真的没关系。【参考方案4】:
我最喜欢的是
void foobar(struct char_and_len*);
出于以下原因:
只需要传递一个参数 返回值/输出参数不混 可以忽略返回值,尤其是在需要再次释放返回值时,这可能是编程错误的严重来源。输出参数不可忽略。 拥有指向结构的指针可避免过多的复制操作。只需向函数提供一个指针。 这样,函数的调用者可以决定struct char_and_len
的存储位置(在堆、堆栈上),而在使用返回值时,数据至少需要暂时放入堆栈
【讨论】:
如果您为此目的制作了一个结构,为什么不按值返回整个结构。 虽然总的来说我同意你的观点,因为我们在这里处理字符串,你不认为定义一个必须相互转换的字符串“类型”是个问题吗? 【参考方案5】:使用字符串。
对于您引用的具体示例,请记住长度隐式或显式是任何字符串类型的属性。例如,C 风格的字符串是以 null 结尾的,所以即使没有明确的长度,调用者仍然可以确定字符串的长度。 Pascal 风格的字符串包括长度作为第一个字节。 char*
不一定是字符串,它可能是您确实需要长度的普通旧文本缓冲区。但是字符串类型的重点是避免需要单独传递数据和长度。
更一般地...
一个函数只能返回一个值,所以如果你需要返回更多的值,你需要使用结构将所有内容打包成一个值,或者传递一个(或多个指针)到接收结果的位置。您使用哪种方法取决于具体情况。您是否已经为需要返回的数据定义了一个结构?调用者是否可能有一个可以接收结果的现有对象?没有最好的方法,只有适合的方法。
【讨论】:
【参考方案6】:最好的方法是始终使用指针。在 C 中,每个函数都复制在不同内存区域中传递的参数。最好的方法是您在性能方面发布的三个中的第二个。
但我会将一个指向结构的指针作为参数传递,该结构包含长度和字符串,并返回 void。
【讨论】:
C 中的字符串始终是指针,无论您是通过引用还是值传递指针本身。 是的,但他也有一个 int。 一个 int 通常是四个字节。在 32 位系统上,指针也是如此。在 64 位系统上,指针(8 字节)实际上会大于int
。
在 64 位系统上 int 和 *int 具有相同的大小,4 个字节而不是 8 个。我刚刚检查过(在 32 位系统上它是 2 个字节)。因此,在这种情况下,您不会注意到性能有任何改进:P 出于这个原因,我说我会使用一个指向其中包含两种数据类型的结构的指针。【参考方案7】:
在 C 中,返回两个值是不“正常的”。这意味着正如您已经做的那样,您可以创建一个结构来返回结果。这是形式上正确的方法,但不是最简单的方法,而且很麻烦。所以我会完全忘记那个解决方案。
通常,返回结果的另一种方式是通过引用传递参数(在 Pascal VB 等中)。 在 C 中,不可能通过引用传递它们,而是传递一个指针。 但在 C++ 中,可以通过引用传递变量(这意味着传递指针但使用变量)。
所以我相信做你需要的最简单的方法是定义:
char* bar(int *lenP); //
现在你有了一个可以返回两个结果的函数的结果:
如果它被定义为伪语法 (s,l)=bar();
此外,您还可以使用:
空条(char * *s,int * lenP); // 这种情况同样对待 too 参数。
在 C++ 中,我会使用按引用的方法,因为虽然从实际角度(处理器的作用)来看是相同的,但它对程序员来说更简单。
【讨论】:
以上是关于有效的多个返回值[重复]的主要内容,如果未能解决你的问题,请参考以下文章