Domanda

C'è un modo per specificare gli argomenti di default a una funzione in C?

È stato utile?

Soluzione

Non proprio. L'unico modo sarebbe scrivere una funzione varargs e compilare manualmente i valori predefiniti per argomenti il chiamante non passa.

Altri suggerimenti

Wow, tutti sono così pessimista da queste parti. La risposta è si.

Non è banale: alla fine, avremo la funzione principale, una struttura di supporto, una funzione involucro, e una macro intorno alla funzione avvolgitore. Nel mio lavoro ho un insieme di macro per automatizzare tutto questo; una volta si capisce il flusso sarà facile per voi di fare lo stesso.

ho scritto questo altrove, quindi ecco un link esterno dettagliata per integrare il riepilogo qui: http: //modelingwithdata.org/arch/00000022.htm

Ci piacerebbe girare

double f(int i, double x)

in una funzione che prende default (i = 8, x = 3,14). Definire una struttura compagna:

typedef struct {
    int i;
    double x;
} f_args;

Rinomina la funzione f_base, e definire una funzione wrapper che imposta i default e le chiamate la base:

double var_f(f_args in){
    int i_out = in.i ? in.i : 8;
    double x_out = in.x ? in.x : 3.14;
    return f_base(i_out, x_out);
}

Ora aggiungere una macro, utilizzando le macro variadic di C. Questo modo gli utenti non devono sapere che sono in realtà la compilazione di una struct f_args e pensare che stanno facendo il solito:

#define f(...) var_f((f_args){__VA_ARGS__});

OK, ora tutte le seguenti avrebbe funzionato:

f(3, 8);      //i=3, x=8
f(.i=1, 2.3); //i=1, x=2.3
f(2);         //i=2, x=3.14
f(.x=9.2);    //i=8, x=9.2

Controlla le regole su come inizializzatori composto impostare i valori predefiniti per le regole esatte.

Una cosa che non funziona: f(0), perché non possiamo distinguere tra un valore mancante e zero. Nella mia esperienza, questo è qualcosa da guardare fuori per, ma può essere curato come si presenta la necessità --- metà del tempo predefinito è davvero pari a zero.

Sono andato attraverso la briga di scrivere questo in su perché penso che gli argomenti e le impostazioni predefinite con nome  davvero fare la codifica in C più facile e ancora più divertente. E C è impressionante per essere così semplice e avere ancora abbastanza lì per rendere possibile tutto questo.

Sì. :-) Ma non in un modo che ci si aspetterebbe.

int f1(int arg1, double arg2, char* name, char *opt);

int f2(int arg1, double arg2, char* name)
{
  return f1(arg1, arg2, name, "Some option");
}

Purtroppo, C non consente di sovraccaricare i metodi in modo che ci si finisce con due funzioni diverse. Eppure, chiamando f2, si sarebbe effettivamente essere chiamando f1 con un valore predefinito. Questa è una soluzione "Non Repeat Yourself", che vi aiuta a evitare di copiare / incollare il codice esistente.

Siamo in grado di creare funzioni che utilizzano parametri denominati (solo) per i valori di default. Questa è una continuazione della risposta bk di..

#include <stdio.h>                                                               

struct range { int from; int to; int step; };
#define range(...) range((struct range){.from=1,.to=10,.step=1, __VA_ARGS__})   

/* use parentheses to avoid macro subst */             
void (range)(struct range r) {                                                     
    for (int i = r.from; i <= r.to; i += r.step)                                 
        printf("%d ", i);                                                        
    puts("");                                                                    
}                                                                                

int main() {                                                                     
    range();                                                                    
    range(.from=2, .to=4);                                                      
    range(.step=2);                                                             
}    

Lo standard C99 definisce che i nomi più tardi nella inizializzazione sostituiscono voci precedenti. Possiamo anche avere alcuni parametri posizionali standard così, basta cambiare la firma macro e la funzione di conseguenza. I parametri di valore predefinito può essere utilizzato solo in stile parametro denominato.

l'output del programma:

1 2 3 4 5 6 7 8 9 10 
2 3 4 
1 3 5 7 9

OpenCV utilizza qualcosa come:

/* in the header file */

#ifdef __cplusplus
    /* in case the compiler is a C++ compiler */
    #define DEFAULT_VALUE(value) = value
#else
    /* otherwise, C compiler, do nothing */
    #define DEFAULT_VALUE(value)
#endif

void window_set_size(unsigned int width  DEFAULT_VALUE(640),
                     unsigned int height DEFAULT_VALUE(400));

Se l'utente non sa quello che dovrebbe scrivere, questo trucco può essere utile:

esempio di utilizzo

No.

Nemmeno il più recente standard di C99 supporta questa.

No, questa è una caratteristica del linguaggio C ++.

Probabilmente il modo migliore per fare questo (che può o non può essere possibile nel vostro caso a seconda della situazione) è quello di spostare a C ++ e usarlo come 'una migliore C'. È possibile utilizzare C ++ senza utilizzare le classi, i modelli, l'overloading degli operatori o di altre funzioni avanzate.

Questo vi darà una variante del C con parametri funzione di sovraccarichi e di default (e qualsiasi altre caratteristiche si è scelto di utilizzare). Devi solo essere un po 'disciplinato, se siete veramente sul serio usando solo un sottoinsieme limitato di C ++.

Un sacco di gente dire che è una pessima idea di utilizzare C ++ in questo modo, e che potrebbe avere un punto. Ma è è solo un parere; Penso che sia valida per utilizzare le funzioni di C ++ che sei a tuo agio con senza dover comprare in tutta la faccenda. Credo che una parte significativa del motivo per il sucesso di C ++ è che siamo abituati da un sacco di programmatori muovendo i suoi primi giorni esattamente in questo modo.

Risposta breve:. No

risposta Leggermente più lungo: C'è un vecchio, vecchia soluzione in cui si passa una stringa che si parse per gli argomenti opzionali:

int f(int arg1, double arg2, char* name, char *opt);

dove opt può includere "nome = valore" coppia o qualcosa del genere, e che si potrebbe chiamare come

n = f(2,3.0,"foo","plot=yes save=no");

Ovviamente questo è solo occasionalmente utile. In genere quando si desidera un'unica interfaccia per una famiglia di funzionalità.


È ancora trovare questo approccio nei codici fisica delle particelle che sono scritti da programmi professionali in C ++ (come per esempio ROOT ). E 'vantaggio principale è che può essere esteso quasi all'infinito, pur mantenendo la compatibilità di nuovo.

Ancora un'altra opzione utilizza structs:

struct func_opts {
  int    arg1;
  char * arg2;
  int    arg3;
};

void func(int arg, struct func_opts *opts)
{
    int arg1 = 0, arg3 = 0;
    char *arg2 = "Default";
    if(opts)
      {
        if(opts->arg1)
            arg1 = opts->arg1;
        if(opts->arg2)
            arg2 = opts->arg2;
        if(opts->arg3)
            arg3 = opts->arg3;
      }
    // do stuff
}

// call with defaults
func(3, NULL);

// also call with defaults
struct func_opts opts = {0};
func(3, &opts);

// set some arguments
opts.arg3 = 3;
opts.arg2 = "Yes";
func(3, &opts);

No.

No, ma si potrebbe considerare l'utilizzo di un set di funzioni (o macro) per approssimare con args predefinite:

// No default args
int foo3(int a, int b, int c)
{
    return ...;
}

// Default 3rd arg
int foo2(int a, int b)
{
    return foo3(a, b, 0);  // default c
}

// Default 2nd and 3rd args
int foo1(int a)
{
    return foo3(a, 1, 0);  // default b and c
}

Si, con le caratteristiche di C99 si può fare questo. Questo funziona senza definire nuove strutture di dati o giù di lì e senza la funzione di dover decidere in fase di esecuzione come è stato chiamato, e senza alcun carico computazionale.

Per una spiegazione dettagliata si veda il mio post a

http://gustedt.wordpress.com/ 2010/06/03 / default-argomenti-per-C99 /

Jens

In genere no, ma in gcc È possibile effettuare l'ultimo parametro di funcA () opzionale con una macro.

In funcB () uso un valore speciale (-1) per segnalare che ho bisogno del valore predefinito per il parametro 'b'.

#include <stdio.h> 

int funcA( int a, int b, ... ){ return a+b; }
#define funcA( a, ... ) funcA( a, ##__VA_ARGS__, 8 ) 


int funcB( int a, int b ){
  if( b == -1 ) b = 8;
  return a+b;
}

int main(void){
  printf("funcA(1,2): %i\n", funcA(1,2) );
  printf("funcA(1):   %i\n", funcA(1)   );

  printf("funcB(1, 2): %i\n", funcB(1, 2) );
  printf("funcB(1,-1): %i\n", funcB(1,-1) );
}

rispondere in modo che:

  1. funzioni inline non sono impiegati
  2. impostazioni predefinite sono calcolati nel corso di pre-elaborazione
  3. macro riutilizzabili modulari
  4. possibile impostare errore del compilatore che corrisponde per significato caso di argomenti insufficiente per i valori predefiniti consentiti
  5. le impostazioni predefinite non sono tenuti a formare la coda della lista di parametri se i tipi di argomenti rimarranno inequivocabile
  6. interopts con C11 _Generic
  7. variare il nome della funzione per il numero di argomenti!

variadic.h:

#ifndef VARIADIC

#define _NARG2(_0, _1, _2, ...) _2
#define NUMARG2(...) _NARG2(__VA_ARGS__, 2, 1, 0)
#define _NARG3(_0, _1, _2, _3, ...) _3
#define NUMARG3(...) _NARG3(__VA_ARGS__, 3, 2, 1, 0)
#define _NARG4(_0, _1, _2, _3, _4, ...) _4
#define NUMARG4(...) _NARG4(__VA_ARGS__, 4, 3, 2, 1, 0)
#define _NARG5(_0, _1, _2, _3, _4, _5, ...) _5
#define NUMARG5(...) _NARG5(__VA_ARGS__, 5, 4, 3, 2, 1, 0)
#define _NARG6(_0, _1, _2, _3, _4, _5, _6, ...) _6
#define NUMARG6(...) _NARG6(__VA_ARGS__, 6, 5, 4, 3, 2, 1, 0)
#define _NARG7(_0, _1, _2, _3, _4, _5, _6, _7, ...) _7
#define NUMARG7(...) _NARG7(__VA_ARGS__, 7, 6, 5, 4, 3, 2, 1, 0)
#define _NARG8(_0, _1, _2, _3, _4, _5, _6, _7, _8, ...) _8
#define NUMARG8(...) _NARG8(__VA_ARGS__, 8, 7, 6, 5, 4, 3, 2, 1, 0)
#define _NARG9(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, ...) _9
#define NUMARG9(...) _NARG9(__VA_ARGS__, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0)
#define __VARIADIC(name, num_args, ...) name ## _ ## num_args (__VA_ARGS__)
#define _VARIADIC(name, num_args, ...) name (__VARIADIC(name, num_args, __VA_ARGS__))
#define VARIADIC(name, num_args, ...) _VARIADIC(name, num_args, __VA_ARGS__)
#define VARIADIC2(name, num_args, ...) __VARIADIC(name, num_args, __VA_ARGS__)

// Vary function name by number of arguments supplied
#define VARIADIC_NAME(name, num_args) name ## _ ## num_args ## _name ()
#define NVARIADIC(name, num_args, ...) _VARIADIC(VARIADIC_NAME(name, num_args), num_args, __VA_ARGS__)

#endif

scenario di utilizzo semplificata:

const uint32*
uint32_frombytes(uint32* out, const uint8* in, size_t bytes);

/*
The output buffer defaults to NULL if not provided.
*/

#include "variadic.h"

#define uint32_frombytes_2(   b, c) NULL, b, c
#define uint32_frombytes_3(a, b, c)    a, b, c
#define uint32_frombytes(...) VARIADIC(uint32_frombytes, NUMARG3(__VA_ARGS__), __VA_ARGS__)

E con _Generic:

const uint8*
uint16_tobytes(const uint16* in, uint8* out, size_t bytes);

const uint16*
uint16_frombytes(uint16* out, const uint8* in, size_t bytes);

const uint8*
uint32_tobytes(const uint32* in, uint8* out, size_t bytes);

const uint32*
uint32_frombytes(uint32* out, const uint8* in, size_t bytes);

/*
The output buffer defaults to NULL if not provided.
Generic function name supported on the non-uint8 type, except where said type
is unavailable because the argument for output buffer was not provided.
*/

#include "variadic.h"

#define   uint16_tobytes_2(a,    c) a, NULL, c
#define   uint16_tobytes_3(a, b, c) a,    b, c
#define   uint16_tobytes(...) VARIADIC(  uint16_tobytes, NUMARG3(__VA_ARGS__), __VA_ARGS__)

#define uint16_frombytes_2(   b, c) NULL, b, c
#define uint16_frombytes_3(a, b, c)    a, b, c
#define uint16_frombytes(...) VARIADIC(uint16_frombytes, NUMARG3(__VA_ARGS__), __VA_ARGS__)

#define   uint32_tobytes_2(a,    c) a, NULL, c
#define   uint32_tobytes_3(a, b, c) a,    b, c
#define   uint32_tobytes(...) VARIADIC(  uint32_tobytes, NUMARG3(__VA_ARGS__), __VA_ARGS__)

#define uint32_frombytes_2(   b, c) NULL, b, c
#define uint32_frombytes_3(a, b, c)    a, b, c
#define uint32_frombytes(...) VARIADIC(uint32_frombytes, NUMARG3(__VA_ARGS__), __VA_ARGS__)

#define   tobytes(a, ...) _Generic((a),                                                                                                 \
                                   const uint16*: uint16_tobytes,                                                                       \
                                   const uint32*: uint32_tobytes)  (VARIADIC2(  uint32_tobytes, NUMARG3(a, __VA_ARGS__), a, __VA_ARGS__))

#define frombytes(a, ...) _Generic((a),                                                                                                 \
                                         uint16*: uint16_frombytes,                                                                     \
                                         uint32*: uint32_frombytes)(VARIADIC2(uint32_frombytes, NUMARG3(a, __VA_ARGS__), a, __VA_ARGS__))

E con selezione nome della funzione variadic, che non può essere combinato con _Generic:

// winternitz() with 5 arguments is replaced with merkle_lamport() on those 5 arguments.

#define   merkle_lamport_5(a, b, c, d, e) a, b, c, d, e
#define   winternitz_7(a, b, c, d, e, f, g) a, b, c, d, e, f, g
#define   winternitz_5_name() merkle_lamport
#define   winternitz_7_name() winternitz
#define   winternitz(...) NVARIADIC(winternitz, NUMARG7(__VA_ARGS__), __VA_ARGS__)

Un altro trucco utilizzando le macro:

#include <stdio.h>

#define func(...) FUNC(__VA_ARGS__, 15, 0)
#define FUNC(a, b, ...) func(a, b)

int (func)(int a, int b)
{
    return a + b;
}

int main(void)
{
    printf("%d\n", func(1));
    printf("%d\n", func(1, 2));
    return 0;
}

Se viene passato un solo argomento, b riceve il valore di default (in questo caso 15)

Sì, è possibile fare qualcosa di simile, qui è necessario conoscere le diverse liste di argomenti che si possono ottenere, ma si ha la stessa funzione di gestirli tutti.

typedef enum { my_input_set1 = 0, my_input_set2, my_input_set3} INPUT_SET;

typedef struct{
    INPUT_SET type;
    char* text;
} input_set1;

typedef struct{
    INPUT_SET type;
    char* text;
    int var;
} input_set2;

typedef struct{
    INPUT_SET type;
    int text;
} input_set3;

typedef union
{
    INPUT_SET type;
    input_set1 set1;
    input_set2 set2;
    input_set3 set3;
} MY_INPUT;

void my_func(MY_INPUT input)
{
    switch(input.type)
    {
        case my_input_set1:
        break;
        case my_input_set2:
        break;
        case my_input_set3:
        break;
        default:
        // unknown input
        break;
    }
}

YES

Con le macro

3 Parametri:

#define my_func2(...) my_func3(__VA_ARGS__, 0.5)
#define my_func1(...) my_func2(__VA_ARGS__, 10)
#define VAR_FUNC(_1, _2, _3, NAME, ...) NAME
#define my_func(...) VAR_FUNC(__VA_ARGS__, my_func3, my_func2, my_func1)(__VA_ARGS__)

void my_func3(char a, int b, float c) // b=10, c=0.5
{
    printf("a=%c; b=%d; c=%f\n", a, b, c);
}

Se si desidera quarto argomento, poi un my_func3 in più deve essere aggiunto. Notare i cambiamenti nella VAR_FUNC, my_func2 e my_func

4 parametri:

#define my_func3(...) my_func4(__VA_ARGS__, "default") // <== New function added
#define my_func2(...) my_func3(__VA_ARGS__, (float)1/2)
#define my_func1(...) my_func2(__VA_ARGS__, 10)
#define VAR_FUNC(_1, _2, _3, _4, NAME, ...) NAME
#define my_func(...) VAR_FUNC(__VA_ARGS__, my_func4, my_func3, my_func2, my_func1)(__VA_ARGS__)

void my_func4(char a, int b, float c, const char* d) // b=10, c=0.5, d="default"
{
    printf("a=%c; b=%d; c=%f; d=%s\n", a, b, c, d);
}

L'unica eccezione che float variabili non possono essere dati i valori di default ( a meno che se è l'ultimo argomento, come nel caso 3 parametri ), perché hanno bisogno di periodo ('. '), che non viene accettata entro argomenti di macro. Ma si può capire un lavoro intorno come visto in macro my_func2 ( di 4 caso parametri )

Programma

int main(void)
{
    my_func('a');
    my_func('b', 20);
    my_func('c', 200, 10.5);
    my_func('d', 2000, 100.5, "hello");

    return 0;
}

Output:

a=a; b=10; c=0.500000; d=default                                                                                                                                                  
a=b; b=20; c=0.500000; d=default                                                                                                                                                  
a=c; b=200; c=10.500000; d=default                                                                                                                                                
a=d; b=2000; c=100.500000; d=hello  

Perché non possiamo farlo.

Dare l'argomento opzionale un valore di default. In questo modo, il chiamante della funzione non hanno necessariamente bisogno per passare il valore dell'argomento. L'argomento assume il valore di default. E facilmente che l'argomento diventa facoltativo per il cliente.

Per es.

void foo (int a, int b = 0);

Ecco b è un argomento opzionale.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top