如何从C中的函数返回多个值?


87

如果我有一个产生结果int和结果的函数string,如何从一个函数中返回它们呢?

据我所知,我只能返回一件事,这取决于函数名前面的类型。


6
通过string你的意思是“我使用C ++,这是std::string类”或“我用C,这是一个char *指针或char[]数组。”
克里斯·卢茨

好吧,在我的特定情况下,它们是两个整数:一个是我所比较的“分数”,另一个是找到最大分数的“指数”。我想在这里使用一个字符串示例,仅用于更一般的情况
Tony Stark

通过引用传递字符串并返回int。最快的方法。不需要结构。
Stefan Steiger,2010年

1
返回2个结果的函数不是一件事吗?鲍伯叔叔会怎么说?
邓肯

Answers:


123

我不知道您string是什么人,但我将假设它管理自己的内存。

您有两种解决方案:

1:返回struct包含所有所需类型的。

struct Tuple {
    int a;
    string b;
};

struct Tuple getPair() {
    Tuple r = { 1, getString() };
    return r;
}

void foo() {
    struct Tuple t = getPair();
}

2:使用指针传递值。

void getPair(int* a, string* b) {
    // Check that these are not pointing to NULL
    assert(a);
    assert(b);
    *a = 1;
    *b = getString();
}

void foo() {
    int a, b;
    getPair(&a, &b);
}

您选择使用哪一种在很大程度上取决于个人偏好,即您更喜欢哪种语义。


7
我认为这更多取决于返回值之间的相关性。如果int是错误代码,而字符串是结果,则不应将它们放在结构中。那真是愚蠢。在那种情况下,我将返回int并将字符串作为achar *和a传递给size_t长度,除非对于函数分配自己的字符串和/或return绝对至关重要NULL
克里斯·卢茨

@克里斯我完全同意你的看法,但是我不知道他需要的变量的用法语义。
特拉维斯·高克尔

克里斯好点。我认为值得指出的另一件事是价值与参考。如果我没有错误地返回示例中所示的结构,则意味着在返回时将进行复制,对吗?(我对C有点不满意),而另一种方法使用按引用传递,因此不需要分配更多的内存。当然,该结构可以通过指针返回并共享相同的好处,对吗?(确保正确分配内存,当然还要分配所有内存)
RTHarston,

@BobVicktor:C根本没有引用语义(这是C ++专有的),因此所有内容都是一个值。两个指针解决方案(#2)将指针的副本传递到一个函数中,getPair然后该函数取消引用。根据您正在执行的操作(OP从未明确这是否实际上是一个C问题),分配可能是个问题,但通常不在C ++领域(返回值优化保存所有这些)和C领域中的数据复制通常显式地发生(通过strncpy或其他方式)。
Travis Gockel

@TravisGockel感谢您的纠正。我指的是使用指针的事实,因此它不是复制值,而是共享已分配的值。但是您说对了,这在C语言中并没有正确地称为“传递引用”。感谢其他知识点。我喜欢学习有关语言的这些小知识。:)
RTHarston,

10

Option 1:声明一个带有int和string的结构,并返回一个struct变量。

struct foo {    
 int bar1;
 char bar2[MAX];
};

struct foo fun() {
 struct foo fooObj;
 ...
 return fooObj;
}

Option 2:您可以通过指针传递两个参数之一,并通过指针更改实际参数,然后照常返回另一个参数:

int fun(char **param) {
 int bar;
 ...
 strcpy(*param,"....");
 return bar;
}

要么

 char* fun(int *param) {
 char *str = /* malloc suitably.*/
 ...
 strcpy(str,"....");
 *param = /* some value */
 return str;
}

Option 3:与选项2相似。您可以同时通过指针传递任何内容,但不从该函数返回任何内容:

void fun(char **param1,int *param2) {
 strcpy(*param1,"....");
 *param2 = /* some calculated value */
}

关于选项2,您还应该输入字符串的长度。int fun(char *param, size_t len)
克里斯·卢茨

现在,这取决于函数的功能。如果我们知道函数将什么样的结果填充到char数组中,则可以为其分配足够的空间并将其传递给函数。无需传递长度。类似于我们使用strcpy的方式。
codaddict

7

由于您的结果类型之一是字符串(并且您使用的是C,而不是C ++),因此我建议将指针作为输出参数传递。使用:

void foo(int *a, char *s, int size);

并这样称呼它:

int a;
char *s = (char *)malloc(100); /* I never know how much to allocate :) */
foo(&a, s, 100);

通常,更喜欢在调用函数中进行分配,而不是在函数本身内部进行分配,以便您可以对不同的分配策略尽可能地保持开放。


6

两种不同的方法:

  1. 通过指针传递您的返回值,并在函数内部对其进行修改。您将函数声明为void,但它是通过作为指针传入的值返回的。
  2. 定义一个汇总您的返回值的结构。

我认为#1对于正在发生的事情更为明显,尽管如果返回值太多,它可能会很乏味。在这种情况下,选项2可以很好地工作,尽管为此目的制作专门的结构会涉及一些精神上的负担。


1
C没有参考文献;-),尽管自从使用海报以来string,就可以假设使用C ++ ...
Travis Gockel 2010年

完全忘记了!我修改了答案以使用指针,但是显然我在C ++领域已经很久了。:)
James Thompson

6

创建一个struct并在其中设置两个值,然后返回struct变量。

struct result {
    int a;
    char *string;
}

您必须char *在程序中分配空间。


3

使用指针作为函数参数。然后使用它们返回多个值。


2

通过引用参数传递给函数。

例子:

 void incInt(int *y)
 {
     (*y)++;  // Increase the value of 'x', in main, by one.
 }

也可以使用全局变量,但不建议这样做。

例:

int a=0;

void main(void)
{
    //Anything you want to code.
}

4
void main(void)哦,怎么烧!
克里斯·卢茨

你什么意思?@ Chris Lutz
Badr 2010年

6
main应该返回一个状态码。在* nix下,通常将其声明为int main(int argc, char *argv[]),我相信Windows具有类似的约定。
邓肯

2

一种方法是使用宏。将其放在头文件中multitype.h

#include <stdlib.h>

/* ============================= HELPER MACROS ============================= */

/* __typeof__(V) abbreviation */

#define TOF(V) __typeof__(V)

/* Expand variables list to list of typeof and variable names */

#define TO3(_0,_1,_2,_3) TOF(_0) v0; TOF(_1) v1; TOF(_2) v2; TOF(_3) v3;
#define TO2(_0,_1,_2)    TOF(_0) v0; TOF(_1) v1; TOF(_2) v2;
#define TO1(_0,_1)       TOF(_0) v0; TOF(_1) v1;
#define TO0(_0)          TOF(_0) v0;

#define TO_(_0,_1,_2,_3,TO_MACRO,...) TO_MACRO

#define TO(...) TO_(__VA_ARGS__,TO3,TO2,TO1,TO0)(__VA_ARGS__)

/* Assign to multitype */

#define MTA3(_0,_1,_2,_3) _0 = mtr.v0; _1 = mtr.v1; _2 = mtr.v2; _3 = mtr.v3;
#define MTA2(_0,_1,_2)    _0 = mtr.v0; _1 = mtr.v1; _2 = mtr.v2;
#define MTA1(_0,_1)       _0 = mtr.v0; _1 = mtr.v1;
#define MTA0(_0)          _0 = mtr.v0;

#define MTA_(_0,_1,_2,_3,MTA_MACRO,...) MTA_MACRO

#define MTA(...) MTA_(__VA_ARGS__,MTA3,MTA2,MTA1,MTA0)(__VA_ARGS__)

/* Return multitype if multiple arguments, return normally if only one */

#define MTR1(...) {                                                           \
    typedef struct mtr_s {                                                    \
      TO(__VA_ARGS__)                                                         \
    } mtr_t;                                                                  \
    mtr_t *mtr = malloc(sizeof(mtr_t));                                       \
    *mtr = (mtr_t){__VA_ARGS__};                                              \
    return mtr;                                                               \
  }

#define MTR0(_0) return(_0)

#define MTR_(_0,_1,_2,_3,MTR_MACRO,...) MTR_MACRO

/* ============================== API MACROS =============================== */

/* Declare return type before function */

typedef void* multitype;

#define multitype(...) multitype

/* Assign return values to variables */

#define let(...)                                                              \
  for(int mti = 0; !mti;)                                                     \
    for(multitype mt; mti < 2; mti++)                                         \
      if(mti) {                                                               \
        typedef struct mtr_s {                                                \
          TO(__VA_ARGS__)                                                     \
        } mtr_t;                                                              \
        mtr_t mtr = *(mtr_t*)mt;                                              \
        MTA(__VA_ARGS__)                                                      \
        free(mt);                                                             \
      } else                                                                  \
        mt

/* Return */

#define RETURN(...) MTR_(__VA_ARGS__,MTR1,MTR1,MTR1,MTR0)(__VA_ARGS__)

这样就可以从一个函数最多返回四个变量,并将它们分配给最多四个变量。例如,您可以像这样使用它们:

multitype (int,float,double) fun() {
    int a = 55;
    float b = 3.9;
    double c = 24.15;

    RETURN (a,b,c);
}

int main(int argc, char *argv[]) {
    int x;
    float y;
    double z;

    let (x,y,z) = fun();

    printf("(%d, %f, %g\n)", x, y, z);

    return 0;
}

它是这样打印的:

(55, 3.9, 24.15)

该解决方案可能无法移植,因为可变参数宏和for语句变量声明需要C99或更高版本。但是我认为在这里发布足够有趣。另一个问题是,如果为它们分配错误的值,则编译器不会警告您,因此您必须小心。

其他示例以及使用联合的基于堆栈的代码版本在我的github存储库中可用。


1
更大的可移植性问题是非标准功能__typeof__
MM

除了typeof,您可能还可以使用sizeof。获取返回值的大小,并分配足够大的数组以存储所有返回值。然后,您可以将其退回。
克拉斯。S
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.