Pregunta

¿Hay una manera de especificar los argumentos por defecto a una función en C?

¿Fue útil?

Solución

En realidad no. La única manera sería escribir una función varargs y rellenar manualmente los valores por defecto para los argumentos que la persona que llama no pasa.

Otros consejos

Vaya, todo el mundo es tan pesimista por aquí. La respuesta es sí.

No es trivial: el final, vamos a tener la función principal, una estructura de soporte, una función de contenedor, y una macro en torno a la función de contenedor. En mi trabajo tengo un conjunto de macros para automatizar todo esto; una vez a entender el flujo que va a ser fácil para que usted pueda hacer lo mismo.

He escrito esto en otro lugar, así que aquí tiene un enlace externo detallada para complementar el resumen aquí: http: //modelingwithdata.org/arch/00000022.htm

Nos gustaría convertir

double f(int i, double x)

en una función que toma valores predeterminados (i = 8, x = 3,14). Definir una estructura de compañía:

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

Cambiar el nombre de su f_base función y definir una función de contenedor que establece valores predeterminados y llamadas 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);
}

Ahora agregue una macro, el uso de macros de variadic C. De esta manera los usuarios no tienen que saber que están En realidad poblar una estructura f_args y pensar que están haciendo los de siempre:

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

Bien, ahora todo lo siguiente funcionaría:

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

Compruebe las reglas sobre cómo inicializadores compuesto establecer los valores predeterminados para las reglas exactas.

Una cosa que no funciona: f(0), porque no podemos distinguir entre un valor faltante y cero. En mi experiencia, esto es algo a tener en cuenta, pero puede ser atendido como surge la necesidad --- la mitad del tiempo predeterminado en realidad es igual a cero.

Me fui por la molestia de escribir esto porque creo que los argumentos y valores predeterminados nombradas  Realmente hacen que la codificación en C más fácil y más divertido. Y C es impresionante por ser tan simple y todavía tener suficiente allí para hacer todo esto posible.

Sí. :-) Pero no de una manera que se puede 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");
}

Por desgracia, C no le permite a la sobrecarga de métodos para que acabaría con dos funciones diferentes. Aún así, llamando a f2, tendría en realidad estar llamando a la F1 con un valor por defecto. Esta es una solución "Do not Repeat Yourself", que le ayuda a evitar copiar / pegar el código existente.

Podemos crear funciones que utilizan parámetros con nombre (sólo) para los valores por defecto. Esta es una continuación de la respuesta 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);                                                             
}    

El estándar C99 define que los nombres posteriores en la inicialización anulan artículos anteriores. También podemos tener algunos parámetros de posición estándar, así, sólo cambia la firma y la función macro en consecuencia. Los parámetros de valor por defecto sólo se pueden utilizar en el estilo de parámetro con nombre.

La salida del programa:

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

OpenCV utiliza 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));

Si el usuario no sabe lo que debe escribir, este truco puede ser útil:

ejemplo de uso

No.

Ni siquiera el último estándar C99 es compatible con esta.

No, eso es una característica del lenguaje C ++.

Probablemente la mejor manera de hacer esto (que puede o no puede ser posible en su caso en función de su situación) es mover a C ++ y utilizarlo como 'una mejor C'. Usted puede utilizar C ++ sin utilizar clases, plantillas, sobrecarga de operadores u otras características avanzadas.

Esto le dará una variante de C con la sobrecarga de funciones y parámetros por defecto (y cualesquiera otras funciones que eligió para su uso). Sólo tienes que ser un poco disciplinados si usted es realmente serio acerca del uso de sólo un subconjunto restringido de C ++.

Mucha gente va a decir que es una terrible idea de usar C ++ de esta manera, y que podría tener un punto. Pero es que es sólo una opinión; Creo que es válido utilizar características de C ++ que se sienta cómodo sin tener que comprar en todo el asunto. Creo que una parte significativa de la razón del éxito de C ++ es que se acostumbró por una gran cantidad de programadores en ella los primeros días exactamente de esta manera.

Respuesta corta:. No

Respuesta Ligeramente más largo: Hay un viejo, de edad Solución donde se pasa una cadena que de análisis de argumentos opcionales:

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

donde opt puede incluir "nombre = valor" par o algo, y lo que ustedes llamarían como

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

Es evidente que esto sólo es útil en ocasiones. En general, cuando se desea una única interfaz para una familia de funcionalidad.


Usted todavía encontrar este enfoque en los códigos de física de partículas que están escritos por profesionales programas en C ++ (como por ejemplo raíz ). Su principal ventaja es que se puede extender casi indefinidamente, manteniendo la espalda compatibilidad.

Sin embargo, otra opción utiliza 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.

No, pero es posible considerar el uso de un set de funciones (o macros) para aproximar usando argumentos por defecto:

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

Sí, con las características de C99 puede hacer esto. Esto funciona sin definir nuevas estructuras de datos más o menos y sin la función de tener que decidir en tiempo de ejecución cómo se llamaba, y sin ninguna sobrecarga computacional.

Para una explicación detallada ver mi post a

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

Jens

En general no, pero en gcc Es posible hacer que el último parámetro de funcA () opcional con una macro.

En funcB () i utilizar un valor especial (-1) para señalar que necesito el valor por defecto para el 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) );
}

responder de modo que:

  1. no se emplean funciones en línea
  2. Los valores predeterminados se calcula en la decodificación previa
  3. macros reutilizables modulares
  4. posible establecer error del compilador que coincide de manera significativa el caso de argumentos insuficientes para los valores predeterminados permitidos
  5. los valores predeterminados no son necesarios para formar la cola de la lista de parámetros, si los tipos de argumentos permanecerán sin ambigüedades
  6. interopts con C11 _Generic
  7. variar el nombre de la función por el 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

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

Y con _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__))

Y con la selección de una función variadic, que no se pueden combinar con _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__)

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

Si sólo se pasa un argumento, b recibe el valor por defecto (en este caso 15)

Si se puede hacer algo similar, aquí hay que conocer las diferentes listas de argumentos, usted puede obtener, pero que tienen la misma función para manejar a todos.

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

A travé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);
}

Si desea cuarto argumento, entonces un my_func3 adicional debe ser añadido. Observe los cambios en VAR_FUNC, my_func2 y mi_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);
}

La única excepción que flotador variables no se pueden dar valores por defecto ( a menos si es el último argumento como en el caso 3 parámetros ), ya que necesitan tiempo ('. '), que no es aceptada dentro de argumentos de la macro. Pero puede averiguar un trabajo alrededor como se ve en macro my_func2 ( de caso 4 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;
}

Salida:

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 qué no podemos hacer esto.

Dar el argumento opcional un valor predeterminado. De esta manera, la persona que llama de la función no necesariamente tienen que pasar el valor del argumento. El argumento toma el valor por defecto. Y fácilmente que el argumento se convierte en opcional para el cliente.

Para por ejemplo.

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

Aquí b es un argumento opcional.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top