C standard argumenter

stemmer
229

Er det en måte å angi standard argumenter til en funksjon i C?

Publisert på 24/09/2009 klokken 14:38
kilden bruker
På andre språk...                            


19 svar

stemmer
17

Nei, det er en C ++ språket funksjonen.

Svarte 24/09/2009 kl. 14:39
kilden bruker

stemmer
12

Kort svar: Nei.

Litt lenger svare: Det er en gammel, gammel løsning der du passerer en streng som du analysere for valgfrie argumenter:

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

hvor opt kan inkludere "name = verdi" pair eller noe, og som du vil kalle ut

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

Selvfølgelig er dette bare av og til nyttig. Vanligvis når du ønsker et enkelt grensesnitt til en familie av funksjonalitet.


Du fortsatt finne denne tilnærmingen i partikkelfysikk koder som er skrevet av profesjonelle programmer i C ++ (som for eksempel ROOT ). Den viktigste fordelen er at det kan utvides i det uendelige og samtidig opprettholde tilbake kompatibilitet.

Svarte 24/09/2009 kl. 14:39
kilden bruker

stemmer
18

Nei.

Ikke engang den aller nyeste C99 standard støtter dette.

Svarte 24/09/2009 kl. 14:39
kilden bruker

stemmer
115

Ikke egentlig. Den eneste måten ville være å skrive en varargs funksjon og manuelt fylle inn standardverdier for argumenter som den som ringer ikke passere.

Svarte 24/09/2009 kl. 14:40
kilden bruker

stemmer
8

Nei.

Svarte 24/09/2009 kl. 15:01
kilden bruker

stemmer
145

Ja. :-) Men ikke på en måte som du ville forvente.

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

Dessverre, ikke C ikke tillate deg å overbelaste metoder slik at du vil ende opp med to forskjellige funksjoner. Likevel, ved å ringe f2, du vil faktisk være å kalle f1 med en standardverdi. Dette er en "Ikke gjenta deg selv" løsning, som hjelper deg å unngå å kopiere / lime inn eksisterende kode.

Svarte 24/09/2009 kl. 15:06
kilden bruker

stemmer
3

Nei, men du kan vurdere å bruke et sett av funksjoner (eller makroer) for å tilnærme med standard args:

// 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
}
Svarte 24/09/2009 kl. 16:32
kilden bruker

stemmer
3

Vanligvis ikke, men i gcc Du kan ta den siste parameteren for funcA () valgfritt med en makro.

I funcB () i bruk en spesiell verdi (-1) for å signalisere at jeg må standardverdien for den 'b' parameter.

#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) );
}
Svarte 24/09/2009 kl. 16:48
kilden bruker

stemmer
11

Sannsynligvis den beste måten å gjøre dette på (som kan eller ikke kan være mulig i ditt tilfelle, avhengig av situasjonen) er å flytte til C ++ og bruke det som 'en bedre C'. Du kan bruke C ++ uten å bruke klasser, maler, operatør overbelastning eller andre avanserte funksjoner.

Dette vil gi deg en variant av C med funksjon overbelastning og standard parametere (og hva andre funksjoner du velger å bruke). Du må bare være litt disiplinert hvis du er virkelig seriøs om å bruke bare en begrenset delmengde av C ++.

Mange vil si at det er en forferdelig idé å bruke C ++ på denne måten, og de kan ha et poeng. Men det er bare en mening; Jeg tror det er lovlig å bruke funksjonene i C ++ som du er komfortabel med uten å måtte kjøpe inn hele greia. Jeg tror en stor del av årsaken til suksess i C ++ er at det ble brukt av svært mange programmerere i sin tidlige dager på akkurat denne måten.

Svarte 24/09/2009 kl. 18:14
kilden bruker

stemmer
12

Nok et alternativ bruker 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);
Svarte 24/09/2009 kl. 19:49
kilden bruker

stemmer
0

Ja, du kan gjøre somthing simulair, her må du kjenne de ulike argumentlister du kan få, men du har den samme funksjonen for å håndtere da det hele tatt.

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;
    }
}
Svarte 25/09/2009 kl. 11:02
kilden bruker

stemmer
233

Wow, alle er så pessimist rundt her. Svaret er ja.

Det er ikke trivielt: på slutten, vil vi ha kjernefunksjon, en støttende struct, en wrapper funksjon, og en makro rundt wrapper funksjon. I mitt arbeid har jeg et sett med makroer for å automatisere alt dette; når du forstår flyten det vil være enkelt for deg å gjøre det samme.

Jeg har skrevet dette opp andre steder, så her er en detaljert ekstern lenke for å supplere sammendraget her: http://modelingwithdata.org/arch/00000022.htm

Vi ønsker å snu

double f(int i, double x)

inn i en funksjon som tar standardverdier (i = 8, x = 3,14). Definer en følges struct:

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

Endre navn på funksjon f_base, og definerer en wrapper funksjon som setter mislighold og kaller basen:

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

Nå kan du legge en makro, ved hjelp av Cs variadic makroer. På denne måten brukerne ikke trenger å vite de faktisk fyller en f_argsstruct og tror de gjør det vanlige:

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

OK, nå er alle av følgende ville fungere:

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

Sjekk reglene om hvordan sammensatte initializers angi standardinnstillinger for de eksakte regler.

En ting som ikke vil fungere: f(0), fordi vi ikke kan skille mellom en manglende verdi og null. I min erfaring, er dette noe å se opp for, men kan bli tatt vare på som behovet oppstår --- halvparten av tiden din standard egentlig er null.

Jeg gikk gjennom bryet med å skrive dette opp fordi jeg tror navngitte argumenter og mislighold virkelig gjør koding i C enklere og enda mer moro. Og C er kjempebra for å være så enkel og likevel ha nok der for å gjøre dette mulig.

Svarte 28/05/2010 kl. 01:39
kilden bruker

stemmer
4

Ja, med funksjoner av C99 kan du gjøre dette. Dette fungerer uten å definere nye datastrukturer eller så, og uten funksjon å bestemme under kjøring hvor det ble kalt, og uten noen beregnings overhead.

For en detaljert forklaring se min stolpe

http://gustedt.wordpress.com/2010/06/03/default-arguments-for-c99/

Jens

Svarte 14/06/2010 kl. 13:56
kilden bruker

stemmer
36

Vi kan lage funksjoner som bruker navngitte parametere (kun) for standardverdier. Dette er en videreføring av bk. Svar.

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

Den C99-standarden definerer som senere navnene i initialisering styre tidligere elementer. Vi kan også ha noen standard posisjonsparametere også, bare endre makro og funksjon signatur deretter. Standardverdien parametrene kan bare brukes i navngitt parameter stil.

Program utgang:

1 2 3 4 5 6 7 8 9 10 
2 3 4 
1 3 5 7 9
Svarte 29/10/2011 kl. 05:14
kilden bruker

stemmer
19

OpenCV bruker noe sånt som:

/* 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));

Hvis brukeren ikke vet hva han skal skrive, kan dette trikset være nyttig:

eksempel på bruk

Svarte 18/12/2012 kl. 19:38
kilden bruker

stemmer
-4

Hvorfor kan vi ikke gjøre dette.

Gi valgfritt argument en standardverdi. På den måten trenger den som ringer til funksjonen ikke nødvendigvis trenger å passere verdien av argumentet. Argumentet tar standardverdien. Og lett som argument blir valgfritt for kunden.

for eksempel

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

Her b er en valgfri argument.

Svarte 22/08/2013 kl. 11:46
kilden bruker

stemmer
1

Jeg forbedret Jens Gustedt er svaret slik at:

  1. inline funksjoner blir ikke benyttet
  2. mislighold er beregnet under forbehandling
  3. modulære gjenanvendbare makroer
  4. mulig å sette kompilator feil som gir en relevant sams tilfelle av utilstrekkelig argumenter for de tillatte standardverdiene
  5. Standardverdiene er ikke nødvendig for å danne den bakre ende av parameterlisten om argumentet typer vil være utvetydig
  6. interopts med C11 _Generic
  7. variere funksjonsnavnet med antall argumenter!

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

Forenklet bruk scenario:

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__)

Og med _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__))

Og med variadic funksjon markering, som ikke kan kombineres med _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__)
Svarte 18/11/2015 kl. 17:51
kilden bruker

stemmer
0

JA

gjennom makroer

3 Parametere:

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

Hvis du ønsker fjerde argument, da en ekstra my_func3 må legges. Legg merke til endringene i VAR_FUNC, my_func2 og my_func

4 Parametere:

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

Eneste unntak som flyter variablene kan ikke gis standardverdier ( med mindre hvis det er det siste argumentet som i 3 parametre tilfellet ), fordi de trenger perioden ( ''), som ikke er akseptert innen makro argumenter. Men kan regne ut en omgå som sett i my_func2 makro ( 4 parametre tilfelle )

Program

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

Produksjon:

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  
Svarte 23/07/2018 kl. 08:06
kilden bruker

stemmer
1

Et annet triks ved hjelp av makroer:

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

Hvis bare ett argument er passert, bfår standardverdien (i dette tilfelle 15)

Svarte 02/10/2018 kl. 14:12
kilden bruker

Cookies help us deliver our services. By using our services, you agree to our use of cookies. Learn more