Лекция 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>
предоставляют богатый набор функций для работы со строками: от базового копирования и конкатенации до сложного парсинга и преобразования типов. - Глубокое понимание представления строк в памяти помогает избегать ошибок, связанных с переполнением буфера и некорректной работой с указателями.