Перейти к содержанию

Лекция 16: Строки в языке Си

"Я человек весьма терпимый, могу выслушать и чужие мнения."
— Ярослав Гашек


Введение

Лекция посвящена тому, как в языке Си реализуются строки – последовательности символов, представленные в виде массива типа char с завершающим нулём. Рассмотрены способы объявления строк, работа с указателями, стандартные функции для ввода, вывода и обработки строк, а также нюансы, связанные с кодировками символов.


Понятие строки в Си

  • Строка – это массив символов, где последний элемент всегда – терминальный символ '\0'.
  • В языке Си отсутствует специальный тип «строка»: вместо него используются обычные массивы типа char.
  • Благодаря нулевому символу функции вывода (например, printf с форматом %s) знают, где заканчивается строка.

Объявление и инициализация строк

Объявление строк как массивов символов

  • Построчное задание:
    #include <stdio.h>
    void main() {
        char word[10];
        word[0] = 'A';
        word[1] = 'B';
        word[2] = 'C';
        word[3] = '\0';  // Завершающий ноль
        printf("%s", word);
        getch();
    }
    
  • Инициализация строковой константой:
    #include <stdio.h>
    void main() {
        char word[10] = "ABC";
        char text[100] = {'H', 'E', 'L', 'L', 'O'}; // Остальные элементы автоматически инициализируются нулями
        printf("%s\n", word);
        printf("%s", text);
        getch();
    }
    

При инициализации строковой константой компилятор сам определяет количество символов и добавляет завершающий символ '\0'.


Кодировка символов и строк

  • Кодировка ASCII:
    – Представляет символы в диапазоне от 0 до 127.
    – Первые 33 кода – управляющие и непечатные символы (например, '\n', '\t').
  • Расширенная ASCII и Unicode:
    – Для национальных символов (например, кириллицы) используются кодовые страницы (CP-1251, KOI-8 и др.) или таблица Unicode, где первые 128 символов совпадают с ASCII.
  • В памяти строка хранится как последовательность чисел, каждое из которых соответствует коду символа.

Строковые литералы и указатели

  • Строковый литерал – это последовательность символов, заключённая в двойные кавычки, которая хранится в статической (read-only) памяти.
  • При использовании строкового литерала имя переменной может быть указателем на константный массив символов:
    char *m3 = "\nСимвольная строка.";
    
  • Различие между массивом и указателем:
    • Массив (например, char heart[] = "Я люблю язык Cи!";) – имя является адресом первого элемента, но изменить его нельзя.
    • Указатель (например, char *head = "Я люблю язык Pascal!";) можно переназначать, меняя адрес, на который он указывает.

Пример работы с указателями:

for (int i = 0; i < 7; i++)
    putchar(*(head + i));
Здесь с помощью арифметики указателей выводятся отдельные символы строки.


Основные группы операций со строками в C

Для работы со строками в языке C выделяют три основные группы операций. Ниже приведены таблицы с пояснениями для каждой группы, а также примеры кода.

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

Эти функции используются для получения строковых данных из внешних источников (например, клавиатуры или файла) и вывода их на экран или в файл.

Функция Пояснение Пример использования
printf() Форматированный вывод строки (и других типов) в стандартный поток вывода. Использует спецификатор %s для вывода строк. printf("Строка: %s\n", str);
scanf() Форматированный ввод данных из стандартного потока ввода. При использовании %s считывает строку до первого разделителя (пробела, табуляции, или перевода строки). scanf("%19s", buffer);
fgets() Считывает строку из заданного потока (например, stdin) с учётом ограничения по количеству символов. Безопаснее, чем gets(), так как предотвращает переполнение буфера. fgets(buffer, sizeof(buffer), stdin);
puts() Выводит строку в стандартный поток вывода и автоматически добавляет символ перевода строки в конце. puts("Hello, world!");
fgetc() Считывает один символ из указанного потока. Это полезно для посимвольного ввода или при обработке файла символ за символом. int ch = fgetc(stdin);
fputc() Выводит один символ в заданный поток. fputc('A', stdout);

Пример кода:

#include <stdio.h>

int main(void) {
    char buffer[80];

    // Ввод строки с использованием fgets (безопаснее, чем gets)
    printf("Введите строку: ");
    fgets(buffer, sizeof(buffer), stdin);

    // Вывод строки на экран с помощью printf и puts
    printf("Вы ввели: %s", buffer);
    puts(buffer);

    return 0;
}

Преобразование строк

Функции этой группы позволяют преобразовывать строковые представления чисел в числовые значения и обратно, а также форматировать строку по заданному шаблону.

Функция Пояснение Пример использования
atoi() Преобразует строку, содержащую число, в значение типа int. Возвращает 0, если строка не начинается с корректного числового представления. int num = atoi("12345");
atof() Преобразует строку в число с плавающей точкой (тип double). double d = atof("3.14");
strtol() Преобразует строку в число типа long, позволяя задать систему счисления (например, 10 для десятичной, 16 для шестнадцатеричной). Также возвращает указатель на первый символ, который не удалось преобразовать. long n = strtol("1010", NULL, 2); // n будет 10 (из двоичного)
sprintf() Форматирует строку согласно заданному формату и записывает результат в указанный буфер. sprintf(buffer, "Число: %d", num);
snprintf() Аналогична sprintf(), но позволяет задать максимальное количество символов для записи в буфер, что помогает предотвратить переполнение. snprintf(buffer, sizeof(buffer), "Число: %d", num);

Пример кода:

#include <stdio.h>
#include <stdlib.h>

int main(void) {
    char numStr[] = "12345";
    int num = atoi(numStr);  // Преобразование строки в int
    printf("Преобразованное число: %d\n", num);

    double d = atof("3.14"); // Преобразование строки в double
    printf("Преобразованное число с плавающей точкой: %.2f\n", d);

    // Преобразование числа в строку
    char buffer[20];
    sprintf(buffer, "Число: %d", num);
    printf("Форматированная строка: %s\n", buffer);

    return 0;
}

Обработка строк

Эти функции используются для манипуляций со строками: вычисления их длины, копирования, сравнения, объединения, а также поиска символов и подстрок.

Функция Пояснение Пример использования
strlen() Вычисляет длину строки (количество символов до первого '\0'). size_t len = strlen("Hello"); // len будет 5
strcpy() Копирует строку из источника в целевой массив, включая завершающий символ '\0'. strcpy(dest, src);
strncpy() Копирует указанное количество символов из строки-источника в целевой массив. Может не добавить завершающий ноль, если количество копируемых символов равно указанному лимиту. strncpy(dest, src, 5);
strcmp() Сравнивает две строки лексикографически. Возвращает 0, если строки равны, отрицательное число, если первая строка меньше второй, и положительное число, если первая больше второй. int cmp = strcmp("abc", "abd");
strcat() Объединяет (конкатенирует) две строки, добавляя содержимое второй строки в конец первой. strcat(dest, src);
strncat() Объединяет указанное количество символов из второй строки с первой. strncat(dest, src, 3);
strchr() Ищет первое вхождение заданного символа в строке и возвращает указатель на него; если символ не найден, возвращает NULL. char *p = strchr("Hello", 'e');
strstr() Ищет первое вхождение подстроки в строке. Возвращает указатель на начало найденной подстроки или NULL, если подстрока не найдена. char *p = strstr("Hello world", "world");

Пример кода:

#include <stdio.h>
#include <string.h>

int main(void) {
    char src[] = "Hello";
    char dest[30];

    // Копирование строки
    strcpy(dest, src);
    printf("После strcpy: %s\n", dest);

    // Объединение (конкатенация) строк
    strcat(dest, " World");
    printf("После strcat: %s\n", dest);

    // Вычисление длины строки
    size_t len = strlen(dest);
    printf("Длина строки: %zu\n", len);

    // Поиск символа
    char *p = strchr(dest, 'W');
    if (p != NULL)
        printf("Символ 'W' найден по адресу: %p\n", p);

    // Поиск подстроки
    p = strstr(dest, "World");
    if (p != NULL)
        printf("Подстрока \"World\" найдена по адресу: %p\n", p);

    return 0;
}

Ввод и вывод строк (повтор)

Функции ввода и вывода часто используются вместе с форматированным вводом/выводом, что позволяет работать как с отдельными символами, так и со строками целиком.

  • scanf() считывает строку до первого разделителя (пробела, табуляции, или перевода строки).
  • gets() (не рекомендуется) считывает строку до символа новой строки, но не проверяет длину буфера.
  • fgets() – безопасный аналог, позволяющий задать максимальное количество считываемых символов.
  • printf() и puts() используются для вывода строк.

Преобразование строк и чисел

Функции преобразования позволяют получать числовое значение из строки и наоборот:

  • atoi(), atof() – простые функции для преобразования строк в числа.
  • strtol() и подобные – более гибкие функции с поддержкой систем счисления.
  • sprintf() и snprintf() – форматируют число в строку с заданным шаблоном.

Массивы символьных строк

Строки можно хранить как в виде двумерного массива символов, так и в виде массива указателей:

  • Двумерный массив:
    char days[12][10] = {
        "Январь", "Февраль", "Март", "Апрель", "Май", "Июнь",
        "Июль", "Август", "Сентябрь", "Октябрь", "Ноябрь", "Декабрь"
    };
    
  • Массив указателей:
    char *poet[4] = {
        "Погиб поэт!", "- невольник чести -", "Пал,", "оклеветанный молвой…"
    };
    

Заключение

  • Строки в Си – это массивы символов с завершающим нулём, что позволяет обрабатывать их с помощью стандартных функций.
  • Важно помнить о безопасном вводе строк, корректном выделении памяти и различиях между массивами и указателями на строки.
  • Стандартные библиотеки <stdio.h>, <string.h> и <stdlib.h> предоставляют богатый набор функций для работы со строками: от базового копирования и конкатенации до сложного парсинга и преобразования типов.
  • Глубокое понимание представления строк в памяти помогает избегать ошибок, связанных с переполнением буфера и некорректной работой с указателями.

Бегут в спортзалах по кругу