Функция с переменным количеством параметров - PullRequest
3 голосов
/ 12 мая 2009

с учетом этой функции

double avg(double v1,double v2,...)
{
    double sum=v1+v2;
    int counter=2;
    double temp;
    va_list pargs;
    va_start(pargs,v2);
    while((temp=va_arg(pargs,double))!=0.0)
    {
        sum+=temp;
        counter++;
    }
    va_end(pargs);
    return sum/counter;
}

Этот вызов printf("%lf\n",avg(3.0,4.5,4.5,3.0,0.0)) возвращает правильный результат, но если я удаляю последний параметр 0.0, он печатает -321738127312000000000.0000000, но сумма и счетчик имеют правильные значения. Я вроде не понимаю, почему я должен проверить это !=0.0 и иметь последний параметр 0.0

Ответы [ 5 ]

6 голосов
/ 12 мая 2009

Поскольку без какой-либо внешней информации функция не знает, сколько аргументов было передано. Существует несколько стратегий для решения этой проблемы: включите явный аргумент, который представляет собой число дополнительных аргументов, используйте строку формата для определения аргументы (например, с семейством функций printf и scanf), или используйте значение дозорного, например 0, для объявления конца аргументов.

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

3 голосов
/ 12 мая 2009

если вы удалите! = 0.0, ваша программа выполняет грязное чтение, пока не прочитает нулевой блок памяти.

у вас есть два варианта:

  • укажите, сколько аргументов вы передаете, т.е. avg (3, 4.3, 2.0, 3.0);
  • укажите терминатор или страж, то есть среднее (4.3, 2.0, 3.0, 0.0);

EDIT

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

#define avg(v1, v2, ...) _avg((v1), (v2), __VA_ARGS__, 0.0)

double _avg(double v1,double v2,...) 
{ 
    /* same code, just prefixing function name with _ */

берегитесь:

avg(3.0, 3.0, 0.0, 100.0, 100.0) 

возвращает 3.0, так как вы преждевременно завершаете va_list. Вы можете попробовать использовать другое "странное" значение часового ...

1 голос
/ 12 мая 2009

Вам нужно иметь значение защиты (0.0) и проверять его, потому что компилятор не обязательно подсчитывает или ограничивает параметры при построении фрейма стека. Поэтому вы можете продолжить чтение (или запись) за пределы списка параметров и в данные, которые содержат ваш указатель возврата, ваши локальные переменные или что-то еще. Если вы посмотрите на реализацию вашего компилятора va_arg, вы, вероятно, обнаружите, что все, что он делает, - это инициализирует указатель за адресом вашей переменной (v2) и затем увеличивает его до указанного вами размера (double). Он с радостью сделает это, пока вы не получите нарушение чтения.

1 голос
/ 12 мая 2009

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

Это то, что делает тест для! = 0.0, он использует дозорное значение (0) для определения конца ряда. Еще один способ сделать это - передать число элементов в качестве первого параметра функции, а затем использовать цикл for для циклического перебора переменных args.

0 голосов
/ 12 мая 2009

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

Использование дозорного значения допустимо только в том случае, если в вашем домене есть соответствующее значение. Например, если вы имеете дело только с положительными числами, то вы можете использовать любой отрицательный номер в качестве дозорного. Однако для avg () имеет больше смысла принимать весь домен с плавающей точкой.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...