Это кажется, что должно быть действительно просто, но по некоторым причинам, я не заставляю это работать. У меня есть строка, названная seq, который похож на это:
ala
ile
val
Я хочу взять первые 3 символа и скопировать их в другую строку. Я использую команду:
memcpy(fileName, seq, 3 * sizeof(char));
Это должно сделать fileName = "ala"
, право? Но по некоторым причинам, я добираюсь fileName = "ala9"
. Я в настоящее время работаю вокруг этого, просто говоря fileName[4] = '\0'
, но задавался вопросом, почему я получаю это 9.
Примечание: После изменения seq к
ala
ile
val
ser
и повторно выполняя тот же код, имя файла становится "alaK"
. Не 9 больше, но все еще ошибочный символ.
C использует нулевой терминатор для обозначения конца строки. memcpy не знает, что вы копируете строки (он просто копирует байты), поэтому он не думает ставить его. Обходной путь, который вы используете, на самом деле является правильным ответом.
Edit: wolfPack88 имеет хорошее замечание. Вам действительно нужно изменить filename[3]. Кроме того, в комментариях ниже есть несколько замечаний по поводу strncpy, который тоже стоит изучить.
Неожиданный символ является артефактом неправильного нуль-терминирования fileName
.
В этом случае fileName
должен быть буфером char
длиной не менее 4 (три для трех символов ala и один для завершающего нулевого символа). Для установки нулевого символа можно использовать:
fileName[3] = '\0';
после memcpy
.
Если вы хотите использовать memcpy для копирования строк, вы должны вручную установить символ '\ 0' после последнего символа строки. Если вы не хотите обрабатывать '\ 0' вручную, используйте вместо этого strcpy или strncpy.
Строки в C заканчиваются нулем, что означает, что вам нужен символ нуля в конце строки. Похоже, вам посчастливилось иметь нулевой символ прямо у следующего символа, так что у вас есть только один дополнительный символ мусора, вы могли бы с тем же успехом получить тысячи символов мусора ...
В дополнение к завершению строки нулем,
fileName[3] = '\0';
Вы также можете рассмотреть возможность использования strncpy
вместо memcpy
. Кроме того, sizeof (char)
всегда должен оцениваться как 1, поэтому он является избыточным.
Удачи!
Вам нужно установить
fileName[3] = 0;
Убедитесь, что в fileName достаточно места для байта NUL конца строки.
Вы должны использовать filename[3]='\0';
. Что касается того, почему это необходимо: потому что ничто другое не устанавливает терминатор NUL для строки, поэтому вы должны это сделать.
Edit: конечно, для реального использования вы не используете константу, как я показал выше. Обычно вы используете что-то вроде:
char *substring(char *out, char const *in, size_t len) {
memcpy(out, in, len);
out[len] = '\0';
return out;
}
Обратите внимание, что у вас была практически правильная идея с использованием memcpy
. strncpy
(для наглядного примера) - это не действительно то, что нужно использовать для этой (или почти любой другой) цели. В списке функций стандартной библиотеки, которых следует избегать, strncpy
занимает второе место, уступая только gets
(хотя, справедливости ради, я должен отметить, что strtok
занимает близкое третье место).
Также обратите внимание, что (как и большинство стандартных библиотечных функций) эта не делает никаких попыток проверить передаваемые параметры - например, если вы скажете ей скопировать 99 символов из строки длиной всего 10 символов в буфер длиной всего 5 символов, она все равно попытается скопировать 99 символов, что приведет к неопределенному поведению).
Edit2: Одна из альтернатив - использовать sprintf.
sprintf - ваш друг для извлечения символов из середины одной строки и помещения их в буфер символов с нулевым окончанием.
sprintf(fileName, "%.3s", seq);
или
sprintf(fileName, "%.*s", 3, seq);
или даже
snprintf(fileName, sizeof(fileName), "%.*s", len, seq);
дадут вам то, что вы хотите. Версия *
допускает переменную длину, а snprintf
более безопасна для предотвращения переполнения буфера
В стандартной библиотеке языка Си нет специальной функции для копирования части строки. Правильный способ сделать это - использовать memcpy
(как вы уже сделали) и явно нуль-терминировать результат. Вы забыли завершить результат, поэтому вы видите странные дополнительные символы после скопированной части строки.
Обратите внимание, что memcpy
будет работать только в том случае, если вы заранее знаете длину исходной строки, то есть знаете, что копируемая часть строки полностью лежит внутри исходной строки. Если есть вероятность, что копируемая часть исходной строки содержит завершающий нулевой символ (т.е. исходная строка заканчивается в середине копируемой части), то вам придется либо написать собственную функцию для копирования, либо использовать нестандартную, но широко доступную strlcpy
.
Иногда можно встретить примеры кода, в которых для этой цели пытаются использовать функцию strncpy
. Хотя в некоторых случаях может показаться, что она "работает", нет абсолютно никакого смысла использовать strncpy
, учитывая, что она не предназначена для использования таким образом.
Причина в том, что вы копируете три символьных байта из seq, однако, нет завершающего нулевого символа. Поэтому ваш обходной путь - это не обходной путь, а правильное решение.
C-строки должны быть нуль-терминированными. Если это не так, то "пользователь" строк читает до тех пор, пока не сможет читать дальше, что приводит к неопределенному поведению.
Btw, почему бы не использовать strncpy ?