Pergunta

Existe uma maneira de especificar argumentos padrão para uma função em C?

Foi útil?

Solução

Não é verdade. A única maneira seria escrever uma varargs função e preencher manualmente valores padrão para argumentos que o chamador não passa.

Outras dicas

Wow, todo mundo é tão pessimista por aqui. A resposta é sim.

Não é trivial: no final, teremos a função de núcleo, uma estrutura de suporte, uma função de invólucro, e uma macro em torno da função wrapper. No meu trabalho eu tenho um conjunto de macros para automatizar tudo isso; uma vez -lo a compreender o fluxo que vai ser fácil para você fazer o mesmo.

Eu escrevi isso em outro lugar, então aqui está um link externo detalhado para complementar o resumo aqui: http: //modelingwithdata.org/arch/00000022.htm

Gostaríamos de transformar

double f(int i, double x)

numa função que recebe o padrão (I = 8, x = 3,14). Definir um struct companheiro:

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

Renomear sua função f_base, e definir uma função de invólucro que define padrões e chamadas a 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);
}

Agora, adicione uma macro, utilizando macros variádicos de C. Desta forma, os usuários não precisam saber que estão realmente preencher um struct f_args e acho que eles estão fazendo o de sempre:

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

OK, agora todos os seguintes iria funcionar:

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

Verifique as regras de padrões como composto initializers definidos para as regras exatas.

Uma coisa que não vai funcionar: f(0), porque não podemos distinguir entre um valor em falta e zero. Na minha experiência, isso é algo que atente para, mas pode ser tomado cuidado de, surge a necessidade --- metade do tempo o seu padrão é realmente zero.

Eu atravessei o trabalho de escrever isso porque eu acho que argumentos nomeados e padrões realmente fazer a codificação em C mais fácil e ainda mais divertido. E C é incrível por ser tão simples e ainda ter o suficiente lá para fazer tudo isso possível.

Sim. :-) Mas não de uma forma que seria de esperar.

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

Infelizmente, C não permite sobrecarregar métodos para que você iria acabar com duas funções diferentes. Ainda assim, chamando F2, você realmente ser chamado f1 com um valor padrão. Esta é uma solução "Do not Repeat Yourself", que ajuda a evitar copiar / colar o código existente.

Podemos criar funções que usam parâmetros nomeados (apenas) para valores padrão. Esta é uma continuação do bk da. Resposta.

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

As C99 norma define que os nomes posteriores nos itens anteriores inicialização substituição. Nós também pode ter alguns parâmetros posicionais padrão, bem, basta alterar a assinatura macro e função em conformidade. Os parâmetros de valor padrão só pode ser usada em estilo de parâmetro nomeado.

Saída do programa:

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

OpenCV usa algo como:

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

Se o usuário não sabe o que ele deveria escrever, este truque pode ser útil:

exemplo de uso

Não.

Nem mesmo as mais recentes C99 suporta o padrão isso.

Não, isso é uma característica linguagem C ++.

Provavelmente a melhor maneira de fazer isso (que pode ou não ser possível no seu caso, dependendo da sua situação) é passar para C ++ e usá-lo como 'um melhor C'. Você pode usar C ++, sem o uso de classes, modelos, sobrecarga de operadores ou outros recursos avançados.

Isto lhe dará uma variante do C com sobrecarga de funções e padrão parâmetros (e quaisquer outros recursos que você escolheu usar). Você apenas tem que ser um pouco disciplinado, se você for realmente sério sobre o uso de apenas um subconjunto restrito de C ++.

Um monte de gente vai dizer que é uma péssima idéia de usar C ++, desta forma, e eles podem ter um ponto. Mas é isso é apenas uma opinião; Eu acho que é válida para usar os recursos de C ++ que você está confortável com sem ter que comprar para a coisa toda. Eu acho que uma parte significativa da razão para o sucesso do C ++ é que ele foi usado por uma enorme quantidade de programadores em sua primeiros dias exatamente desta forma.

Resposta curta:. Não

resposta ligeiramente mais longo: Há um velho, velho solução alternativa onde você passar uma string que você parse para argumentos opcionais:

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

onde opt podem incluir "name = valor" par ou algo assim, e que você chamaria como

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

Obviamente este é apenas ocasionalmente útil. Geralmente quando você quer uma única interface para uma família de funcionalidade.


Você ainda encontrar esta abordagem em códigos de física de partículas que são escritos por profissionais programas em C ++ (como por exemplo ROOT ). É do principal vantagem é que ele pode ser prorrogado indefinidamente, mantendo a compatibilidade de volta.

No entanto, outro usos opção 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);

No.

Não, mas você pode considerar o uso de um set de funções (ou macros) para aproximar usando argumentos padrão:

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

Sim, com recursos do C99 você pode fazer isso. Isso funciona sem definir novas estruturas de dados ou assim e sem a função de ter que decidir em tempo de execução como era chamado, e sem qualquer sobrecarga computacional.

Para uma explicação detalhada veja meu post em

http://gustedt.wordpress.com/ 2010/06/03 / default-argumentos-para-c99 /

Jens

Geralmente não, mas no gcc Você pode fazer o último parâmetro de funcA () opcional com uma macro.

Em funcB () i usar um valor especial (-1) para sinalizar que eu preciso o valor padrão para o parâmetro '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) );
}

I melhorou de Jens Gustedt resposta de modo que:

  1. funções inline não são empregados
  2. defaults são computados durante a pré-processamento
  3. macros reutilizáveis ??modulares
  4. possível conjunto de erro compilador que significativamente corresponde ao caso de argumentos insuficientes para os padrões permitidos
  5. os padrões não são necessários para formar a cauda da lista de parâmetros se os tipos de argumentos permanecerá inequívoca
  6. interopts com C11 _Generic
  7. variar o nome da função pelo número de argumentos!

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

cenário de uso simplificado:

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

E com _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__))

E com a seleção nome da função aridade variável, que não pode ser combinado com _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__)

Outro truque usando 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;
}

Se apenas um argumento é passado, b recebe o valor padrão (neste caso 15)

Sim, você pode fazer algo semelhante, aqui você tem que conhecer as diferentes listas de argumentos que você pode começar, mas você tem a mesma função para lidar com todos eles.

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

YES

Através de macros

3 Parâmetros:

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

Se você quiser quarto argumento, em seguida, um my_func3 extra deve ser adicionado. Observe as mudanças na VAR_FUNC, my_func2 e my_func

4 Parâmetros:

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

Apenas exceção que flutuador variáveis ??não pode ser dado valores padrão (, a menos que seja o último argumento como no caso 3 parâmetros ), porque eles precisam de período ('. '), o que não é aceito dentro de argumentos de macro. Mas pode descobrir um trabalho em torno, como visto na my_func2 macro ( de 4 caso parâmetros )

Programa

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

Output:

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  

Por que não podemos fazer isso.

Dê o argumento opcional um valor padrão. Dessa forma, o chamador da função não precisa necessariamente passar o valor do argumento. O argumento leva o valor padrão. E facilmente esse argumento torna-se opcional para o cliente.

Por exemplo.

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

Aqui b é um argumento opcional.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top