Question

Y at-il un moyen de spécifier des arguments par défaut à une fonction C?

Était-ce utile?

La solution

Pas vraiment. La seule façon serait écrire une fonction varargs et remplir manuellement les valeurs par défaut des arguments l'appelant ne passe pas.

Autres conseils

Wow, tout le monde est un pessimiste ici. La réponse est oui.

Il est pas anodin: à la fin, nous aurons la fonction de base, une structure de support, une fonction d'emballage, et une macro autour de la fonction enveloppe. Dans mon travail, j'ai un ensemble de macros pour automatiser tout cela; une fois que vous comprenez le flux, il sera facile pour vous de faire la même chose.

Je l'ai écrit ailleurs jusqu'à, voici donc un lien externe détaillé pour compléter le résumé ici: http: //modelingwithdata.org/arch/00000022.htm

Nous aimerions tourner

double f(int i, double x)

dans une fonction qui prend les valeurs par défaut (i = 8, x = 3,14). Définir un struct compagnon:

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

Renommer votre fonction f_base et définir une fonction wrapper qui définit les valeurs par défaut et les appels 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);
}

Maintenant, ajoutez une macro, en utilisant des macros variadique de C. Ainsi, les utilisateurs ne doivent pas savoir qu'ils sont peuplant fait une struct f_args et pensent qu'ils font l'habituel:

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

OK, maintenant toutes les conditions suivantes travailleraient:

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

Vérifiez les règles sur la façon dont initializers composé mis en valeurs par défaut pour les règles exactes.

Une chose qui ne fonctionne pas: f(0), parce que nous ne pouvons pas distinguer entre une valeur manquante et zéro. Dans mon expérience, c'est quelque chose à surveiller, mais il peut être pris en charge comme le besoin se fait sentir --- la moitié du temps votre défaut est vraiment nul.

Je suis passé par la peine d'écrire ce parce que je pense que les arguments nommés et par défaut  font vraiment le codage en C plus facile et plus amusant. Et C est génial pour être si simple et encore avoir assez là pour rendre tout cela possible.

Oui. :-) Mais pas d'une manière que vous attendez.

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

Malheureusement, C ne vous permet pas de surcharger les méthodes afin que vous finiriez avec deux fonctions différentes. Pourtant, en appelant f2, vous seriez en fait appellerez f1 avec une valeur par défaut. Ceci est une solution « Ne vous répétez », ce qui vous aide à éviter de copier / coller le code existant.

Nous pouvons créer des fonctions qui utilisent des paramètres nommés (uniquement) pour les valeurs par défaut. Ceci est la suite de la réponse de 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);                                                             
}    

La norme définit C99 dont les noms plus tard lors de l'initialisation remplacent les éléments précédents. Nous pouvons aussi avoir des paramètres de position standard ainsi, il suffit de changer la signature macro et la fonction en conséquence. Les paramètres de valeur par défaut ne peuvent être utilisés dans le style de paramètre nommé.

Sortie du programme:

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

OpenCV utilise quelque chose comme:

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

Si l'utilisateur ne sait pas ce qu'il doit écrire, cette astuce peut être utile:

Exemple d

Non.

Pas même la toute dernière norme C99 supporte.

Non, c'est une fonctionnalité de langage C ++.

Probablement la meilleure façon de le faire (qui peut ou peut ne pas être possible dans votre cas, en fonction de votre situation) est de passer à C ++ et l'utiliser comme « un meilleur C ». Vous pouvez utiliser C ++ sans utiliser des classes, des modèles, la surcharge d'opérateur ou d'autres fonctions avancées.

Cela vous donnera une variante de C avec des paramètres de surcharge de fonction et par défaut (et quelles que soient les autres fonctions que vous avez choisi d'utiliser). Il vous suffit d'être un peu disciplinés si vous êtes vraiment sérieux au sujet en utilisant uniquement un sous-ensemble restreint de C ++.

Beaucoup de gens diront que c'est une très mauvaise idée d'utiliser C ++ de cette façon, et ils pourraient avoir un point. Mais est c'est juste une opinion; Je pense qu'il est valable d'utiliser les fonctionnalités de C ++ que vous êtes à l'aise avec sans avoir à acheter dans l'ensemble. Je pense qu'une partie importante de la raison de la sucess de C ++ est qu'il a obtenu utilisé par un très grand nombre de programmeurs dans c'est les premiers jours exactement de cette manière.

Réponse courte:. Non

Un peu plus réponse: Il y a un vieux, vieux solution de contournement où vous passez une chaîne que vous parse pour les arguments optionnels:

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

où opt peut inclure deux ou quelque chose « name = valeur », et que vous appelleriez comme

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

Il est évident que ce n'est parfois utile. En général, lorsque vous voulez une interface unique pour une famille de fonctionnalité.


Vous trouverez toujours cette approche dans les codes de la physique des particules qui sont écrits par des programmes professionnels en c ++ (comme par exemple ROOT ). Son principal avantage est qu'il peut être prolongée presque indéfiniment, tout en maintenant la compatibilité arrière.

Une autre option utilise 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);

Non.

Non, mais vous pouvez envisager d'utiliser un set de fonctions (ou macros) à l'aide args approximer par défaut:

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

Oui, avec des fonctionnalités de C99 vous pouvez le faire. Cela fonctionne sans définir de nouvelles structures de données ou si et sans la fonction d'avoir à décider lors de l'exécution comment il a été appelé, et sans aucune charge de calcul.

Pour une explication détaillée, voir mon post à

http://gustedt.wordpress.com/ 2010/06/03 / default-arguments-pour-C99 /

Jens

En général non, mais gcc Vous pouvez faire le dernier paramètre de FoncA () en option avec une macro.

FoncB () i utilise une valeur spéciale (-1) pour signaler que i besoin de la valeur par défaut du paramètre « 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) );
}

J'amélioré répondre pour:

  1. fonctions en ligne ne sont pas utilisées
  2. par défaut sont calculés au cours de prétraiter
  3. macros modulaires reuseable
  4. possible de définir l'erreur du compilateur qui correspond de manière significative le cas des arguments insuffisants pour les valeurs par défaut autorisées
  5. les valeurs par défaut ne sont pas nécessaires pour former la queue de la liste des paramètres si les types d'arguments restent sans ambiguïté
  6. interopts avec C11 _Generic
  7. modifier le nom de la fonction par le nombre d'arguments!

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

scénario d'utilisation simplifié:

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

Et avec _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__))

Et avec la sélection du nom de la fonction variadique, qui ne peut être combiné avec _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__)

Une autre astuce en utilisant des macros:

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

Si un seul argument est passé, b reçoit la valeur par défaut (dans ce cas 15)

Oui, vous pouvez faire quelque chose de similaire, ici, vous devez connaître les différentes listes d'arguments que vous pouvez obtenir, mais vous avez la même fonction pour les gérer tous.

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

OUI

Par macros

3 Paramètres:

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

Si vous voulez 4 arguments, puis un my_func3 supplémentaire doit être ajouté. Remarquez les changements dans VAR_FUNC, my_func2 et my_func

4 Paramètres:

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

La seule exception que float variables ne peuvent pas être des valeurs par défaut ( à moins que si elle est le dernier argument que dans les 3 paramètres cas ), parce qu'ils ont besoin période (. « ), qui ne sont pas acceptés dans les arguments macro. Mais peut comprendre un travail autour comme on le voit dans macro my_func2 ( de 4 paramètres cas )

Programme

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

Sortie:

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  

Pourquoi ne pouvons-nous faire.

Donner l'argument optionnel une valeur par défaut. De cette façon, l'appelant de la fonction ne doit pas nécessairement passer la valeur de l'argument. L'argument prend la valeur par défaut. Et facilement cet argument devient facultatif pour le client.

Pour par exemple.

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

Voici b est un argument optionnel.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top