web-dev-qa-db-fra.com

Comment renvoyer plusieurs valeurs d'une fonction en C?

Si j'ai une fonction qui produit un résultat int et un résultat string, comment puis-je les renvoyer à partir d'une fonction?

Autant que je sache, je ne peux renvoyer qu'une seule chose, telle que déterminée par le type précédant le nom de la fonction.

78
Tony Stark

Je ne sais pas ce que votre string est, mais je vais supposer qu'il gère sa propre mémoire.

Vous avez deux solutions:

1: Retourne un struct qui contient tous les types dont vous avez besoin.

struct Tuple {
    int a;
    string b;
};

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

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

2: Utilisez des pointeurs pour transmettre des valeurs.

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

Le choix que vous choisissez d'utiliser dépend en grande partie de vos préférences personnelles quant à la sémantique que vous préférez.

115
Travis Gockel

Option 1: Déclarer une structure avec un int et une chaîne et retourner une variable de structure.

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

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

Option 2: Vous pouvez passer l’un des deux via le pointeur et modifier le paramètre actuel via le pointeur et renvoyer l’autre comme d’habitude:

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

ou

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

Option 3: Similaire à l'option 2. Vous pouvez passer les deux via un pointeur et ne rien renvoyer de la fonction:

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

Deux approches différentes:

  1. Transmettez vos valeurs de retour par pointeur et modifiez-les dans la fonction. Vous déclarez votre fonction comme nulle, mais elle revient via les valeurs transmises en tant que pointeurs.
  2. Définissez une structure qui agrège vos valeurs de retour.

Je pense que le n ° 1 est un peu plus évident sur ce qui se passe, bien que cela puisse devenir fastidieux si vous avez trop de valeurs de retour. Dans ce cas, l'option n ° 2 fonctionne assez bien, bien que la création de structures spécialisées à cette fin demande un certain effort mental.

6
James Thompson

Étant donné que l'un de vos types de résultats est une chaîne (et que vous utilisez C, pas C++), il est recommandé de passer des pointeurs en tant que paramètres de sortie. Utilisation:

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

et appelez comme ça:

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

En général, préférez effectuer l'affectation dans la fonction appelant, et non à l'intérieur de la fonction elle-même, afin de pouvoir être aussi ouvert que possible pour différentes stratégies d'allocation.

6
Jesse Beder

Créez une structure et définissez deux valeurs à l'intérieur et renvoyez la variable de structure.

struct result {
    int a;
    char *string;
}

Vous devez allouer de l'espace pour le char * dans votre programme.

6
zs2020

Utilisez des pointeurs comme paramètres de fonction. Ensuite, utilisez-les pour renvoyer plusieurs valeurs.

3
Bloodlee

En passant des paramètres par référence à fonction.

Exemples:

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

Également en utilisant des variables globales, mais ce n'est pas recommandé.

Exemple:

int a=0;

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

Une approche consiste à utiliser des macros. Placez ceci dans un fichier d'en-tête 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__)

Cela permet de renvoyer jusqu'à quatre variables d'une fonction et de les affecter à un maximum de quatre variables. Par exemple, vous pouvez les utiliser comme ceci:

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

C'est ce que ça imprime:

(55, 3.9, 24.15)

La solution peut ne pas être aussi portable car elle nécessite C99 ou une version ultérieure pour les macros variadiques et les déclarations de variable for-statement. Mais je pense que c'était assez intéressant pour poster ici. Un autre problème est que le compilateur ne vous avertira pas si vous leur attribuez de mauvaises valeurs. Vous devez donc faire attention.

Des exemples supplémentaires, ainsi qu'une version du code utilisant des unions, basée sur une pile, sont disponibles sur my référentiel github .

2
Klas. S