2010-04-12 20 views
59

Se ho una funzione che produce un risultato int e un risultato string, come posso restituirli entrambi da una funzione?Come posso restituire più valori da una funzione in C?

Per quanto posso dire posso solo restituire una cosa, come determinato dal tipo che precede il nome della funzione.

+6

Con 'string' vuoi dire" Sto usando C++ e questa è la classe 'std :: string'" o "Sto usando C e questo è un puntatore' char * 'o' char [] ' matrice ". –

+0

beh, nel mio caso particolare, erano due inte: uno per il 'punteggio' di quello che stavo confrontando, e uno per l''indice' di dove è stato trovato quel punteggio massimo. volevo usare un esempio di stringa qui solo per il caso più generale – sepiroth

+0

Passare la stringa per riferimento e restituire l'int. Il modo più veloce. Nessuna struttura richiesta. –

risposta

86

Non so cosa sia il tuo string, ma assumerò che gestisca la propria memoria.

Avete due soluzioni:

1: restituire un struct che contiene tutti i tipi di cui avete bisogno.

struct Tuple { 
    int a; 
    string b; 
}; 

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

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

2: utilizzare i puntatori per passare i valori.

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

Quale si sceglie di utilizzare dipende in gran parte dalle preferenze personali su qualsiasi semantica che ti piace di più.

+6

Penso che dipenda più da quanto sono correlati i valori di ritorno. Se int è un codice di errore e la stringa è un risultato, questi non dovrebbero essere messi insieme in una struttura. È semplicemente sciocco. In tal caso, restituirò int e passerò la stringa come 'char *' e a 'size_t' per la lunghezza a meno che non sia assolutamente vitale per la funzione allocare la propria stringa e/o restituire' NULL'. –

+0

@Chris Sono d'accordo con te completamente, ma non ho idea della semantica d'uso delle variabili di cui ha bisogno. –

3

Creare una struttura e impostare due valori all'interno e restituire la variabile struct.

struct result { 
    int a; 
    char *string; 
} 

si deve allocare lo spazio per l'char * nel programma.

2

Utilizzare i puntatori come parametri di funzione. Quindi usarli per restituire più valori.

5

due diversi approcci:

  1. Passo nei tuoi valori di ritorno da puntatore, e modificarli all'interno della funzione. Dichiari la tua funzione come nulla, ma sta ritornando tramite i valori passati come puntatori.
  2. Definire una struttura che aggrega i valori restituiti.

Penso che # 1 sia un po 'più ovvio su quello che sta succedendo, anche se può diventare noioso se si hanno troppi valori di ritorno. In tal caso, l'opzione n. 2 funziona abbastanza bene, sebbene ci sia un sovraccarico mentale nel creare strutture specializzate per questo scopo.

+1

C non ha riferimenti ;-), sebbene dal momento in cui il poster utilizza 'string', potrebbe essere sicuro assumere C++ ... –

+0

Completamente dimenticato! Ho modificato la mia risposta per usare i puntatori, ma sono stato chiaramente in terra C++ per troppo tempo. :) –

4

Poiché uno dei tipi di risultato è una stringa (e si utilizza C, non C++), si consiglia di passare i puntatori come parametri di output. Usa:

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

e chiamare in questo modo:

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

In generale, preferiscono fare l'allocazione nel chiamare funzione, non all'interno della funzione stessa, in modo da poter essere il più aperto il più possibile per diverse strategie di allocazione.

6

Option 1: dichiara una struttura con int e stringa e restituisce una variabile struct.

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

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

Option 2: È possibile passare uno dei due tramite puntatore e modificare il parametro attuale attraverso il puntatore e restituite l'altra, come al solito:

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: simile all'opzione 2. È possibile passare entrambi tramite puntatore e non restituire nulla dalla funzione:

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

Per quanto riguarda l'opzione 2, è necessario anche inserire la lunghezza della stringa. 'int fun (char * param, size_t len)' –

+0

Ora dipende da cosa sta facendo la funzione. Se sappiamo quale tipo di risultato la funzione entra nel char array, potremmo semplicemente allocare abbastanza spazio per esso e passarlo alla funzione. Non c'è bisogno di passare la lunghezza. Qualcosa di simile a come usiamo 'strcpy' per esempio. – codaddict

1

Passando i parametri con riferimento alla funzione.

Esempi:

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

anche utilizzando le variabili globali, ma non è raccomandato.

Esempio:

int a=0; 

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

'void main (void)' OH COME SI BRUCIA! –

+0

cosa intendi? @ Chris Lutz – Badr

+3

'main' dovrebbe restituire un codice di stato. Sotto * nix, è più normale dichiararlo come 'int main (int argc, char * argv [])', credo che Windows abbia convenzioni simili. – Duncan

-3

Hey Pietro I beleive è possibile restituire 2 valori con l'aiuto di puntatori in C.

1

Un approccio è quello di utilizzare le macro. Mettere questo in un file di intestazione 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__) 

questo modo è possibile per tornare fino a quattro variabili da una funzione e assegnarli a un massimo di quattro variabili. A titolo di esempio, è possibile utilizzare in questo modo:

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

Questo è ciò che la stampa:

(55, 3.9, 24.15) 

La soluzione non può essere il più portabile, perché richiede C99 o poi per le macro variadic e per- dichiarazione dichiarazioni variabili. Ma penso che sia stato abbastanza interessante postare qui. Un altro problema è che il compilatore non ti avviserà se assegni loro valori errati, quindi devi stare attento.

Altri esempi e una versione basata su stack del codice che utilizza i sindacati sono disponibili sul mio github repository.

+0

Un problema di portabilità più grande è la caratteristica non standard '__typeof__' –

+0

Invece di typeof, probabilmente si potrebbe usare sizeof. Ottieni la dimensione dei valori di ritorno e alloca un array abbastanza grande da archiviarli tutti. Quindi puoi restituirlo. –

Problemi correlati