2010-04-12 19 views
59

Si tengo una función que produce un resultado int y un resultado string, ¿cómo los devuelvo a ambos desde una función?¿Cómo devuelvo valores múltiples desde una función en C?

Por lo que puedo decir, solo puedo devolver una cosa, según lo determinado por el tipo que precede al nombre de la función.

+6

Por 'string' te refieres a" Estoy usando C++ y esta es la clase 'std :: string' "o" Estoy usando C y este es un puntero 'char *' o 'char []' formación." –

+0

bueno, en mi caso particular, fueron dos entradas: una para el "puntaje" de lo que estaba comparando, y otra para el "índice" de donde se encontró ese puntaje máximo. Quería usar un ejemplo de cadena aquí solo para el caso más general – sepiroth

+0

Pase la cadena por referencia y devuelva la int. La manera más rápida. No se requieren estructuras –

Respuesta

86

No sé cuál es su string, pero supongo que administra su propia memoria.

Usted tiene dos soluciones:

1: devolver un struct que contiene todos los tipos que necesita.

struct Tuple { 
    int a; 
    string b; 
}; 

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

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

2: Utilice los punteros para transmitir valores.

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

Que uno elige a utilizar depende en gran medida de las preferencias personales en cuanto a lo que sea la semántica más te gusten.

+6

Creo que depende más de cuán relacionados estén los valores de retorno. Si el int es un código de error y la cadena es un resultado, entonces estos no se deben juntar en una estructura. Eso es tonto. En ese caso, devolvería el int y pasaría la cadena como 'char *' y 'size_t' para la longitud, a menos que sea absolutamente vital para la función asignar su propia cadena y/o devolver' NULL'. –

+0

@Chris Estoy de acuerdo contigo por completo, pero no tengo idea de la semántica de uso de las variables que necesita. –

3

Crea una estructura y establece dos valores dentro y devuelve la variable struct.

struct result { 
    int a; 
    char *string; 
} 

Usted tiene que asignar espacio para el char * en su programa.

2

Utilice los punteros como sus parámetros de función. A continuación, utilícelos para devolver valores múltiples.

5

dos enfoques diferentes:

  1. Pass en sus valores devueltos por el puntero, y modificarlos dentro de la función. Usted declara su función como nula, pero regresa a través de los valores pasados ​​como punteros.
  2. Defina una estructura que agregue sus valores de retorno.

Creo que # 1 es un poco más obvio sobre lo que está pasando, aunque puede ser tedioso si tiene demasiados valores de devolución. En ese caso, la opción n. ° 2 funciona bastante bien, aunque hay algunos gastos generales involucrados en la fabricación de estructuras especializadas para este fin.

+1

C no tiene referencias ;-), aunque como el cartel usó 'cadena', podría ser seguro suponer que C++ ... –

+0

¡Se olvidó completamente de eso! Modifiqué mi respuesta para usar punteros, pero claramente he estado en C++ por mucho tiempo. :) –

4

Dado que uno de los tipos de resultados es una cadena (y está utilizando C, no C++), recomiendo pasar punteros como parámetros de salida. Uso:

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

y lo llaman así:

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

En general, preferimos hacer la asignación en el llamando función, no dentro de la función en sí, por lo que puede ser lo más abierto como sea posible para diferentes estrategias de asignación.

6

Option 1: Declare una estructura con un int y una cadena y devuelva una variable struct.

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

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

Option 2: Puede pasar uno de los dos a través de puntero y realizar cambios en el parámetro real a través del puntero y vuelve la otra, como de costumbre:

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

o

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

Option 3: Similar a la opción 2. Puede pasar ambos a través del puntero y no devolver nada desde la función:

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

Con respecto a la opción 2, también debe pasar la longitud de la cadena. 'int fun (char * param, size_t len)' –

+0

Ahora eso depende de lo que esté haciendo la función. Si sabemos qué tipo de resultado tiene la función en la matriz de caracteres, podríamos asignarle espacio suficiente y pasarlo a la función. No es necesario pasar el largo. Algo similar a cómo usamos 'strcpy' por ejemplo. – codaddict

1

Pasando los parámetros por referencia a la función.

Ejemplos:

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

también mediante el uso de variables globales, pero no es recomendable.

Ejemplo:

int a=0; 

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

'void main (void)' OH ¡CÓMO SE QUEMA! –

+0

¿qué quieres decir? @ Chris Lutz – Badr

+3

'main' se supone que devuelve un código de estado. En * nix, es más habitual declararlo como 'int main (int argc, char * argv [])', creo que Windows tiene convenciones similares. – Duncan

-3

Hey Peter CREO puede volver 2 valores con la ayuda de punteros en C.

1

Un enfoque es utilizar las macros. Colocar esto en un archivo de cabecera 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__) 

Esto hace que sea posible para volver hasta cuatro variables a partir de una función y asignarlos a un máximo de cuatro variables. A modo de ejemplo, se puede utilizar como esto:

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

Esto es lo que se imprime:

(55, 3.9, 24.15) 

La solución puede no ser tan portátil, ya que requiere C99 o posterior para macros variadic y para- declaraciones de variables de declaración. Pero creo que fue lo suficientemente interesante para publicar aquí. Otro problema es que el compilador no le advertirá si les asigna los valores incorrectos, por lo que debe tener cuidado.

Ejemplos adicionales, y una versión basada en pila del código que usa uniones, están disponibles en mi github repository.

+0

Un problema de portabilidad más grande es la característica no estándar '__typeof__' –

+0

En lugar de typeof, probablemente podría usar sizeof. Obtenga el tamaño de los valores de retorno y asigne una matriz lo suficientemente grande como para almacenarlos todos. Entonces puedes devolverlo. –

Cuestiones relacionadas