Передача комментариев для ответа.
Основная причина vsnprintf()
была добавлена в C99 в том, что это труднодля защиты vsprintf()
или аналогичный.Один из обходных путей - открыть /dev/null
, использовать vfprintf()
, чтобы отформатировать данные, отметить, насколько велик был результат, а затем решить, безопасно ли продолжать.Ики, особенно если вы открываете устройство при каждом вызове.
Это означает, что ваш код может стать:
#include <assert.h>
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
extern char *formatString(const char *format, ...);
char *formatString(const char *format, ...)
{
static FILE *fp_null = NULL;
if (fp_null == NULL)
{
fp_null = fopen("/dev/null", "w");
if (fp_null == NULL)
return NULL;
}
va_list ap;
va_start(ap, format);
int size = vfprintf(fp_null, format, ap);
va_end(ap);
if (size < 0)
return NULL;
char *result = (char *) malloc(size + 1);
if (result == NULL)
return NULL;
va_start(ap, format);
int check = vsprintf(result, format, ap);
va_end(ap);
assert(check == size);
return result;
}
int main(void)
{
char *r1 = formatString("%d Dancing Pigs = %4.2f%% of annual GDP (grandiose dancing pigs!)\n",
34241562, 21.2963);
char *r2 = formatString("%s [%-13.10s] %s is %d%% %s\n", "Peripheral",
"sub-atomic hyperdrive", "status", 99, "of normality");
if (r1 != NULL)
printf("r1 = %s", r1);
if (r2 != NULL)
printf("r2 = %s", r2);
free(r1);
free(r2);
return 0;
}
Как написано с fp_null
статической переменной внутри функции,Поток файла не может быть закрыт.Если это беспокоит, сделайте его переменной внутри файла и предоставьте функцию для if (fp_null != NULL) { fclose(fp_null); fp_null = NULL; }
.
Я не подозреваю, что Unix-подобная среда с /dev/null
;вы можете перевести это на NUL:
, если вы работаете в Windows.
Обратите внимание, что исходный код в вопросе не использовал va_start()
и va_end()
дважды (в отличие от этого кода);это приведет к катастрофе.На мой взгляд, неплохо ставить va_end()
как можно скорее после va_start()
, как показано в этом коде.Ясно, что если ваша функция сама проходит через va_list
, то здесь будет больший разрыв, чем показано здесь, но когда вы просто передаете аргументы переменной другой функции, как здесь, должна быть только одна строка между.
Код корректно компилируется на Mac под управлением macOS 10.14 Mojave с использованием GCC 8.2.0 (скомпилировано в macOS 10.13 High Sierra) с командной строкой:
$ gcc -O3 -g -std=c90 -Wall -Wextra -Werror -Wmissing-prototypes \
> -Wstrict-prototypes vsnp37.c -o vsnp37
$
При запуске он выдает:
r1 = 34241562 Dancing Pigs = 21.30% of annual GDP (grandiose dancing pigs!)
r2 = Peripheral [sub-atomic ] status is 99% of normality