it-swarm.com.de

C Standardargumente

Gibt es eine Möglichkeit, einer Funktion in C Standardargumente anzugeben?

243
Nathaniel Flath

Nicht wirklich. Die einzige Möglichkeit wäre, eine Varargs-Funktion zu schreiben und manuell Standardwerte für Argumente einzugeben, die der Aufrufer nicht übergeben kann.

125
Eli Courtwright

Wow, jeder hier ist so ein Pessimist. Die Antwort ist ja. 

Das ist nicht trivial: Am Ende haben wir die Kernfunktion, eine unterstützende Struktur, eine Wrapperfunktion und ein Makro Um die Wrapperfunktion. In meiner Arbeit habe ich eine Reihe von Makros, um all dies zu automatisieren; Sobald Sie den Fluss verstehen, wird es Ihnen leicht fallen, das Gleiche zu tun.

Ich habe dies an anderer Stelle geschrieben. Hier ist ein ausführlicher externer Link, um die Zusammenfassung hier zu ergänzen: http://modelingwithdata.org/Arch/00000022.htm

Wir möchten uns wenden

double f(int i, double x)

in eine Funktion, die Standardwerte übernimmt (i = 8, x = 3.14). Definieren Sie eine Begleitstruktur:

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

Benennen Sie Ihre Funktion in f_base um und definieren Sie eine Wrapper-Funktion, die Standardwerte festlegt und Die Basis aufruft:

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

Fügen Sie nun ein Makro mit den variadischen Makros von C hinzu. Auf diese Weise müssen Benutzer nicht wissen, dass sie Tatsächlich eine f_args-Struktur bevölkern, und glauben, dass sie das übliche tun:

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

OK, jetzt würde alles folgende funktionieren:

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

Überprüfen Sie die Regeln, wie zusammengesetzte Initialisierer die genauen Regeln festlegen.

Eine Sache, die nicht funktioniert: f(0), da wir nicht zwischen einem fehlenden Wert und __.Zero unterscheiden können. Nach meiner Erfahrung ist dies etwas, auf das Sie achten sollten, aber es kann darauf geachtet werden, dass __ die Notwendigkeit entsteht - die Hälfte der Zeit, in der Ihr Standardwert tatsächlich Null ist.

Ich habe mir die Mühe gemacht, dies aufzuschreiben, weil ich denke, benannte Argumente und Standardwerte macht das Codieren in C wirklich einfacher und macht noch mehr Spaß. Und C ist fantastisch, weil er so einfach ist und immer noch genug da ist, um all dies zu ermöglichen.

247
bk.

Ja. :-) Aber nicht so, wie man es erwarten würde.

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

Leider erlaubt es C nicht, Methoden zu überladen, sodass am Ende zwei verschiedene Funktionen zur Verfügung stehen. Wenn Sie jedoch f2 aufrufen, würden Sie tatsächlich f1 mit einem Standardwert aufrufen. Dies ist eine "Don't Repeat Yourself" -Lösung, mit der Sie das Kopieren/Einfügen von vorhandenem Code vermeiden.

151
Wim ten Brink

Wir können Funktionen erstellen, die (nur) benannte Parameter für Standardwerte verwenden. Dies ist eine Fortsetzung der Antwort von bk.

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

Der C99-Standard definiert, dass spätere Namen in der Initialisierung vorherige Elemente überschreiben. Wir können auch einige standardmäßige Positionsparameter haben, ändern Sie einfach das Makro und die Funktionssignatur entsprechend. Die Standardwertparameter können nur im benannten Parameterstil verwendet werden.

Programmausgabe:

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

OpenCV benutzt etwas wie:

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

Wenn der Benutzer nicht weiß, was er schreiben soll, kann dieser Trick hilfreich sein:

usage example

20
user1129665

Nein.

Nicht einmal der neueste C99-Standard unterstützt dies.

18
unwind

Nein, das ist eine C++ - Sprachfunktion.

17
Alex B

Eine weitere Option verwendet 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);
13
Chris Lutz

Kurze Antwort: Nein.

Etwas längere Antwort: Es gibt einen alten, alten Workaround, bei dem Sie eine Zeichenfolge übergeben, die Sie (parse für optionale Argumente übergeben:

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

dabei kann opt ein "name = value" -Paar oder etwas enthalten, und das würden Sie gerne anrufen

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

Dies ist offensichtlich nur gelegentlich nützlich. Im Allgemeinen, wenn Sie eine einzelne Schnittstelle für eine Funktionsfamilie benötigen.


Sie finden diesen Ansatz immer noch in Partikelphysik-Codes, die von professionellen Programmen in c ++ geschrieben werden (wie zum Beispiel ROOT ). Der Hauptvorteil besteht darin, dass es unter Beibehaltung der Back-Kompatibilität nahezu unbegrenzt erweitert werden kann.

13
dmckee

Möglicherweise ist dies der beste Weg (dies kann je nach Situation in Ihrem Fall möglicherweise nicht möglich sein), indem Sie zu C++ wechseln und es als "besseres C" verwenden. Sie können C++ verwenden, ohne Klassen, Vorlagen, Operatorüberladung oder andere erweiterte Funktionen zu verwenden.

Dadurch erhalten Sie eine Variante von C mit Funktionsüberladung und Standardparametern (und den anderen von Ihnen gewählten Funktionen). Sie müssen nur ein wenig diszipliniert sein, wenn Sie wirklich nur eine eingeschränkte Teilmenge von C++ verwenden möchten.

Viele Leute werden sagen, dass es eine schreckliche Idee ist, C++ auf diese Weise zu verwenden, und sie haben vielleicht einen Punkt. Aber es ist nur eine Meinung; Ich denke, es ist gültig, Funktionen von C++ zu verwenden, mit denen Sie sich auskennen, ohne sich in das Ganze einkaufen zu müssen. Ich denke, dass ein wesentlicher Grund für den Erfolg von C++ darin liegt, dass es auf die gleiche Art und Weise von sehr vielen Programmierern in den Anfangszeiten verwendet wurde.

12
Michael Burr

Nein. 

8
chaos

Ja, mit den Funktionen von C99 können Sie dies tun. Dies funktioniert, ohne neue Datenstrukturen zu definieren oder so und ohne, dass die Funktion zur Laufzeit entscheiden muss, wie sie aufgerufen wurde, und Ohne Rechenaufwand.

Eine ausführliche Erklärung finden Sie in meinem Beitrag unter

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

Jens

4
Jens Gustedt

Nein, Sie können jedoch die Verwendung von set von Funktionen (oder Makros) in Betracht ziehen, um die Standardeinstellungen zu verwenden:

// 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
}
3
David R Tribble

Generell nein, aber in gcc Sie können den letzten Parameter von funcA () mit einem Makro optional machen.

In funcB () benutze ich einen speziellen Wert (-1), um zu signalisieren, dass ich den Standardwert für den Parameter 'b' benötige.

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

Ich habe Jens Gustedts answer so verbessert, dass:

  1. inline-Funktionen werden nicht eingesetzt
  2. standardwerte werden während der Vorverarbeitung berechnet
  3. wiederverwendbare modulare Makros
  4. es ist möglich, einen Compiler-Fehler festzulegen, der dem Fall unzureichender Argumente für die zulässigen Standardwerte entspricht
  5. die Standardwerte sind nicht erforderlich, um das Ende der Parameterliste zu bilden, wenn die Argumenttypen eindeutig sind
  6. interopts mit C11 _Generic
  7. variieren Sie den Funktionsnamen um die Anzahl der Argumente!

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

Vereinfachtes Nutzungsszenario:

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

Und mit _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__))

Und mit variadischer Funktionsnamensauswahl, die nicht mit _Generic kombiniert werden kann:

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

Ein weiterer Trick mit Makros:

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

Wenn nur ein Argument übergeben wird, erhält b den Standardwert (in diesem Fall 15).

1
Keine Lust

JA

Durch Makros

3 Parameter: 

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

Wenn Sie das vierte Argument wünschen, muss ein zusätzlicher my_func3 hinzugefügt werden. Beachten Sie die Änderungen in VAR_FUNC, my_func2 und my_func

4 Parameter: 

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

Einzige Ausnahme das float Variablen können keine Standardwerte zugewiesen werden (es sei denn, es ist das letzte Argument wie in dem Fall mit 3 Parametern), weil sie einen Punkt ('.') benötigen, der von Makro-Argumenten nicht akzeptiert wird. Kann aber eine Umgehung finden, wie in my_func2-Makro (von 4 Parametern Fall)

Programm

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

Ausgabe:

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  
0
Ramu

Ja, Sie können etwas Ähnliches tun, hier müssen Sie die verschiedenen Argumentlisten kennen, die Sie bekommen können, aber Sie haben die gleiche Funktion, um alle zu behandeln.

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