Подтвердить что ты не робот

Snprintf для конкатенации строк

Я использую snprintf для конкатенации строки в массив char:

char buf[20] = "";
snprintf(buf, sizeof buf, "%s%s", buf, "foo");
printf("%s\n", buf);
snprintf(buf, sizeof buf, "%s%s", buf, " bar");
printf("%s\n", buf);

Проблема заключается в втором объединении buf вместо добавления "bar", заменяет его на "foo". Результат выглядит так:

foo
bar

Первый %s должен содержать buf (который в этом случае содержит "foo"). А второй %s должен прикрепить к нему "bar". Правильно?

Что я делаю неправильно?

4b9b3361

Ответ 1

Вы нарушаете контракт restrict на snprintf, который указывает, что никакой другой аргумент не может перекрывать буфер.

Копирование ввода в себя - это трата усилий в любом случае. 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);

Ответ 2

Почему бы не использовать strncat()? Он был разработан таким образом:

char buf[20] = "";
strncat(buf, "foo", sizeof buf);
printf("%s\n", buf);
strncat(buf, " bar", sizeof buf - strlen(buf));
printf("%s\n", buf);

Если ваши системы поддерживают его, вы можете использовать strncat_s() вместо strncat, поскольку он имеет дополнительный уровень защиты от переполнения и избегает необходимости вычисления количества оставшихся байтов в выходном буфере.

Если вы должны использовать snprintf, вам нужно будет создать отдельный указатель, чтобы отслеживать конец строки. Этот указатель будет первым аргументом, который вы передадите в snprintf. В вашем текущем коде всегда используется buf, что означает, что он всегда будет печатать в начале этого массива. Вы можете использовать strlen, чтобы найти конец строки после каждого вызова snprintf, или вы можете использовать возвращаемое значение snprintf для увеличения указателя.

Ответ 3

Хотя принятый ответ в порядке, лучший (на мой взгляд) ответ заключается в том, что конкатенация строк неверна. Вы должны построить весь вывод в одном вызове snprintf. Все дело в использовании форматированных выходных функций, и это намного эффективнее и безопаснее, чем выполнение арифметических указателей и множественных вызовов. Например:

snprintf(buf, sizeof buf, "%s%s%s", str_a, str_b, str_c);

Ответ 4

Попробуйте следующее:

char buf[20];
snprintf(buf, sizeof buf, "%s", "foo");
printf("%s\n", buf);
int len = strlen(buf);
snprintf(buf+len, (sizeof buf) - len, "%s", " bar");
printf("%s\n", buf);

Выход - "foo bar". Первый аргумент snprintf, указатель на char, - это то, где он начнет заполнять символы. Он не обращает внимания на то, что уже находится в буфере. Однако функция strlen обращает внимание. Он подсчитывает количество символов перед nul (0), которое snprintf помещает там. Поэтому вместо передачи buf передайте buf + strlen (buf). Вы также можете использовать strncat, который будет немного более эффективным.

Я вижу тег С++ по вашему вопросу. Посмотрите std::string. Лучше.