Использование snprintf для создания отформатированных строк - PullRequest
0 голосов
/ 16 января 2019

Я пытаюсь узнать о snprintf и нашел этот ответ с примером:

char buf[20] = "";
char *cur = buf, * const end = buf + sizeof buf;
cur += snprintf(cur, end-cur, "%s", "foo");
printf("%s\n", buf);
if (cur < end) {
    cur += snprintf(cur, end-cur, "%s", " bar");
}
printf("%s\n", buf);
free(str);

Что мне не ясно, так это то, что мы выделяем фиксированный размер буфера в жестком коде, который, похоже, страдает от переполнения буфера. В N1570 я обнаружил, что (7.21.6.5)

1

#include <stdio.h>
int snprintf(char * restrict s, size_t n,
const char * restrict format, ...);

2 Функция snprintf эквивалентна функции fprintf, за исключением того, что вывод записывается в массив (заданный аргументом s), а не в поток. Если n равно нулю, ничего не написано, а s может быть нулем указатель.

Так что мне кажется, что идиоматическое использование будет следующим:

int need_space = snprintf(NULL, 0, "abs %s", "fgh") + 1; //How much to allocate?
char *const str = malloc(need_space * sizeof(char)); //allocate
int written = snprintf(str, need_space, "abs %s", "fgh"); //do format
printf("Need space = %d, written = %d\n", need_space, written);
printf("%s\n", str);

Или это не распространено и имеет другую проблему?

1 Ответ

0 голосов
/ 16 января 2019

2х вызов snprintf() является распространенной идиомой.

... есть другая проблема?

Проблемы лежат по углам

Ведение формата

Ниже приведен повторяющийся формат, склонный к разрыву по мере старения кода, и только одна строка изменяется.

// Oops!
int need_space = snprintf(NULL, 0, "abs %s", "fgh") + 1;
char *const str = malloc(need_space * sizeof(char));
int written = snprintf(str, need_space, "abs %s ", "fgh");

Вы заметили разницу?

Лучше один раз указать формат.

#define FMT_ABS_S "abs %s"
int need_space = snprintf(NULL, 0, FMT_ABS_S, "fgh");
char *const str = malloc(sizeof *str * (need_space + 1u));
int written = snprintf(str, need_space, FMT_ABS_S, "fgh");

Волатильность

Код должен гарантировать, что значения не изменяются (не volatile) между двумя вызовами, и в многопоточных приложениях возникают особые проблемы.

Ошибка проверки

В коде отсутствуют проверки. Но даже с проверками, как справиться с неожиданными результатами - возможно, просто под залог?

static const char *fmt_s = "abs %s";
int need_space = snprintf(NULL, 0, fmt_s, "fgh");
if (need_space < 0) {
  Handle_EncodingError();
}
char *const str = malloc(sizeof *str * (need_space + 1u));
if (str == NULL) {
  Handle_OutOfMemory();
}
int written = snprintf(str, need_space, fmt_s, "fgh");
if (written < 0 || written > need_space) {
  Handle_Error();
}

Пройдя дважды, хотя

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

Тем не менее, для меня «что делать, если выделение не удастся», в любом случае является ограничителем показа, и код сообщает об ошибке и, возможно, завершается.

Выборочно достаточно одного раза

Когда формат s*printf() строго контролируется, я считаю достаточным 1 вызов.

// int to string

#define LOG2_N 28
#define LOG2_D 93
// Number of char needed for a string of INT_MIN is log10(bit width) + 3
#define INT_LEN ((sizeof(int)*CHAR_BIT-1)*LOG2_N/LOG2_D + 3)

char s[INT_LEN * 2];  // I like to use 2x to handle locale issues, no need to be stingy here.

int len = snprintf(s, sizeof s, "%d", i);

if (len < 0 || len >= sizeof s) {
  Handle_VeryUnusualFailure();
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...