Вопрос

Есть ли способ указать аргументы по умолчанию для функции в C?

Это было полезно?

Решение

Не совсем.Единственным способом было бы написать функцию с переменным числом аргументов и вручную заполнить значения по умолчанию для аргументов, которые вызывающая сторона не передает.

Другие советы

Ух ты, здесь все такие пессимисты.Ответ - да.

Это не тривиально:в итоге у нас будет основная функция, вспомогательная структура, функция-оболочка и макрос вокруг функции-оболочки.В моей работе у меня есть набор макросов для автоматизации всего этого;как только вы поймете суть процесса, вам будет легко сделать то же самое.

Я писал об этом в другом месте, поэтому вот подробная внешняя ссылка, дополняющая краткое изложение здесь: http://modelingwithdata.org/arch/00000022.htm

Мы хотели бы обратиться

double f(int i, double x)

в функцию, которая принимает значения по умолчанию (i= 8, x= 3,14).Определите сопутствующую структуру:

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

Переименуйте свою функцию f_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);
}

Теперь добавьте макрос, используя переменные макросы языка C.Таким образом, пользователям не нужно знать, что они на самом деле заполняют f_args структурировать и думать, что они делают все как обычно:

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

Хорошо, теперь все нижеперечисленное будет работать:

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

Проверьте правила о том, как составные инициализаторы устанавливают значения по умолчанию для точных правил.

Одна вещь, которая не сработает: f(0), потому что мы не можем отличить пропущенное значение от нуля.По моему опыту, это то, что нужно остерегаться, но могут быть учтены как возникает вопрос---на половину нужно время по умолчанию на самом деле равна нулю.

Я не поленился написать это, потому что думаю, что именованные аргументы и значения по умолчанию действительно делают кодирование на C проще и даже веселее.И C великолепен тем, что он такой простой и в нем все еще достаточно, чтобы сделать все это возможным.

Да.:-) Но не так, как вы ожидали.

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

К сожалению, C не позволяет перегружать методы, поэтому в итоге вы получите две разные функции.Тем не менее, вызывая f2, вы фактически вызываете f1 со значением по умолчанию.Это решение «Не повторяйте себя», которое поможет вам избежать копирования/вставки существующего кода.

Мы можем создавать функции, которые используют именованные параметры (только) для значений по умолчанию.Это продолжение ответа Бк.

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

Стандарт C99 определяет, что последующие имена в инициализации переопределяют предыдущие элементы.Мы также можем иметь некоторые стандартные позиционные параметры, просто измените соответствующим образом макрос и сигнатуру функции.Параметры значений по умолчанию можно использовать только в стиле именованных параметров.

Выход программы:

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

OpenCV использует что-то вроде:

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

Если пользователь не знает, что ему написать, этот трюк может оказаться полезным:

usage example

Нет.

Этого не поддерживает даже самый последний стандарт C99.

Нет, это особенность языка C++.

Вероятно, лучший способ сделать это (который может быть возможен в вашем случае, а может и не быть, в зависимости от вашей ситуации) — перейти на C++ и использовать его как «лучший C».Вы можете использовать C++ без использования классов, шаблонов, перегрузки операторов или других дополнительных функций.

Это даст вам вариант C с перегрузкой функций и параметрами по умолчанию (и любыми другими функциями, которые вы решите использовать).Вам просто нужно быть немного дисциплинированным, если вы действительно серьезно относитесь к использованию только ограниченного подмножества C++.

Многие люди скажут, что использовать C++ таким образом — ужасная идея, и, возможно, они будут правы.Но это всего лишь мнение;Я думаю, что вполне допустимо использовать те функции C++, которые вам удобны, без необходимости покупать все это целиком.Я думаю, что значительная часть причины успеха C++ заключается в том, что очень много программистов на заре его существования использовали его именно таким образом.

Короткий ответ: Нет.

Чуть более длинный ответ: Есть старый, старый обходной путь, при котором вы передаете строку, которую вы анализировать для необязательных аргументов:

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

где opt может включать пару «имя=значение» или что-то в этом роде, и что вы бы назвали как

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

Очевидно, что это полезно лишь иногда.Обычно, когда вам нужен единый интерфейс для семейства функций.


Этот подход до сих пор встречается в кодах физики элементарных частиц, написанных профессиональными программами на C++ (например, КОРЕНЬ).Его главное преимущество заключается в том, что его можно расширять практически до бесконечности, сохраняя при этом обратную совместимость.

Еще один вариант использует structс:

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

Да, с помощью функций C99 вы можете это сделать.Это работает без определения новых структур данных или около того и без необходимости функции решать во время выполнения, как она была вызвана, и без каких-либо вычислительных затрат.

Подробное объяснение смотрите в моем посте по адресу

http://gustedt.wordpress.com/2010/06/03/default-arguments-for-c99/

Йенс

Обычно нет, но в gcc последний параметр funcA() можно сделать необязательным с помощью макроса.

В funcB() я использую специальное значение (-1), чтобы указать, что мне нужно значение по умолчанию для параметра «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) );
}

Я улучшил Йенса Густедта отвечать так что:

  1. встроенные функции не используются
  2. значения по умолчанию вычисляются во время предварительной обработки
  3. модульные многоразовые макросы
  4. можно установить ошибку компилятора, которая по смыслу соответствует случаю недостаточности аргументов для разрешенных значений по умолчанию.
  5. значения по умолчанию не требуются для формирования хвоста списка параметров, если типы аргументов останутся однозначными
  6. взаимодействует с C11 _Generic
  7. меняйте имя функции по количеству аргументов!

вариация.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

Упрощенный сценарий использования:

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

И с _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__))

И с выбором вариативного имени функции, который нельзя комбинировать с _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__)

Еще один трюк с использованием макросов:

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

Если передан только один аргумент, b получает значение по умолчанию (в данном случае 15)

Да, вы можете сделать что-то похожее, здесь вам нужно знать различные списки аргументов, которые вы можете получить, но у вас есть одна и та же функция для обработки всего этого.

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

ДА

Через макросы

3 параметра:

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

Если вам нужен четвертый аргумент, необходимо добавить дополнительный my_func3.Обратите внимание на изменения в VAR_FUNC, my_func2 и my_func.

4 параметра:

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

Единственное исключение, которое плавать переменным не могут быть присвоены значения по умолчанию (если только это не последний аргумент, как в случае с 3 параметрами), поскольку им нужна точка ('.'), которая не допускается в аргументах макроса.Но можно найти обходной путь, как показано в макросе my_func2 (случай с 4 параметрами)

Программа

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

Выход:

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  

Почему мы не можем этого сделать.

Присвойте необязательному аргументу значение по умолчанию.Таким образом, вызывающему функцию не обязательно передавать значение аргумента.Аргумент принимает значение по умолчанию.И этот аргумент легко становится необязательным для клиента.

Например,

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

Здесь b — необязательный аргумент.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top