313 votes

C arguments par défaut

Existe-t-il un moyen de spécifier des arguments par défaut à une fonction en C ?

10 votes

Je veux juste un C légèrement meilleur, pas C++. pensez à C+ . avec une variété de petites améliorations tirées de C++, mais pas le grand bazar. Et, s'il vous plaît, pas de link-loader différent. ça ne devrait être qu'une autre étape de type préprocesseur. standardisé. partout...

0 votes

Related question que je n'ai pas vu dans la barre latérale.

0 votes

Je vous dirais d'arrêter d'être un barbare et d'apprendre à utiliser C++(11, ...) bien - jk ! /Je ne peux pas m'en empêcher, désolé.

309voto

bk. Points 1766

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

Ce n'est pas trivial : à la fin, nous aurons la fonction principale, une structure de support, une fonction d'encapsulation, et une macro autour de la fonction wrapper. Dans mon travail, j'ai un ensemble de macros pour automatiser tout cela. Une fois que vous aurez compris le processus, il vous sera facile de faire de même.

J'ai écrit ce sujet ailleurs, 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)

en une fonction qui prend les valeurs par défaut (i=8, x=3.14). Définissez une structure d'accompagnement :

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

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

Ajoutez maintenant une macro, en utilisant les macros variadiques du C. De cette façon, les utilisateurs n'ont pas besoin de savoir qu'ils qu'ils remplissent réellement un f_args structure et pensent qu'ils font comme d'habitude :

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

OK, maintenant tout ce qui suit fonctionnerait :

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 les initialisateurs composés définissent les valeurs par défaut pour connaître les règles exactes.

Une chose qui ne marchera pas : f(0) car nous ne pouvons pas faire la différence entre une valeur manquante et zéro. D'après mon expérience, c'est un problème auquel il faut faire attention, mais qui peut être résolu par l'utilisation de la commande le besoin s'en fait sentir - la moitié du temps, votre valeur par défaut est vraiment zéro.

Je me suis donné la peine d'écrire ceci parce que je pense que les arguments nommés et les valeurs par défaut rendent vraiment le codage en C plus facile et plus amusant. Et Le C est génial parce qu'il est si simple et qu'il a suffisamment d'éléments pour rendre tout cela possible.

19 votes

+1 créatif ! Elle a ses limites, mais elle apporte aussi des paramètres nommés à la table. Notez que, {} (initialisateur vide) est une erreur C99.

33 votes

Cependant, voici quelque chose de génial pour vous : La norme permet de spécifier des membres nommés plusieurs fois, les derniers étant prioritaires. Ainsi, pour les paramètres nommés uniquement, vous pouvez résoudre le problème des valeurs par défaut et permettre un appel vide. #define vrange(...) CALL(range,(param){.from=1, .to=100, .step=1, __VA_ARGS__})

3 votes

J'espère que les erreurs du compilateur sont lisibles, mais c'est une excellente technique ! Cela ressemble presque aux kwargs de Python.

171voto

Wim ten Brink Points 12422

Oui. :-) Mais pas de la manière à laquelle on pourrait s'attendre.

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, le C ne permet pas de surcharger les méthodes et vous vous retrouveriez avec deux fonctions différentes. Pourtant, en appelant f2, vous appelleriez en fait f1 avec une valeur par défaut. Il s'agit d'une solution "Don't Repeat Yourself", qui vous permet d'éviter de copier/coller du code existant.

28 votes

Pour information, je préfère utiliser le nombre à la fin de la fonction pour indiquer le nombre d'args qu'elle prend. C'est plus facile que d'utiliser n'importe quel nombre arbitraire :)

5 votes

C'est de loin la meilleure réponse, car elle montre un moyen simple d'atteindre le même objectif. J'ai une fonction qui fait partie d'une API fixe que je ne veux pas modifier, mais j'ai besoin qu'elle prenne un nouveau paramètre. Bien sûr, c'est tellement évident que je ne l'ai pas vu (je suis resté bloqué en pensant au paramètre par défaut !).

9 votes

F2 pourrait également être une macro de préprocesseur

165voto

Eli Courtwright Points 53071

Pas vraiment. Le seul moyen serait de écrire une fonction varargs et remplir manuellement les valeurs par défaut pour les arguments que l'appelant ne passe pas.

26 votes

Je déteste le manque de vérification lors de l'utilisation de varargues.

19 votes

Je ne le recommande pas, mais je voulais simplement vous faire comprendre que c'est possible.

4 votes

Cependant, comment voulez-vous vérifier si l'appelant passe l'argument ou non ? Je pense que pour que cela fonctionne, ne faut-il pas que l'appelant vous dise qu'il ne l'a pas passé ? Je pense que cela rend l'approche globale un peu moins utilisable - l'appelant pourrait aussi bien appeler une fonction avec un autre nom.

44voto

u0b34a0f6ae Points 14874

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 C99 définit que les noms ultérieurs dans l'initialisation remplacent les éléments précédents. Nous pouvons également avoir des paramètres positionnels standard, il suffit de modifier la signature de la macro et de la fonction en conséquence. Les paramètres de valeur par défaut ne peuvent être utilisés que dans le style paramètre nommé.

Sortie du programme :

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

3 votes

Cela semble être une mise en œuvre plus facile et plus directe que la solution Wim ten Brink ou BK. Y a-t-il des inconvénients à cette mise en œuvre que les autres ne possèdent pas ?

18voto

unwind Points 181987

Non.

Même la toute dernière norme C99 ne le permet pas.

0 votes

Un simple non aurait été encore mieux ;)

22 votes

@kevindtimm : Ce n'est pas possible, le SO impose une longueur minimale aux réponses. J'ai essayé. :)

3 votes

Veuillez vous référer à ma réponse :)

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X