C, Работа с функциями с переменным аргументом

Допустим, я хочу сделать что-то вроде этого

void my_printf(char *fmt,...) {
 char buf[big enough];
 sprintf(buf,fmt,...);  
}

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


person Mike    schedule 10.03.2010    source источник
comment
Как вы узнаете, что буфер достаточно велик? И вы действительно должны вернуть количество преобразований, выполненных sprintf(), иначе пользователи не смогут узнать, что функция сработала.   -  person    schedule 11.03.2010
comment
Я вырезал все, что не было необходимо для иллюстрации моей точки зрения. Очевидно, в моем коде релиза я гораздо тщательнее :)   -  person Mike    schedule 11.03.2010


Ответы (3)


sprintf имеет форму va_list, называемую vsprintf. Передайте va_list, который вы создаете локально, в качестве последнего аргумента.

void my_printf(char *fmt,...) {
 va_list ap;
 va_start(ap, fmt);

 char buf[big enough];
 vsprintf(buf,fmt,ap);

 va_end(ap);
}
person Johannes Schaub - litb    schedule 10.03.2010
comment
Рабочий заголовок — <stdarg.h>. - person Mike DeSimone; 11.03.2010
comment
Если он доступен, вы должны использовать vsnprintf. - person R Samuel Klatchko; 11.03.2010

Я не уверен, насколько полезным будет этот код, поскольку он написан на C++, но он показывает, как с помощью специальной функции Win32 vsnprintf() проверить, что выделенный буфер достаточно велик, а если нет, то выделяет еще больший. И он возвращает std::string, поэтому вам придется использовать malloc/realloc для обработки этого. Но какого черта:

string Format( const char * fmt, ... ) {
    const int BUFSIZE = 1024;
    int size = BUFSIZE, rv = -1;
    vector <char> buf( size );
    do {
        va_list valist;
        va_start(valist, fmt );
        // if vsnprintf() returns < 0, the buffer wasn't big enough
        // so increase buffer size and try again
        rv = _vsnprintf( &buf[0], size, fmt, valist );
        va_end( valist );
        size *= 2;
        buf.resize( size );
    }
    while( rv < 0 );
    return string( &buf[0] );
}
person Community    schedule 10.03.2010

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

 va_list ap;
char *param;
va_start(ap,fmt);
param = va_arg(ap,char*);
while(param)
{
do something...
param = va_arg(ap,char*);
}

или вы можете указать количество параметров в качестве первого параметра вашей функции

void my_printf(int param_num,...)
{
 va_list ap;
char *param;
va_start(ap,fmt);
while(param_num)
{
do something...
param = va_arg(ap,char*);
param_num--;
}

}

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

person Medran    schedule 10.03.2010
comment
требование от пользователя указать количество параметров было бы проектной катастрофой - person Mike; 11.03.2010
comment
я хочу сказать, что ... это не голубь, затаившийся в определенном употреблении. Пользователь функции может решить, что лучше для его ситуации. Передача числа параметров ничем не отличается от включения числа в строку... т.е. sprintf(%d %d,a,b) точно такой же, как yourprintf(2,a,b), sprintf такой же склонны к ошибкам пользователя. - person Medran; 11.03.2010