Аргументы C по умолчанию

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


person Nathaniel Flath    schedule 24.09.2009    source источник
comment
просто хочу немного лучше C, а не C ++. думаю C +. с множеством небольших улучшений, поднятых с C ++, но не с большим беспорядком. И, пожалуйста, никакого другого загрузчика ссылок. должен быть просто еще одним шагом, подобным препроцессору. стандартизированный. где угодно...   -  person ivo Welch    schedule 15.02.2015
comment
Связанный вопрос, который я не отображается в списке на боковой панели.   -  person Shelby Moore III    schedule 18.11.2015
comment
Я бы сказал, перестань быть варваром и научись использовать C ++ (11, ...) хорошо - jk! / меня тушит пламя ... но ... тебе это понравится ... хахаха, я ничего не могу с собой поделать, извини.   -  person moodboom    schedule 17.03.2017
comment
Возможный дубликат значений по умолчанию для аргументов в Функции C и перегрузка функций в C   -  person Jonathan Mee    schedule 06.04.2018


Ответы (19)


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

person Eli Courtwright    schedule 24.09.2009
comment
Ненавижу отсутствие проверки при использовании varargs. - person dmckee --- ex-moderator kitten; 24.09.2009
comment
Также вам следует; Я на самом деле не рекомендую этого; Я просто хотел передать, что это возможно. - person Eli Courtwright; 24.09.2009
comment
Однако как вы хотите проверить, передает ли вызывающий аргумент или нет? Я думаю, чтобы это сработало, разве у вас нет звонящего сказать вам, что он не прошел? Я думаю, что это делает весь подход несколько менее удобным - вызывающий может также вызвать функцию с другим именем. - person Johannes Schaub - litb; 24.09.2009
comment
Это определенно проблема, поскольку нет надежного кросс-платформенного способа проверить, было ли что-то передано вообще или сколько аргументов вам прислали. Таким образом, varargs может хорошо работать, если у вас есть функция, в которой имеет смысл вызывать ее как f(5, 6, 7, NULL) с каким-то завершающим аргументом. Иногда я пишу функции, для которых это может иметь смысл, хотя и не часто, поэтому я начал свой ответ со слов: «Не совсем». - person Eli Courtwright; 24.09.2009
comment
Системный вызов open(2) использует это для необязательного аргумента, который может присутствовать в зависимости от требуемых аргументов, а printf(3) считывает строку формата, которая указывает, сколько аргументов будет. Оба используют varargs довольно безопасно и эффективно, и, хотя вы, конечно, можете облажаться с ними, printf() особенно кажется довольно популярным. - person Chris Lutz; 24.09.2009
comment
Printf на самом деле является плохим примером, потому что компиляторы C в конечном итоге проверяют количество аргументов, которые вы передаете printf, и выдают вам ошибки, если вы передаете неправильное число. Сама функция varargs не может этого сделать, поскольку нет функции или макроса, которые сообщали бы вам, сколько параметров было фактически передано. - person Eli Courtwright; 25.09.2009
comment
@Eli: Не все компиляторы C являются gcc. Существует некоторая продвинутая магия компилятора, чтобы он предупреждал, когда ваши аргументы printf () не соответствуют вашей строке формата. И я не думаю, что можно получить аналогичные предупреждения для ваших собственных вариативных функций (если они не используют тот же стиль строки формата). - person tomlogic; 14.06.2010
comment
@BenjaminGruenbaum: Хороший улов, я обновил ссылку, чтобы указать на Википедию. - person Eli Courtwright; 03.08.2013
comment
у кого-нибудь есть реализация этого? даже если это ПЛОХАЯ идея. - person Youssef Bouhjira; 12.03.2014
comment
Как это возможно, что есть две open() функции с одинаковыми именами? int open(const char *pathname, int flags) и int open(const char *pathname, int flags, mode_t mode); (см. man 2 open) - person CLOVIS; 09.01.2021

Вау, здесь все такие пессимисты. Ответ положительный.

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

Я написал об этом в другом месте, поэтому вот подробная внешняя ссылка, дополняющая резюме здесь: 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 великолепен тем, что он настолько прост и все еще имеет достаточно, чтобы все это стало возможным.

person bk.    schedule 28.05.2010
comment
+1 креатив! Он имеет свои ограничения, но также вносит в таблицу именованные параметры. Обратите внимание, что {} (пустой инициализатор) - это ошибка C99. - person u0b34a0f6ae; 29.10.2011
comment
Однако здесь есть кое-что отличное для вас: стандарт позволяет указывать именованные элементы несколько раз, позднее переопределив. Таким образом, только для именованных параметров вы можете решить проблему по умолчанию и разрешить пустой вызов. #define vrange(...) CALL(range,(param){.from=1, .to=100, .step=1, __VA_ARGS__}) - person u0b34a0f6ae; 29.10.2011
comment
Я надеюсь, что ошибки компилятора можно прочитать, но это отличный метод! Почти похоже на питона kwargs. - person totowtwo; 21.06.2012
comment
Ответ с двумя разными именами функций - лучший и более простой способ достижения той же цели! - person RunHolt; 12.10.2013
comment
Отличное решение. Но он дает missing initializer for field предупреждение, следует просто игнорировать это - person Youssef Bouhjira; 12.03.2014
comment
@RunHolt Конечно, проще, но объективно не лучше; Именованные параметры имеют такие преимущества, как легкость чтения вызовов (за счет читабельности исходного кода). Один лучше для разработчиков исходного кода, другой - для пользователей функции. Немного поспешно просто выбросить этот лучше! - person Alice; 25.04.2014
comment
Умный. К сожалению, не очень хорошо взаимодействует с Emscripten (asm.js и Node.js) при вызове сгенерированных функций из Javascript. Таким образом, я улучшил ответ Йенса Густедта, поскольку он не трансформирует функцию, созданную компилятором. - person Shelby Moore III; 18.11.2015
comment
почему по умолчанию не указанные аргументы 0 не случайное число? - person DawidPi; 04.04.2016
comment
@DawidPi: C11 6.7.9 (19), при инициализации агрегатов: все подобъекты, которые не инициализированы явно, должны быть инициализированы неявно так же, как объекты со статической продолжительностью хранения И, как вы знаете, элементы статической длительности инициализируются нулевым значением | NULL | \ 0. [Это тоже было в c99.] - person bk.; 04.04.2016
comment
Честно говоря, отличный способ решить эту недостающую функциональность. Действительно здорово в качестве интеллектуального упражнения. Но я никак не могу усложнить свой код - в частности, имея дело с документированием функций и снижением читабельности. И все мои младшие разработчики кричат ​​- вполне разумно - блин ?! - person HughHughTeotl; 03.04.2018
comment
Вы не можете вызвать обычную функцию f (int a) с синтаксисом инициализатора f (.a = 17). Это делает его нестандартным синтаксисом и труднее читать. Ваше решение действительно работает, но gcc -Wextra (который устанавливает -Woverride-init) жалуется, если вы используете VA_ARGS внутри {} для переопределения значений. - person Allan Wind; 08.09.2020
comment
В этом решении есть серьезная ошибка, которая не позволяет ему быть общим. Автор также предупреждает об этом, но только для первого аргумента. Он заключается в том, что у вас должно быть специальное значение (в данном случае 0) для каждого аргумента, которое можно интерпретировать как отсутствие переданного значения. Это вообще невозможно, так как это значение может быть преднамеренным аргументом вызывающей стороны. В этом случае значение по умолчанию заменит значение, определенное вызывающим. Впрочем, хорошая идея. - person EnzoR; 28.02.2021

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

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 со значением по умолчанию. Это решение «Не повторяйся», которое помогает избежать копирования / вставки существующего кода.

person Wim ten Brink    schedule 24.09.2009
comment
FWIW, я бы предпочел использовать число в конце функции, чтобы указать количество требуемых аргументов. Делает это проще, чем просто использовать произвольное число. :) - person Macke; 31.10.2011
comment
Это, безусловно, лучший ответ, потому что он демонстрирует простой способ достижения той же цели. У меня есть функция, которая является частью фиксированного API, которую я не хочу изменять, но мне нужно, чтобы она приняла новый параметр. Конечно, это настолько ослепляюще очевидно, что я пропустил это (застрял, думая о параметре по умолчанию!) - person RunHolt; 12.10.2013
comment
f2 также может быть макросом препроцессора - person osvein; 21.03.2017

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

#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
person u0b34a0f6ae    schedule 29.10.2011
comment
Это кажется более простой и понятной реализацией, чем решение Wim ten Brink или BK. Есть ли у этой реализации недостатки, которых нет у других? - person stephenmm; 16.07.2014

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

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

пример использования

person Community    schedule 18.12.2012
comment
Это был вопрос C, и решение не предоставляет значений по умолчанию для C. Компиляция вашего кода C как C ++ на самом деле не так уж и интересна, тогда вы также можете использовать значения по умолчанию, которые предоставляет C ++. Кроме того, код C не всегда является допустимым кодом C ++, поэтому он требует усилий по переносу. - person Allan Wind; 08.09.2020

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

person Alex B    schedule 24.09.2009
comment
Я думал, что это особенность языка Python ... - person jdk1.0; 22.05.2021

No.

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

person unwind    schedule 24.09.2009
comment
простое нет было бы даже лучше;) - person KevinDTimm; 24.09.2009
comment
@kevindtimm: Это невозможно, SO требует минимальной длины ответов. Я пытался. :) - person unwind; 24.09.2009
comment
Пожалуйста, обратитесь к моему ответу. :) - person chaos; 24.09.2009

Еще один вариант использует 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);
person Chris Lutz    schedule 24.09.2009

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

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

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

где opt может включать пару "name = value" или что-то в этом роде, и который вы бы назвали как

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

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


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

person dmckee --- ex-moderator kitten    schedule 24.09.2009
comment
Совместите это с varargs, и вы получите массу удовольствия! - person David Thornley; 24.09.2009
comment
Я бы использовал пользовательский struct, и вызывающий абонент сделал его, заполнил поля для различных параметров, а затем передал его по адресу или передал NULL для параметров по умолчанию. - person Chris Lutz; 24.09.2009
comment
Копирование шаблонов кода из ROOT - ужасная идея! - person innisfree; 09.07.2017

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

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

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

person Michael Burr    schedule 24.09.2009

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

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

person David Ranieri    schedule 02.10.2018

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

Подробное объяснение см. В моем сообщении по адресу

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

Йенс

person Jens Gustedt    schedule 14.06.2010
comment
См. Также мой ответ, заимствованный из вашего. - person Shelby Moore III; 18.11.2015
comment
Вставьте пример. - person user877329; 02.10.2018

Нет, но вы можете рассмотреть возможность использования набора функций (или макросов) для аппроксимации с использованием аргументов по умолчанию:

// 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
}
person David R Tribble    schedule 24.09.2009

Я улучшил ответ Йенса Густедта, чтобы:

  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__)
person Shelby Moore III    schedule 18.11.2015

Обычно нет, но в 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) );
}
person sambowry    schedule 24.09.2009

ДА

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

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

Если вам нужен 4-й аргумент, необходимо добавить дополнительный 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);
}

Единственное исключение, что переменные float не могут иметь значения по умолчанию (если это не последний аргумент, как в случае с тремя параметрами), потому что им нужна точка ('.'), что не принимается в аргументах макроса. Но можно найти обходной путь, как показано в макросе 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  
person Ramu    schedule 23.07.2018

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

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;
    }
}
person eaanon01    schedule 25.09.2009

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

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

Например,

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

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

person navgupta    schedule 22.08.2013
comment
Потрясающее понимание, проблема в том, что C не поддерживает необязательные аргументы или перегруженные функции, поэтому прямое решение не компилируется. - person Thomas; 28.03.2014

person    schedule
comment
какой обходной путь? Я вижу, что это 20202020 в шестнадцатеричном формате, но как его набрать? - person Lazer; 06.05.2010
comment
@Lazer, это были бы пробелы ASCII, не так ли? - person Gustavo6046; 17.09.2020