C - memcpy с char *, длина которого больше длины строки источника - PullRequest
2 голосов
/ 25 апреля 2019

У меня есть следующий код в C сейчас

int length = 50
char *target_str = (char*) malloc(length);
char *source_str = read_string_from_somewhere() // read a string from somewhere
                                                //    with length, say 20
memcpy(target_str, source_str, length);

Сценарий таков, что target_str инициализируется с 50 байтами. source_str - строка длиной 20.

Если я хочу скопировать source_str в target_str, я использую memcpy () , как указано выше, с длиной 50, то есть размером target_str. Причина, по которой я использую length в memcpy, заключается в том, что максимальное значение source_str может составлять length, но обычно оно меньше этого значения (в приведенном выше примере это 20).

Теперь, если я хочу скопировать до длины source_str на основе его завершающего символа ('\0'), даже если длина memcpy больше, чем индекс завершающего символа, приведенный выше код является правильным способом сделать это ? или есть альтернативное предложение.

Спасибо за любую помощь.

Ответы [ 4 ]

2 голосов
/ 25 апреля 2019

Если я хочу скопировать source_str в target_str, я использую memcpy (), как указано выше, с длиной 50, которая является размером target_str.Причина, по которой я использую длину в memcpy, заключается в том, что source_str может иметь максимальное значение длины, но обычно оно меньше этого значения (в приведенном выше примере это 20).

Крайне важно различать

  • размер массива, на который указывает source_str, и
  • длина строки, если таковая имеется, на которую source_str указывает (+/- терминатор).

Если source_str определенно указывает на массив длиной 50 или более, то подход memcpy(), который вы представляете, - это нормально.Если нет, то это приводит к неопределенному поведению, когда source_str фактически указывает на более короткий массив.Может произойти любой результат в рамках вашей реализации C.

Если source_str точно указывает на (правильно завершенную) строку C длиной не более length - 1 символов, и если она является ее строкойзначение, которое вы хотите скопировать, тогда strcpy() более естественно, чем memcpy().Он скопирует все содержимое строки, вплоть до терминатора.Это не представляет проблемы, когда source_str указывает на массив короче length, при условии, что он содержит терминатор строки.

Если ни один из этих случаев точно не выполняется, то неясно, что вы хотитесделать.Функция strncpy() может охватывать некоторые из этих случаев, но она не охватывает все из них.

2 голосов
/ 25 апреля 2019

Сценарий состоит в том, что target_str инициализируется с 50 байтами. source_str - строка длиной 20.

Если я хочу скопировать source_str в target_str, я использую memcpy (), как указано выше, с длиной 50, которая является размером target_str.

в настоящее время вы запрашиваете memcpy для чтения 30 символов после конца строки источника, поскольку он не заботится о возможном нулевом терминаторе в источнике, это неопределенное поведение

поскольку вы копируете строку, вы можете использовать strcpy вместо memcpy

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

, чтобы вы могли использовать strncpy , указав длину цели, просто позаботьтесь о необходимости добавить окончательный нулевой символ в случае, если цель меньше исходного:

int length = 50
char *target_str = (char*) malloc(length);
char *source_str = read_string_from_somewhere(); // length unknown

strncpy(target_str, source_str, length - 1); // -1 to let place for \0
target_str[length - 1] = 0; // force the presence of a null character at end in case
1 голос
/ 25 апреля 2019

Теперь, если я хочу скопировать до длины source_str на основе его завершающего символа ('\ 0'), даже если длина memcpy больше, чем индекс завершающего символа, приведенный выше код является правильным способомсделай это?

Нет;вы будете копировать все содержимое source_str, даже после нулевого терминатора, если это происходит до конца выделенного пространства для строки, на которую он указывает.

Если ваша задача минимизировать вспомогательноепространство, используемое вашей программой, вы можете использовать strlen для определения длины source_str и выделить target_str на основании этого.Кроме того, strcpy аналогичен memcpy, но специально предназначен для строк с нулевым символом в конце (обратите внимание, что он не имеет параметра "size" или "length"):

char *target_str = NULL;
char *source_str = read_string_from_somewhere();
size_t len = strlen(source_str);

target_str = malloc(len + 1);

strcpy(target_str, source_str);

// ...

free(target_str);
target_str = NULL;
1 голос
/ 25 апреля 2019

memcpy используется для копирования фиксированных блоков памяти, поэтому, если вы хотите скопировать что-то более короткое, которое заканчивается на '\n', вы не хотите использовать memcpy.

Есть другие функции, такие как strncpy или strlcpy, которые делают подобные вещи. Лучше всего проверить, что делают реализации. Я удалил оптимизированные версии из исходного исходного кода для удобства чтения.

Это пример реализации memcpy: https://git.musl -libc.org / cgit / musl / tree / src / string / memcpy.c

void *memcpy(void *restrict dest, const void *restrict src, size_t n)
{
    unsigned char *d = dest;
    const unsigned char *s = src;
    for (; n; n--) *d++ = *s++;
    return dest;
}

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

это strlcpy из: https://git.musl -libc.org / cgit / musl / tree / src / string / strlcpy.c

size_t strlcpy(char *d, const char *s, size_t n)
{
    char *d0 = d;
    size_t *wd;

    if (!n--) goto finish;
    for (; n && (*d=*s); n--, s++, d++);
    *d = 0;
finish:
    return d-d0 + strlen(s);
}

Хитрость в том, что n && (*d = 0) оценивается как ложное и нарушает условие цикла и рано завершает работу.

Следовательно, это дает вам желаемое поведение.

...