почему этот пример явно устанавливает последний символ строки (назначенный `snprintf ()`) равным `/ 0`? - PullRequest
0 голосов
/ 03 марта 2019

С https://stackoverflow.com/a/13067917/156458

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

Так почему в следующем примере из интерфейса программирования Linux явно установлен последний символ строки (назначенный snprintf()) будет \0?

char *
inetAddressStr(const struct sockaddr *addr, socklen_t addrlen,
               char *addrStr, int addrStrLen)
{
    char host[NI_MAXHOST], service[NI_MAXSERV];
    if (getnameinfo(addr, addrlen, host, NI_MAXHOST,
                    service, NI_MAXSERV, NI_NUMERICSERV) == 0)
        snprintf(addrStr, addrStrLen, "(%s, %s)", host, service);
    else
        snprintf(addrStr, addrStrLen, "(?UNKNOWN?)");
    addrStr[addrStrLen - 1] = '\0';     /* Ensure result is null-terminated */
    return addrStr;
}

Ответы [ 2 ]

0 голосов
/ 03 марта 2019

Установка последнего символа целевого массива на '\0' явно представляется бесполезной, если snprintf соответствует стандарту C.

Обратите внимание, однако, что ядро ​​Linux использует свою собственную версию библиотеки C, которая можетили может не соответствовать стандарту C.Большинство функций старше, чем стандарт C99, где впервые было указано snprintf, что может объяснять различия в реализации.Текущая версия реализации ядра snprintf устанавливает нулевой терминатор.Похоже, что опубликованный код получен из кода пользователя, а не кода ядра, так что это не имеет значения.

Обратите также внимание, что некоторые библиотеки C имеют нестандартное поведение.Например, библиотека времени выполнения Microsoft C не поддерживала расширения C99 в течение почти 15 лет после ее публикации и все еще не полностью соответствует по различным причинам.Например, snprintf имеет нестандартное поведение для преобразования %n.

Обратите также внимание, что некоторые расширения Microsoft с именами, очень похожими на стандартные функции, ведут себя удивительным и запутанным образом.Например, _snprintf() не будет устанавливать нулевой терминатор в целевом массиве, если выходные данные будут усечены.см. эту страницу документации для подробностей.

Для размещенного кода библиотека C вряд ли поступит от Microsoft, но ядро ​​linux может использоваться в системах с различными библиотеками C: GNU libc используется для большинства настольных дистрибутивов, но Android, например, использует другую библиотеку.Автор фрагмента кода:

  • не полностью понимает стандарт C и написал избыточный код.
  • не доверяет реализации локальной библиотеки C и не хочетпредположим, snprintf записывает нулевой терминатор.
  • использует защитное программирование по привычке и на всякий случай пишет избыточный код.
0 голосов
/ 03 марта 2019

Это не должно быть необходимо при правильной реализации snprintf.Однако в некоторых системах snprintf был оберткой вокруг некоторых «похожих» функций, которые делали такие забавные вещи, как , не записывая байт NUL в конце или игнорируя аргумент length.Это не относится к snprintf из любой системы POSIX или snprintf из linux или freebsd ядер.

Кроме того, этот кодне проверять возвращаемое значение snprintf (удалось ли скопировать всю строку?), что для примера кода довольно грубое.


snprintf всегда завершает строку, которую копирует вбуфер с байтом NUL, , если аргумент длина / размер не равен 0 , и в этом случае он ничего не копирует.Цитирование из susv4 [1]:

int snprintf(char *restrict s, size_t n, const char *restrict format, ...);

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

Если копирование происходит междуобъекты, которые перекрываются в результате вызова sprintf () или snprintf (), результаты не определены.

[1] язык в стандарте C99 идентичен

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...