Как: защитить память - strncat ()? - PullRequest
2 голосов
/ 28 октября 2011
function(char *a, char *b)
{

   char newStr[100];

   strncpy(newStr, a, sizeof(newStr)); //Line 1 - copy no more than 100 bytes  

   strncat(newStr, b, (sizeof(newStr) -  strlen(newStr)));   //Line 2 - ?

   newStr[99] = NULL; //Line 3 - null terminate string

}

Строка 2: Правильно указать 100 байтов минус strlen того, что скопировано из a, чтобы гарантировать, что я не копирую 100 байтов?

Спасибо.

Ответы [ 2 ]

3 голосов
/ 28 октября 2011

strncpy() не делает то, о чем вы, вероятно, думаете.

strncat() - это «более безопасная» версия strcat(), позволяющая указать размер целевого массива.

strncpy() - это , а не соответствующая "более безопасная" версия strcpy(). Если целевой массив слишком большой, strncpy() заполнит его нулевыми символами; В 99% случаев это не нужно, так как вам нужен только один '\0', чтобы отметить конец строки. Хуже того, если целевой массив слишком мал, strncpy() скопирует столько символов, сколько может , и оставит цель без определения .

strncpy() был разработан для неясной структуры данных, используемой ранними системами Unix для хранения имен файлов. Имя файла было сохранено в 14-байтовом буфере фиксированной длины, дополненном нулевыми байтами. Если бы имя файла было ровно 14 символов, не было бы нулевого терминатора. Это не строка .

В маловероятном случае, когда вам нужна такая структура данных, тогда strncpy() - это просто вещь. В противном случае не используйте его; просто используйте strcpy() после подтверждения того, что цель достаточно большая.

Вот как я мог бы написать эту функцию:

void function(char *a, char *b)
{
    char newStr[100];

    /* Make newStr an empty string so you can concatenate onto it */
    newStr[0] = '\0';
    strncat(newStr, a, sizeof newStr - 1);                  /* edited */
    strncat(newStr, b, sizeof newStr - strlen(newStr) - 1); /* edited */

    /* Presumably you do something with newStr here */
}

Примечания:

  1. Объявите тип возвращаемого значения для функции. Если вы не объявите это явно, ваш компилятор, вероятно, будет по умолчанию использовать int, но это плохой стиль и устаревшая языковая функция.
  2. Избегайте strncat().
  3. Я использовал '\0', а не NULL, чтобы завершить строку нулем. NULL - нулевой указатель константа; не используйте его для обозначения нулевого символа .

Здесь есть существенная неэффективность: второй strncat() должен повторно сканировать с начала newStr. Для небольшого количества коротких строк это не имеет большого значения, но для большого количества строк, объединяемых в большой целевой массив, это может вызвать серьезные замедления. Есть способы обойти это, но они либо нестандартны (strlcpy(), strlcat()), либо неудобны.

РЕДАКТИРОВАТЬ : Спасибо Мэтью за указание на ошибки в моем коде. Я думаю Я исправил их; Я уверен, что могу рассчитывать на то, что кто-то ударит меня по голове, если я заменю старые ошибки новыми.

Альтернатива:

snprintf(newStr, sizeof newStr, "%s%s", a, b);
2 голосов
/ 28 октября 2011

Это почти правильно. Во-первых, NUL-строка завершения:

newStr[99] = NULL;

не так.

strncat всегда NUL-завершает, а третий параметр - максимальное количество байтов для записи NOT , включая NUL.

Гипотетически, если strncat не завершать NUL, проблема с этой строкой состоит в том, что она всегда записывает в последний элемент массива, даже если фактическая строка может быть намного короче. Если бы a и b были «Hello» и «world!», Конечный массив был бы:

H | е | л | л | о |, | | Ш | о | г | л | д |! | Г | I | B | B | е | г | I | s | ч

где gibberish представляет предыдущее содержимое массива в этих позициях. Только в 99, после большинства этих неинициализированных остатков, будет NUL.

РЕДАКТИРОВАТЬ: Кроме того, Кит прав насчет strncpy. Его функция частично верна, но вторая функция может переполнять буфер, поскольку она не учитывает строку, которая уже существует. Две строки могут содержать до 199 символов (включая NUL).

Кроме того, я ошибся в третьем параметре, включая NUL. Это оставляет нас с (модифицированным от Кейта):

void function(char *a, char *b)
{
    char newStr[100];

    /* Make newStr an empty string so you can catenate onto it */
    newStr[0] = '\0';
    strncat(newStr, a, sizeof newStr - 1);
    strncat(newStr, b, sizeof newStr - strlen(newStr) - 1);

    /* Presumably you do something with newStr here */
}
...