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

Является ли snprintf() ВСЕГДА нулевым завершением?

Является ли snprintf всегда нулевым завершением буфера назначения?

Другими словами, достаточно ли этого:

char dst[10];

snprintf(dst, sizeof (dst), "blah %s", somestr);

или вам нужно сделать это, если somestr достаточно длинный?

char dst[10];

somestr[sizeof (dst) - 1] = '\0';
snprintf(dst, sizeof (dst) - 1, "blah %s", somestr);

Меня интересует как то, что говорит стандарт, так и то, что может сделать какой-то популярный libc, который не является стандартным поведением.

4b9b3361

Ответ 1

Как другие ответы устанавливают: :

snprintf... Записывает результаты в буфер символьной строки. (...) будет завершен нулевым символом, если buf_size не равен нулю.

Итак, все, что вам нужно позаботиться, это то, что вы не передаете ему нулевой буфер, потому что (очевидно) он не может записать нуль в "никуда".


Однако остерегай, что в Microsoft library нет функции с именем snprintf, но вместо этого исторически только была функция с именем _snprintf (обратите внимание на подчеркивание), что делает не добавлять завершающий нуль. Здесь документы (VS 2012, ~~ VS 2013):

http://msdn.microsoft.com/en-us/library/2ts7cx93%28v=vs.110%29.aspx

Возвращаемое значение

Пусть len - длина форматированной строки данных (не включая завершающий нуль). len и count в байтах для _snprintf, wide символов для _snwprintf.

  • Если len < count, тогда len-символы хранятся в буфере, а null-terminator добавляется, и возвращается len.

  • Если len = count, тогда len-символы хранятся в буфере, нет null-terminator добавляется, и возвращается len.

  • Если len > count, то счетчики хранятся в буфере, нет null-terminator добавляется, и возвращается отрицательное значение.

(...)

Visual Studio 2015 (VC14), по-видимому, представил совместимую функцию snprintf, но прежняя с ведущим подчеркиванием и не нулевым завершающим поведением все еще существует

Функция snprintf усекает вывод, когда len больше чем или равный счету, помещая нуль-терминатор в buffer[count-1]. (...)

Для всех функций, отличных от snprintf, если len = count, len символы хранятся в буфере, не добавляется нуль-терминатор, (...)

Ответ 2

Согласно snprintf (3) manpage.

Функции snprintf() и vsnprintf() пишутся не более size байтов (включая конечный нулевой байт ('\ 0')) до str.

Итак, да, не нужно прекращать, если size >= 1.

Ответ 3

В соответствии со стандартом C, если размер буфера не равен 0, vsnprintf() и snprintf() null завершает свой вывод.

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

Итак, если вам нужно знать, как большой буфер выделяется, используйте нулевой размер, и затем вы можете использовать нулевой указатель в качестве адресата. Обратите внимание, что я связан с страницами POSIX, но они явно говорят, что не существует никакой расхождения между стандартом C и POSIX, где они охватывают одно и то же основание:

Функциональность, описанная на этой справочной странице, соответствует стандарту ISO C. Любой конфликт между описанными здесь требованиями и стандартом ISO C является непреднамеренным. Этот объем POSIX.1-2008 относится к стандарту ISO C.

Будьте осторожны с версией Microsoft vsnprintf(). Он определенно ведет себя иначе, чем стандартная версия C, когда в буфере недостаточно места (он возвращает -1, когда стандартная функция возвращает требуемую длину). Не совсем ясно, что версия версии Microsoft завершает свой вывод в условиях ошибки, тогда как стандартная версия C делает.

Обратите внимание также на ответы Используете ли вы безопасные функции TR 24731? (см. MSDN для версии Microsoft vsprintf_s()) и Mac для безопасных альтернатив небезопасным функциям стандартной библиотеки C?

Ответ 4

Некоторые более старые версии SunOS сделали странные вещи с snprintf и, возможно, не NUL-завершили вывод и имели возвращаемые значения, которые не соответствовали тем, что делали все остальные, но все, что было выпущено за последние 10 лет, было что делает C99.

Ответ 5

Неоднозначность начинается с самого стандарта C. Оба C99 и C11 имеют идентичное описание функции snprintf. Вот описание с C99:

7.19.6.5 Функция snprintf
конспект
1 #include <stdio.h> int snprintf(char * restrict s, size_t n, const char * restrict format,...);
Описание
2 Функция snprintf эквивалентна fprintf, за исключением того, что вывод записывается в массив (заданный аргументом s), а не в поток. Если n равно нулю, ничего не записывается, и s может быть нулевым указателем. В противном случае выходные символы за пределами n-1 st отбрасываются, а не записываются в массив, а нулевой символ записывается в конце символов, фактически записанных в массив. Если копирование происходит между перекрывающимися объектами, поведение не определено.
Возвращает
3 Функция snprintf возвращает количество символов, которые были написаны, если бы n было достаточно большим, не считая завершающего нулевого символа или отрицательным значением, если произошла ошибка кодирования. Таким образом, вывод с нулевым завершением был полностью написан тогда и только тогда, когда возвращаемое значение неотрицательно и меньше n.

С одной стороны, предложение

В противном случае выходные символы за пределами n-1 й отбрасываются, а не записываются в массив, а нулевой символ записывается в конце символов, фактически записанных в массив

Говорит, что
if (s указывает на массив длиной 3 символа и) n равно 3, тогда будут записаны 2 символа, а символы, выходящие за пределы второго, будут отброшены; то нулевой символ записывается после этих 2 (и нулевой символ будет написан третьим символом).

И это, на мой взгляд, отвечает на исходный вопрос.
ОТВЕТ:
Если копирование происходит между перекрывающимися объектами, поведение не определено.
Если n равно 0, то на вывод ничего не записывается
в противном случае, если ошибки кодирования не встречаются, выход ALWAYS завершает нуль (независимо от того, соответствует ли выход в выходном массиве или нет, а если нет, то некоторые символы отбрасываются, так что выходной массив никогда не переполняется)
в противном случае (если ошибки кодирования встречаются), выход может оставаться не нулевым.

С другой стороны
Последнее предложение

Таким образом, вывод с нулевым завершением был полностью записан тогда и только тогда, когда возвращаемое значение неотрицательно и меньше n

дает двусмысленность (или мой английский недостаточно хорош). Я могу интерпретировать это предложение по крайней мере двумя способами:
1. Выход завершен с нулевым значением тогда и только тогда, когда возвращаемое значение неотрицательно и меньше n (это означает, что если возвращаемое значение не меньше n, то есть выход (включая завершающий нулевой символ) не помещается в массив, то вывод не заканчивается нулем).
2. Выход завершен (ни один символ не был отброшен) тогда и только тогда, когда возвращаемое значение неотрицательно и меньше n.


Я считаю, что интерпретация 1 выше противоречит ОТВЕТ, вызывает непонимание и длительные дискуссии. Вот почему последнее предложение, описывающее функцию snprintf нуждается в изменении, чтобы устранить любую двусмысленность (что дает основания для написания предложения на стандарт языка C).
Пример недвусмысленной формулировки, я считаю, может быть взят из http://en.cppreference.com/w/c/io/fprintf (см. 4)), благодаря @"Martin Ba" для ссылки.

См. Также вопрос " snprintf: Существуют ли какие-либо стандартные предложения/планы C, чтобы изменить описание этой функции? ".