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

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

据我所知,我只能返回一样东西,这是由函数名前面的类型决定的。

312700 次浏览

Create a struct and set two values inside and return the struct variable.

struct result {
int a;
char *string;
}

You have to allocate space for the char * in your program.

Use pointers as your function parameters. Then use them to return multiple value.

I don't know what your string is, but I'm going to assume that it manages its own memory.

You have two solutions:

1: Return a struct which contains all the types you need.

struct Tuple {
int a;
string b;
};


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


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

2: Use pointers to pass out values.

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);
}

Which one you choose to use depends largely on personal preference as to whatever semantics you like more.

Two different approaches:

  1. Pass in your return values by pointer, and modify them inside the function. You declare your function as void, but it's returning via the values passed in as pointers.
  2. Define a struct that aggregates your return values.

I think that #1 is a little more obvious about what's going on, although it can get tedious if you have too many return values. In that case, option #2 works fairly well, although there's some mental overhead involved in making specialized structs for this purpose.

Since one of your result types is a string (and you're using C, not C++), I recommend passing pointers as output parameters. Use:

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

and call it like this:

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

In general, prefer to do the allocation in the calling function, not inside the function itself, so that you can be as open as possible for different allocation strategies.

Option 1: Declare a struct with an int and string and return a struct variable.

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


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

Option 2: You can pass one of the two via pointer and make changes to the actual parameter through the pointer and return the other as usual:

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

or

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

Option 3: Similar to the option 2. You can pass both via pointer and return nothing from the function:

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

By passing parameters by reference to function.

Examples:

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

Also by using global variables but it is not recommended.

Example:

int a=0;


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

One approach is to use macros. Place this in a header file 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__)

This makes it possible to return up to four variables from a function and assign them to up to four variables. As an example, you can use them like this:

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;
}

This is what it prints:

(55, 3.9, 24.15)

The solution may not be as portable because it requires C99 or later for variadic macros and for-statement variable declarations. But I think it was interesting enough to post here. Another issue is that the compiler will not warn you if you assign them the wrong values, so you have to be careful.

Additional examples, and a stack-based version of the code using unions, are available at my github repository.