【问题标题】: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 中做同一件事的不同方式可能会产生不同的效果和考虑。

标签: c function return multiple-variable-return


【解决方案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++ 中,我会使用按引用的方法,因为虽然从实际角度(处理器的作用)来看是相同的,但它对程序员来说更简单。

      【讨论】:

        猜你喜欢
        • 2016-01-18
        • 2015-06-26
        • 1970-01-01
        • 2021-03-03
        • 2011-04-19
        • 1970-01-01
        • 2012-09-20
        相关资源
        最近更新 更多