Почему пустота f (...) не разрешена в C? - PullRequest
6 голосов
/ 04 июля 2011

Почему C не позволяет использовать функцию с списком аргументов переменной длины, например:

void f(...)
{
    // do something...
}

Ответы [ 6 ]

10 голосов
/ 04 июля 2011

Я думаю, что мотивация для требования, чтобы функции varargs имели именованный параметр, была для однородности va_start.Для простоты реализации va_start принимает имя последнего именованного параметра.При типичном соглашении о вызовах varargs и в зависимости от того, как сохранены аргументы направления, va_arg найдет первый vararg по адресу (&parameter_name) + 1 или (first_vararg_type*)(&parameter_name) - 1, плюс или минус некоторое заполнение для обеспечения выравнивания.

Iне думаю, что есть какая-то конкретная причина, по которой язык не может поддерживать функции varargs без именованных параметров.Должна существовать альтернативная форма va_start для использования в таких функциях, которая должна была бы получить первый vararg непосредственно из указателя стека (или для педантизации указателя кадра, который фактически является значением, которое указывает указатель стека).имелось при входе в функцию, поскольку код в функции вполне мог бы сдвинуть sp с момента входа в функцию).В принципе это возможно - любая реализация должна каким-то образом иметь доступ к стеку [*], но это может раздражать некоторых разработчиков.Когда вы знаете соглашение о вызовах varargs, вы обычно можете реализовать макросы va_ без каких-либо других знаний, специфичных для реализации, и для этого потребуется также , зная, как напрямую получить аргументы вызова.Я реализовывал эти макросы varargs ранее, на уровне эмуляции, и это меня бы раздражало.

Кроме того, не так много практического использования для функции varargs без именованных параметров.Для функции varargs не существует языковой функции для определения типа и количества переменных аргументов, поэтому вызываемый должен все равно знать тип первого vararg, чтобы прочитать его.Так что вы могли бы также сделать его именованным параметром с типом.В printf и его друзьях значение первого параметра сообщает функции, что типов относятся к варагам и сколько их.

IПредположим, что в теории вызываемый может взглянуть на какой-то глобальный объект, чтобы понять, как читать первый аргумент (и есть ли он вообще), но это довольно неприятно.Я, конечно, не стал бы изо всех сил поддерживать это, и добавление новой версии va_start с дополнительным бременем реализации мешает мне.

[*] или если реализация не используетстек, к тому, что он использует вместо того, чтобы передавать аргументы функции.

9 голосов
/ 04 июля 2011

В списке аргументов переменной длины вы должны объявить тип первого аргумента - это синтаксис языка.

void f(int k, ...)
{
    /* do something */
}

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

1 голос
/ 02 октября 2014

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

template<typename T>
void print(T first, ...)
{
    va_list vl;
    va_start(vl, first);
    T temp = first;
    do
    {
        cout << temp << endl;
    }
    while (temp = va_arg(vl, T));
    va_end(vl);
}

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

1 голос
/ 04 июля 2011

C допускает аргументы переменной длины, но вам нужно использовать va_list, va_start, va_end и т. Д. Как вы думаете, printf и друзья реализованы?Тем не менее, я бы порекомендовал против этого.Обычно вы можете выполнить аналогичную вещь более чисто, используя массив или структуру для параметров.

0 голосов
/ 04 июля 2011

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

Мои предположения об их мотивации заключаются в том, что для разрешения void f (...) потребуется больше «скрытого» кода (который можно учесть)как время выполнения), чем не допустить его: для того, чтобы отличить случай f () от f (arg) (и других), C должен предоставить способ подсчитать, сколько аргументов передано, и для этого требуется больше сгенерированного кода (и, вероятно, новое ключевое слово или специальная переменная, например, скажем «nargs», чтобы получить счетчик), а C обычно старается быть как можно более минималистичным.

0 голосов
/ 04 июля 2011

... не допускает аргументов, то есть: для int printf(const char *format, ...); оператор

printf("foobar\n");

действителен.

Если вы не указали хотя бы 1 параметр (который должен использоваться для проверки дополнительных параметров), функция не сможет «узнать», как она была вызвана.

Все эти утверждения будут действительны

f();
f(1, 2, 3, 4, 5);
f("foobar\n");
f(qsort);
...