На что указывает arg в va_start?

Вот код. Комментарием пометил место, где возникает вопрос. Данный printf() выводит размер в 9 байт. Я не очень понимаю, как это работает.

char* add_to_string(const char *format, char *dest, ...)
{
    va_list arg;
    int user_str_size = sizeof(*dest) * strlen(dest);
    va_start(arg, dest);
    int new_size = vsnprintf(NULL, 0, format, arg);
    printf("Size %d\n", new_size); // не понимаю вывод в этом месте!
    va_end(arg);
    dest = (char *)realloc(dest, sizeof(char) * new_size + 1);
    va_start(arg, dest);
    vsnprintf(dest + strlen(dest), new_size - user_str_size + 1, format, arg);
    va_end(arg);
    return dest;
}
char *r = strdup("Aa ");
r = add_to_string("%s %s %s", r, "Bb", "Cc");

Вывод моей программы: Size 9 Aa Bb Cc

Получается, что arg указывает на dest, а не на элементы после него, так? Вот пример из интернета.


void PrintFError ( const char * format, ... )
{
  char buffer[256];
  va_list args;
  va_start (args, format);
  vsnprintf (buffer,256,format, args);
  perror (buffer);
  va_end (args);
}

int main ()
{
   FILE * pFile;
   char szFileName[]="myfile.txt";

   pFile = fopen (szFileName,"r");
   if (pFile == NULL)
     PrintFError ("Error opening '%s'",szFileName);
   else
   {
     // file successfully open
     fclose (pFile);
   }
   return 0;
}

Вывод их программы: Error opening file 'myfile.txt': No such file or directory

Так вот у меня вопрос, на что указывает arg после va_start? Почему мой vsprintf возвращает 9, а не 6. Почему в программе из интернета их format не подставляется в строку результата. Если бы их vsprintf работал бы так же, то они бы получили в результате строку "Error opening 'Error opening '%s''", потому что args указывает на формат. Проще говоря, я не понимаю, почему строка 'Aа ' в принципе учитывается в первом вызове vsprintf и первый вызов возращает мне 9,а не 6


Ответы (1 шт):

Автор решения: avp

arg в va_start(arg, dest); указывает на аргумент, следующий в списке вызова непосредственно за dest.

У меня (Linux, 64-bit) поведение вашей программы (из-за ошибки -- лишний %s в формате %s %s %s) не воспроизводится.

Предполагаю, что это связано с тем, что у вас 32-бит ОС, параметры передаются через стек и в вашем случае лишний аргумент (который вы не передаете в vsnprintf()) адресует "удачно расположеный" мусор в памяти.

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

char* add_to_string(const char *format, char *dest, ...)
{
    va_list arg;
    int user_str_size = strlen(dest);
    va_start(arg, dest);
    int new_size = vsnprintf(NULL, 0, format, arg);
    printf("Size %d\n", new_size); // не понимаю вывод в этом месте!
    va_end(arg);

    dest = (char *)realloc(dest, user_str_size + new_size + 1);
    va_start(arg, dest);
    vsnprintf(dest + user_str_size, user_str_size + new_size, format, arg);
    va_end(arg);
    return dest;
}

Естественно, при вызове строка формата должна содержать 2 %s

  char *r = strdup("Aa ");
  r = add_to_string("%s %s", r, "Bb", "Cc");

  printf("r = <%s>\n", r);

→ Ссылка