Использование snprintf для предотвращения переполнения буфера

gcc 4.4.1

С99

Я использую snprintf как это, чтобы избежать переполнения буфера:

char err_msg[32] = {0};
snprintf(err_msg, sizeof(err_msg) - 1, "[ ST_ENGINE_FAILED ]");

Я добавил -1 для резервирования пространства для нулевого терминатора, если длина строки больше 32 байтов.

Правильно ли я в мыслях?

Ответ 1

Как говорили другие, в этом случае вам не нужен -1. Если массив имеет фиксированный размер, вместо этого я бы использовал strncpy. Он был сделан для копирования строк - sprintf был сделан для сложного форматирования. Однако, если размер массива неизвестен или вы пытаетесь определить, сколько памяти необходимо для форматированной строки. Это то, что мне действительно нравится в стандартной версии snprintf:

char* get_error_message(char const *msg) {
    size_t needed = snprintf(NULL, 0, "%s: %s (%d)", msg, strerror(errno), errno);
    char  *buffer = malloc(needed+1);
    sprintf(buffer, "%s: %s (%d)", msg, strerror(errno), errno);
    return buffer;
}

Объедините эту функцию с va_copy и вы можете создать очень безопасные форматированные операции с строками.  

Ответ 2

Вам не нужен -1, поскольку состояние ссылки:

Функции snprintf() и vsnprintf() не пишут больше, чем размер байтов (включая '\ 0').

Обратите внимание на "включая конечную" \0 "часть

Ответ 3

Нет необходимости в -1. C99 snprintf всегда завершает нуль. Аргумент размера задает размер выходного буфера, включая нулевой терминатор. Таким образом, код становится

char err_msg[32];
int ret = snprintf(err_msg, sizeof err_msg, "[ ST_ENGINE_FAILED ]");

ret содержит фактическое количество напечатанных символов (исключая нулевой ограничитель).

Однако не путайте с Microsoft _snprintf (pre-C99), который делает не заканчивается и, если на то пошло, имеет совершенно другое поведение (например, возврат -1 вместо потенциальной длины печати в случае, если буфер недостаточно велик). Если вы используете _snprintf, вы должны использовать тот же код, что и в своем вопросе.

Ответ 4

Согласно snprintf(3):

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

Ответ 5

В приведенном примере вы должны сделать это вместо:

char err_msg[32];
strncpy(err_msg, "[ ST_ENGINE_FAILED ]", sizeof(err_msg));
err_msg[sizeof(err_msg) - 1] = '\0';

или даже лучше:

char err_msg[32] = "[ ST_ENGINE_FAILED ]";

Ответ 6

sizeof вернет число байтов, которое тип данных будет использовать в памяти, а не длина строки. Например. sizeof (int) возвращает "4 байта" в 32-битной системе (ну, в зависимости от реализации, я думаю). Поскольку вы используете константу в своем массиве, вы можете с радостью передать ее printf.