Рассмотрим пример кода, в котором делается попытка скопировать содержимое строки:
#include<stdio.h>
#include<string.h>
int main()
{
char str[] = "Hello";
printf("%s\n", str);
str = "world";
printf("%s\n", str);
return 0;
}
Однако, вместо ожидаемого результата
Hello
world
компилятор выдает в строке 8 ошибку
error: incompatible types when assigning to type ‘char[]’ from type ‘char *’
Что же здесь происходит?
Прежде всего вспомним, что в языке С имя массива является указателем на его нулевой элемент. Указатель этот постоянен, то есть адрес начала массива остается неизменным. Итак, в левой части равенства находится константный указатель.
В правой части равенства стоит строковый литерал, который является указателем на начало последовательности символов, заканчивающейся символом '\0'
. Таким образом, в строке 8 мы пытаемся присвоить указатель константному указателю, что сродни попытке выполнить присваивание 6 = 4
.
Нам же вместо этого нужно записать данные, последовательность которых начинается с указанного в литерале адреса, в ячейки, первая из которых находится по адресу, указанному в имени массива. Выполняется эта операция функцией strcpy
, которая выглядит примерно так:
char *strcpy(char * const t, const char *const s)
{
char *dst = t;
const char *src = s;
while ((*dst++ = *src++) != '\0')
;
return t;
}
Теперь заменим строку 8 следующей строкой
strcpy(str,"world");
и получим, наконец, то, что ожидалось.
При использовании strcpy
следует соблюдать осторожность. Опасность заключается в том, что копируемая строка (в нашем примере – литерал) может оказаться больше, чем выделенная для ее размещения память (чем размер str[]
). Функция strcpy
такой случай не анализирует и продолжит копирование строки в невыделенную память. Последствия, разумеется, будут неприятными. Поэтому strcpy
не рекомендуется использовать для работы с данными, размер которых не известен заранее.
Снизить риск возникновения подобных проблем поможет функция
char* strncpy(char* dest, const char* src, size_t count)
Последний параметр – максимальное количество копируемых символов. Указывая в нем размер строки-приемника, вы гарантируете, что функция strncpy
не выйдет за пределы выделенной памяти. Следует иметь в виду, что если исходная строка будет скопирована не полностью, то ограничивающий '\0'
в результирующей строке не появится и его придется записать самостоятельно.
Кроме функций strcpy
и strncpy
, являющихся частью стандартной библиотеки C, копировать строки можно с помощью функции strdup
, входящей в стандарт POSIX:
char *strdup(const char *s);
strdup
не только копирует содержимое строки, но и выделяет в памяти место для нее, то есть работает как
t = malloc(strlen(s)+1);
strcpy(t, s);
Нужно только помнить, что функция strdup
содержит скрытый malloc
и освобождать зарезервированную ею память с помощью free
.
Комментарии
comments powered by Disqus