Gibt es eine Möglichkeit, Standardargumente für eine Funktion in C anzugeben?
Gibt es eine Möglichkeit, Standardargumente für eine Funktion in C anzugeben?
Antworten:
Nicht wirklich. Die einzige Möglichkeit wäre , eine varargs-Funktion zu schreiben und Standardwerte für Argumente, die der Aufrufer nicht übergibt, manuell einzugeben .
open(2)
Systemaufruf verwendet dies für ein optionales Argument, das abhängig von den erforderlichen Argumenten vorhanden sein kann, und printf(3)
liest eine Formatzeichenfolge, die angibt, wie viele Argumente vorhanden sein werden. Beide verwenden Varargs ziemlich sicher und effektiv, und obwohl man sie sicherlich vermasseln kann, printf()
scheint sie besonders beliebt zu sein.
Wow, hier ist jeder so ein Pessimist. Die Antwort ist ja.
Es ist nicht trivial: Am Ende haben wir die Kernfunktion, eine unterstützende Struktur, eine Wrapper-Funktion und ein Makro um die Wrapper-Funktion. In meiner Arbeit habe ich eine Reihe von Makros, um all dies zu automatisieren. Sobald Sie den Ablauf verstanden haben, fällt es Ihnen leicht, dasselbe zu tun.
Ich habe dies an anderer Stelle geschrieben. Hier ist ein detaillierter externer Link, der die Zusammenfassung hier ergänzt: http://modelingwithdata.org/arch/00000022.htm
Wir möchten uns umdrehen
double f(int i, double x)
in eine Funktion, die Standardeinstellungen übernimmt (i = 8, x = 3,14). Definieren Sie eine Begleitstruktur:
typedef struct {
int i;
double x;
} f_args;
Benennen Sie Ihre Funktion um f_base
und definieren Sie eine Wrapper-Funktion, die Standardeinstellungen 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 hinzu, indem Sie die variadischen Makros von C verwenden. Auf diese Weise müssen Benutzer nicht wissen, dass sie tatsächlich eine f_args
Struktur füllen, und denken, dass sie das Übliche tun:
#define f(...) var_f((f_args){__VA_ARGS__});
OK, jetzt würde alles 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 Standardeinstellungen für die genauen Regeln festlegen.
Eine Sache, die nicht funktioniert: f(0)
weil wir nicht zwischen einem fehlenden Wert und Null unterscheiden können. Nach meiner Erfahrung ist dies etwas, auf das Sie achten müssen, das Sie jedoch bei Bedarf erledigen können - die Hälfte der Zeit, in der Ihr Standardwert wirklich Null ist.
Ich habe mir die Mühe gemacht, dies aufzuschreiben, weil ich denke, dass benannte Argumente und Standardeinstellungen das Codieren in C wirklich einfacher machen und noch mehr Spaß machen. Und C ist großartig, weil es so einfach ist und immer noch genug da ist, um all dies zu ermöglichen.
{}
(leerer Initialisierer) ein Fehler C99 ist.
#define vrange(...) CALL(range,(param){.from=1, .to=100, .step=1, __VA_ARGS__})
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 können Sie mit C keine Methoden überladen, sodass Sie am Ende zwei verschiedene Funktionen haben. Wenn Sie jedoch f2 aufrufen, rufen Sie tatsächlich f1 mit einem Standardwert auf. Dies ist eine "Wiederholen Sie sich nicht" -Lösung, mit der Sie vermeiden können, vorhandenen Code zu kopieren / einzufügen.
Wir können Funktionen erstellen, die benannte Parameter (nur) 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 Standard-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
OpenCV verwendet so 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:
Nein.
Nicht einmal der neueste C99-Standard unterstützt dies.
Kurze Antwort: Nein.
Etwas längere Antwort: Es gibt eine alte, alte Problemumgehung, bei der Sie eine Zeichenfolge übergeben, die Sie nach optionalen Argumenten analysieren :
int f(int arg1, double arg2, char* name, char *opt);
Dabei kann opt ein Paar "Name = Wert" oder etwas anderes enthalten, das Sie gerne aufrufen würden
n = f(2,3.0,"foo","plot=yes save=no");
Offensichtlich ist dies nur gelegentlich nützlich. Im Allgemeinen, wenn Sie eine einzelne Schnittstelle zu einer Funktionsfamilie wünschen.
Sie finden diesen Ansatz immer noch in Teilchenphysik-Codes, die von professionellen Programmen in C ++ (wie zum Beispiel ROOT ) geschrieben wurden. Der Hauptvorteil besteht darin, dass es nahezu unbegrenzt verlängert werden kann, während die Rückenkompatibilität erhalten bleibt.
struct
und den Anrufer eine erstellen lassen, die Felder für verschiedene Optionen ausfüllen und sie dann als Adresse übergeben oder NULL
als Standardoptionen übergeben.
Der wahrscheinlich beste Weg, dies zu tun (was in Ihrem Fall je nach Ihrer Situation möglich oder nicht möglich ist), besteht darin, zu C ++ zu wechseln und es als "besseres C" zu 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 allen anderen Funktionen, die Sie verwendet haben). Sie müssen nur ein wenig diszipliniert sein, wenn Sie es wirklich ernst meinen, nur eine eingeschränkte Teilmenge von C ++ zu verwenden.
Viele Leute werden sagen, dass es eine schreckliche Idee ist, C ++ auf diese Weise zu verwenden, und sie könnten einen Punkt haben. Aber es ist nur eine Meinung; Ich denke, es ist gültig, Funktionen von C ++ zu verwenden, mit denen Sie vertraut sind, ohne sich auf das Ganze einlassen zu müssen. Ich denke, ein wesentlicher Grund für den Erfolg von C ++ ist, dass es in den Anfängen von sehr vielen Programmierern genau auf diese Weise verwendet wurde.
Eine weitere Option verwendet struct
s:
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);
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, wird b
der Standardwert empfangen (in diesem Fall 15).
Nein, aber Sie können eine Reihe von Funktionen (oder Makros) verwenden, um die Standardargumente 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
}
Ja, mit den Funktionen von C99 können Sie dies tun. Dies funktioniert ohne Definition neuer Datenstrukturen 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
Im Allgemeinen nein, aber in gcc Sie können den letzten Parameter von funcA () mit einem Makro optional machen.
In funcB () verwende 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) );
}
Ich habe Jens Gustedts Antwort so verbessert , dass:
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 Funktionsnamenauswahl, 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__)
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 4. Argument möchten, muss ein zusätzliches 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, dass Float- Variablen keine Standardwerte zugewiesen werden können ( es sei denn, es handelt sich um das letzte Argument wie im Fall mit 3 Parametern ), da sie einen Punkt ('.') Benötigen, der in Makroargumenten nicht akzeptiert wird. Kann aber eine Problemumgehung finden, wie im Makro my_func2 ( Fall mit 4 Parametern) zu sehen )
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
Ja, Sie können etwas Simulaires tun. Hier müssen Sie die verschiedenen Argumentlisten kennen, die Sie erhalten können, aber Sie haben dieselbe Funktion wie alle anderen.
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;
}
}
Warum können wir das nicht tun?
Geben Sie dem optionalen Argument einen Standardwert. Auf diese Weise muss der Aufrufer der Funktion nicht unbedingt den Wert des Arguments übergeben. Das Argument nimmt den Standardwert an. Und leicht wird dieses Argument für den Kunden optional.
Zum Beispiel
void foo (int a, int b = 0);
Hier ist b ein optionales Argument.