C странным поведением массива - PullRequest
6 голосов
/ 14 мая 2010

Узнав, что оба значения strncmp - это не то, что кажется, и что strlcpy недоступно в моей операционной системе (Linux), я решил, что могу попробовать написать сам.

Я нашел цитату из Ульриха Дреппера, сопровождающего libc, который опубликовал альтернативу strlcpy, используя mempcpy. У меня тоже нет mempcpy, но его поведение было легко воспроизвести. Во-первых, это тестовый пример у меня

#include <stdio.h>
#include <string.h>

#define BSIZE 10

void insp(const char* s, int n)
{
   int i;

   for (i = 0; i < n; i++)
      printf("%c  ", s[i]);

   printf("\n");

   for (i = 0; i < n; i++)
      printf("%02X ", s[i]);

   printf("\n");

   return;
}

int copy_string(char *dest, const char *src, int n)
{
   int r = strlen(memcpy(dest, src, n-1));
   dest[r] = 0;

   return r;
}

int main()
{
   char b[BSIZE];
   memset(b, 0, BSIZE);

   printf("Buffer size is %d", BSIZE);

   insp(b, BSIZE);

   printf("\nFirst copy:\n");
   copy_string(b, "First", BSIZE);
   insp(b, BSIZE);
   printf("b = '%s'\n", b);

   printf("\nSecond copy:\n");
   copy_string(b, "Second", BSIZE);
   insp(b, BSIZE);

   printf("b = '%s'\n", b);

   return 0;
}

И вот его результат:

Buffer size is 10                    
00 00 00 00 00 00 00 00 00 00 

First copy:
F  i  r  s  t     b     =    
46 69 72 73 74 00 62 20 3D 00 
b = 'First'

Second copy:
S  e  c  o  n  d          
53 65 63 6F 6E 64 00 00 01 00 
b = 'Second'

Вы можете видеть во внутреннем представлении (созданные строки insp()), что есть некоторый смешанный шум, например строка формата printf() в проверке после первой копии, и посторонний 0x01 во второй копии.

Строки скопированы без изменений, и он корректно обрабатывает слишком длинные исходные строки (давайте пока проигнорируем возможную проблему с передачей 0 в качестве длины copy_string, я исправлю это позже).

Но почему в моем месте назначения находится содержимое внешнего массива (из строки формата)? Это как если бы пункт назначения был действительно ИЗМЕНЕН в соответствии с новой длиной.

Ответы [ 4 ]

4 голосов
/ 14 мая 2010

Конец строки отмечен \ 0 памятью, после этого может быть что угодно, если только ваша ОС не умышленно не заблокирует его, тогда это просто случайный мусор, который там был оставлен.

Обратите внимание, что в этом случае «проблема» отсутствует в строке copy_string, вы точно копируете 10 символов - но память после «первого» в вашем основном коде просто случайна.

2 голосов
/ 14 мая 2010

Поскольку вы не останавливаетесь на исходном размере, вы останавливаетесь на конечном размере, который оказывается больше исходного, поэтому вы копируете исходную строку и немного мусора за ней.

Вы можете легко увидеть, что вы копируете исходную строку с ее нулевым терминатором. Но поскольку вы записываете 10 байтов, а обе строки «Первая» и «Вторая» короче 10 байтов, вы также копируете лишние байты после них.

1 голос
/ 14 мая 2010

Использование memcpy(dest, src, n-1) вызывает неопределенное поведение, если dest и src имеют длину не менее n-1.

Например, First\0 имеет длину шесть символов, но вы читаете из него n-1 (9) символов; содержимое памяти после конца строкового литерала не определено, как и поведение вашей программы при чтении этой памяти.

0 голосов
/ 14 мая 2010

Дополнительный "материал" есть, потому что вы передали размер буфера memcpy. Он скопирует столько символов, даже если источник короче.

Я бы поступил немного иначе:

void copy_string(char *dest, char const *src, size_t n) { 
    *dest = '\0';
    strncat(dest, src, n);
}

В отличие от strncpy, strncat определен для работы так, как большинство людей разумно ожидают.

...