Есть ли способ указать аргументы по умолчанию для функции в C?C аргументы по умолчанию
ответ
Не совсем. Единственный способ - это write a varargs function и вручную заполнить значения по умолчанию для аргументов, которые не передают вызывающий.
Я ненавижу отсутствие проверки при использовании varargs. – dmckee
Также вы должны; Я на самом деле не рекомендую это; Я просто хотел сказать, что это возможно. –
Однако, как вы хотите проверить, передает ли вызывающий объект аргумент или нет? Я думаю, для этого, чтобы работать, разве у вас нет звонящего, чтобы сказать вам, что он не прошел его? Я думаю, что это делает весь подход несколько менее полезным - вызывающий может также вызвать функцию с другим именем. –
Нет, это функция языка C++.
Короткий ответ: No.
Слегка развернутый ответ: Существует старый, старый обходной путь, где вы передаете строку, вы синтаксического анализа для дополнительных аргументов:
int f(int arg1, double arg2, char* name, char *opt);
, где opt может включать пару «имя = значение» или что-то еще, и которое вы бы назвали
n = f(2,3.0,"foo","plot=yes save=no");
Очевидно, что это только полезно. Как правило, если вам нужен один интерфейс для семейства функций.
Вы до сих пор находят этот подход в физике элементарных частиц кодов, написанных профессиональными программами в C++ (как, например, ROOT). Главное преимущество заключается в том, что его можно продлить почти на неопределенный срок, сохраняя обратную совместимость.
Объедините это с varargs, и у вас есть все виды развлечений! –
Я бы использовал пользовательскую 'struct' и позвонил, чтобы заполнить поля для разных параметров, а затем передать их по адресу или передать' NULL' для параметров по умолчанию. –
Кто-то читал слишком много кода FORTRAN! –
No.
Не даже самый последний стандарт C99 поддерживает это.
простой нет было бы еще лучше;) – KevinDTimm
@kevindtimm: Это невозможно, SO накладывает минимальную длину на ответы. Я пытался. :) – unwind
Пожалуйста, обратитесь к моему ответу. :) – chaos
Да. :-) Но не так, как вы ожидали.
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 со значением по умолчанию. Это решение «Не повторяй себя», которое поможет избежать копирования и вставки существующего кода.
FWIW, я бы предпочел использовать номер в конце функции, чтобы указать количество аргументов, которое требуется. Делает это проще, чем просто использовать любое произвольное число. :) – Macke
Это, безусловно, лучший ответ, потому что он демонстрирует простой способ достижения той же цели. У меня есть функция, которая является частью фиксированного API, который я не хочу изменять, но мне нужно, чтобы он принял новый параметр. Конечно, это настолько ослепительно очевидно, что я пропустил его (застрял в мысли о параметре по умолчанию!) – RunHolt
f2 также может быть макросом препроцессора – Spookbuster
Нет, но вы могли бы рассмотреть вопрос об использовании наборе функций (или макросов) к приближенным, используя по умолчанию арга:
// 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
}
Что я сказал .... –
как правило, нет, но в НКУ Вы можете сделать последний параметр 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));
}
Вероятно, лучший способ сделать это (который может или не может быть возможно в вашем случае в зависимости от ситуации), чтобы перейти к C++ и использовать его в качестве «лучшего C». Вы можете использовать C++ без использования классов, шаблонов, перегрузки оператора или других дополнительных функций.
Это даст вам вариант C с перегрузкой функции и параметрами по умолчанию (и любыми другими функциями, которые вы выбрали для использования).Вам просто нужно быть немного дисциплинированным, если вы действительно серьезно относитесь к использованию только ограниченного подмножества C++.
Многие люди скажут, что это ужасная идея использовать C++ таким образом, и у них может быть точка. Но это всего лишь мнение; Я думаю, что использовать возможности C++, которые вам удобны, без необходимости покупать все это. Я думаю, что значительная часть причины успеха C++ заключается в том, что именно таким образом он привык к большому количеству программистов в первые дни.
Еще один вариант использует struct
S:
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);
Любые причины для downvote? Это второй анонимный нисходящий поток за два дня. –
Да, вы можете сделать Somthing simulair, здесь вы должны знать различные списки аргументов вы можете получить, но у вас есть та же функция для обработки, то все.
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;
}
}
Ничего себе, здесь все такие пессимисты. Ответ - да.
Это не тривиально: к концу мы будем иметь основную функцию, поддерживающую структуру, функцию обертки и макрос вокруг функции обертки. В моей работе у меня есть набор макросов для автоматизации всего этого; один раз вы понимаете поток, вам будет легко сделать то же самое.
Я написал это в другом месте, так что здесь подробно внешняя ссылка дополнить резюме здесь: 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);
}
Теперь добавить макрос, используя VARIADIC макросы языка 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 потрясающе для того, чтобы быть таким простым и до сих пор достаточно, чтобы сделать все возможное.
+1 креатив! Он имеет свои ограничения, но также приносит именованные параметры в таблицу. Обратите внимание, что '{}' (пустой инициализатор) является ошибкой C99. – u0b34a0f6ae
Однако, вот что для вас отличное: стандарт позволяет указывать именованные члены несколько раз, а затем переопределять. Таким образом, для именованных параметров вы можете решить проблему по умолчанию и разрешить пустой вызов. '#define vrange (...) CALL (диапазон, (param) {. from = 1, .to = 100, .step = 1, __VA_ARGS __})' – u0b34a0f6ae
Я надеюсь, что ошибки компилятора читабельны, но это отличный техника! Почти похоже на python kwargs. – totowtwo
Да, с функциями C99 вы можете это сделать.Это работает без определения новых структур данных или так и без функции, которая должна решить во время выполнения, как она была вызвана, и без каких-либо вычислительных затрат.
Для детального объяснения см мой пост в
http://gustedt.wordpress.com/2010/06/03/default-arguments-for-c99/
Jens
Упомянуте также мой [ответ] (http://stackoverflow.com/questions/1472138/c-default-arguments#33786937), который получен из вашего. –
Извините, моя связь сегодня слишком плохая. Надеюсь, завтра будет лучше. –
Мы можем создать функции, которые используют именованные параметры (только) для значений по умолчанию. Это продолжение ответа 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);
}
Стандарт C99 определяет, что более поздние имена в инициализации переопределяют предыдущие элементы. У нас также могут быть некоторые стандартные позиционные параметры, просто измените подпись макроса и функции. Параметры значения по умолчанию могут использоваться только в стиле именованных параметров.
выход программы:
1 2 3 4 5 6 7 8 9 10
2 3 4
1 3 5 7 9
Это кажется более простой и более прямой реализацией, чем решение Wim ten Brink или BK. Есть ли недостатки в этой реализации, которые другие не обладают? – stephenmm
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));
Если пользователь не знает, что он должен написать, этот трюк может быть полезным:
Это на самом деле довольно приятная интеграция IDE для C! Btw, это «default», а не «defualt». – Thomas
Почему мы не можем этого сделать.
Дайте необязательный аргумент по умолчанию. Таким образом, вызывающая функция не обязательно должна передавать значение аргумента. Аргумент принимает значение по умолчанию. И легко этот аргумент становится необязательным для клиента.
См., Например,
void foo (int a, int b = 0);
Здесь b - необязательный аргумент.
Потрясающая проницательность, проблема в том, что C не поддерживает необязательные аргументы или перегруженные функции, поэтому прямое решение не компилируется. – Thomas
я улучшил Jens Gustedt-х answer так что:
- встроенные функции не используются
- по умолчанию вычисляются во время предварительной обработки
- модульные многоразовых макросов
- можно установить ошибку компилятора, который по значению совпадает с случай недостаточных аргументов для разрешенных значений по умолчанию
- значения по умолчанию не требуются для формирования хвоста списка параметров, если t типы аргументов будут оставаться однозначными
- interopts with C11 _Generic
- изменить имя функции по количеству аргументов!
variadic.ч:
#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__))
И с VARIADIC выбора имени функции, которая не может быть объединено с _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__)
- 1. шаблона аргументы по умолчанию
- 2. По умолчанию аргументы контейнера
- 3. По умолчанию аргументы командной строки в C
- 4. C++ задает аргументы по умолчанию с #define
- 5. По умолчанию аргументы в конструкторе - C++
- 6. Функция C, которая принимает аргументы по умолчанию
- 7. Как изменить аргументы по умолчанию в C++
- 8. по умолчанию Аргументы функции
- 9. По умолчанию аргументы строки
- 10. JVM по умолчанию Аргументы
- 11. Co-зависимые аргументы по умолчанию
- 12. fortran 77 аргументы по умолчанию
- 13. аргументы по умолчанию в Python
- 14. Python аргументы и аргументы по умолчанию
- 15. Scala аргументы опциональный по умолчанию
- 16. PHP - аргументы функции по умолчанию
- 17. аргументы по умолчанию в конструкторе
- 18. По умолчанию Аргументы в Matlab
- 19. По умолчанию INT основные аргументы в C/C++
- 20. C++, конструктор по умолчанию
- 21. аргументы шаблона по умолчанию компилятор ошибок
- 22. Argparse Позиционные аргументы или по умолчанию
- 23. C++ - Дополнительные аргументы по ссылке
- 24. Python: переменные, наследование и аргументы по умолчанию
- 25. Почему аргументы по умолчанию являются конечными?
- 26. R setset set аргументы по умолчанию
- 27. аргументы аргументов оболочки идут по умолчанию
- 28. По умолчанию Аргументы, определяющие статический член
- 29. Перегрузка функции, которая имеет аргументы по умолчанию
- 30. Основные аргументы меняют местоположение по умолчанию
@pmg - И пока мы на нем, мы может исправить все недостатки C, которые нам не нравятся. И тогда мы можем добавить несколько функций здесь и там, а затем почему бы нам не добавить перегрузку функций, а затем мы сделали хакерский язык, который никто не будет использовать, и это смутит почти всех, кто должен поддерживать ваше приложение. Если вы хотите сделать язык, во что бы то ни стало, сделайте язык, но не создавайте новый язык только для определенной задачи. Это просто ужасно. –
@ Крис - ты не говоришь о Бьярне С, ты? :) –
просто хочу немного лучше C, а не C++. думаю, C +. с множеством небольших улучшений, снятых с C++, но не большого беспорядка. И, пожалуйста, никакого разного загрузчика ссылок. должен быть просто еще одним шагом, подобным препроцессору. стандартизированы. везде ... –