How do I return multiple values from a function in C?

前端 未结 8 1009
深忆病人
深忆病人 2020-11-22 15:14

If I have a function that produces a result int and a result string, how do I return them both from a function?

As far as I can tell I can

相关标签:
8条回答
  • 2020-11-22 15:41

    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.

    0 讨论(0)
  • 2020-11-22 15:42

    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.

    0 讨论(0)
  • 2020-11-22 15:43

    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.

    0 讨论(0)
  • 2020-11-22 15:49

    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 */
    }
    
    0 讨论(0)
  • 2020-11-22 15:55

    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.

    0 讨论(0)
  • 2020-11-22 15:57

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

    0 讨论(0)
提交回复
热议问题