Строки в языке Си

Строкой (строчкой) в языке Си называется массив типа char. Элементами маассива являются коды соответствующиx символов. Последним элементом массива является 0 — маркер конца строки. Длиной строки называется количество ее букв, не включая маркер конца строки.

Предполагается, что одна буква кодируется одним элементом типа char, то есть кодировка содержит не более 255 букв. Программы на Си могут некорректно работать с современными кодировками типа UTF-8. Многобайтовые кодировки мы не рассматриваем.

Для хранения строки из n символов нужно выделить как минимум n+1 байт памяти.

Наличие маркера конца строки позволяет при передаче строки в функцию не передавать ее длину. Рассмотрим в качестве примера прототип и возможную реализацию функции вычисления длины строки.

int string_length(const char *s) {
    int len = 0;
    while(*s++) {
        len++;
    }
    return len;
}
Во-первых, функция получает только адрес начала строки. Во-вторых, заметим, что если в строке пропущен маркер конца строки, то результат выполнения этой функции не определен, и ее вызов может привести к ошибке сегментирования, если цикл while выйдет за пределы памяти, отведенной операционной системой процессу.

Ввод-вывод строк

Для вывода строк можно использовать функции семейства printf (printf, fprintf, sprintf и т.п.). В строке формата нужно поставить %s.

const char *str = "This is a string";
printf("The string is: '%s'\n", str);

Для ввода строк есть функция

char *fgets(char *s, int size, FILE *stream);
Параметр size определяет максимальный размер строки, включая маркер конца строки. Память для хранения строки должна быть выделена заранее.

Функции семейства scanf считывают их входного потока одно слово.

scanf("%s", str); /* ОПАСНО */
Либо с ограничением длины:
scanf("%12s", str); /* Будет записано не более 13 символов, включая 0. */

Типичные ошибки

При работе со строками часто возникают следующие ошибоки.

Функкции стандартной библиотеки

Стандартная бибилиотека предоставляет набор функций для работы со строками. Для работы с нми нужно подключить заголовочный файл #include <strings.h>.

Пример


/*
** Чтение длинной строки.
**
** Функция считывает из текущей позиции файла f строчку сколь угодно
** большого размера. Символ перевода строки '\n' копируется в выходную
** строчку, если он был во входном фале.
** Память для хранения выходной строки динамически выделяется.
** Вызывающая функция должна освободить память вызовом free.
**
** Возвращаемое значение:
**    адрес строки, если чтение прошло успешно
**    NULL, если не удалось прочитать исходную строчку.
*/
char *read_long_string(FILE *f)
{
    char buf[1024];
    buf[0] = 0;
    sprintf(stderr, "This is a prototype!\n");
    char *s = fgets(buf, 1024, f);
    if(s) {
        int len = strlen(s);
        char *result = malloc(len + 1);
        strcpy(result, s);
        return result;
    }
    return NULL;
}

int main() {
    char *s = read_long_string(stdin);
    // ....
    free(s);

    return 0;
}