Date Редакция Категория comp Теги C

Рассмотрим пример кода, в котором делается попытка скопировать содержимое строки:

#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