Строки как массив элементов типа char
В Cи отсутствует специальный тип строк. Строки рассматриваются как массивы символов, оканчивающиеся нулевым символом ('\0'). Строка доступна через указатель на первый символ в строке. Значением строки является адрес ее первого символа. Таким образом, можно сказать, что в Cи строка является указателем - указателем на первый символ строки. В этом смысле строки подобны массивам, потому что массив тоже является указателем на свой первый элемент (рисунок 8.1).
. . . | |||||
A[4] | \0 | ||||
0x0012FF84 | |||||
A[3] | я | ||||
0x0012FF83 | |||||
A[2] | с | ||||
0x0012FF82 | |||||
A[1] | а | ||||
0x0012FF81 | |||||
A[0] | В | ||||
0x0012FF80 | |||||
![]() | ![]() | ||||
const char *A |
| 0x0012FF70 | |||
. . . |
Рисунок 8.1 – Представление массива элементов в памяти ЭВМ
В приведенном примере объявлен символьный массив A на 5 элементов: char A[]=”Вася”;
Обратите внимание на то, что теперь под хранение каждого символа выделяется один байт памяти, а под указатель будет по-прежнему выделено 4 байта. Необходимо отметить, что в приведенном примере размер массива не указан, он определен автоматически по длине строки-инициализатора, причем, автоматически добавлен символ окончания строки.
Строка может быть объявлена либо как массив символов, либо как переменная типа char*. Каждое из двух приведенных ниже эквивалентных объявлений
char S[] = "строка";
char *Sp = "строка";
присваивает строковой переменной начальное значение "строка". Первое объявление создает массив из 7 элементов S содержащий символы 'с', 'т', 'р', 'о', 'к', 'а' и '\0'. Второе объявление создает переменную указатель Sp, который указывает на строку с текстом "строка", лежащую где-то в памяти. Но в любом случае число хранимых символов на 1 больше числа значащих символов за счет оконечного нулевого символа.
Доступ к отдельным символам строки осуществляется по индексам, начинающимся с нуля. Например, S[0] и Sp[0] - первые символы объявленных выше строк, S[1] и Sp[1] - вторые и т.д.
В приведенных объявлениях длина строк определялась автоматически компилятором. Можно объявлять строковые переменные заданной длины. Например, оператор char buff[100];
объявляет переменную buff, которая может содержать строку до 99 значащих символов плюс заключительный нулевой символ.
Теперь приступим к знакомству с наиболее полезным применением символьных указателей: запоминанию массивов строк. На самом деле нельзя создать массив строк, но можно запомнить массив символьных указателей, каждый из которых содержит адрес строки в памяти.
Определением массива символьных указателей создается динамический массив. Этот массив похож на двумерную таблицу с одним исключением: все строки в этом массиве могут иметь разную длину (разное количество символов в строке).
Все двумерные таблицы, которые рассматривались ранее, выровнены с обеих сторон. Например, объявим символьную таблицу с 5 строками и 10 колонками, каждая строка которой содержит одинаковое число символов. Ее можно определить следующим образом:
char names [ 5 ] [ 10 ] = { { "Сергей" },
{ "Михаил" },
{ "Николай" },
{ "Иван" },
{ "Алексей" }};
Она показана на рисунке 8.2. Обратите внимание, в таблице много пробелов. Каждая строка занимает 10 символов даже в том случае, когда символов в строке намного меньше. Незаполненные элементы содержат символ ограничитель строки, потому что Cи записывает этот символ во все неинициализированные вами элементы массива. Этот тип таблиц занимает слишком много места в памяти.
![]() | ||||||||||||
![]() | С | е | р | г | е | й | \0 | |||||
М | и | х | а | и | л | \0 | ||||||
Строки 2 | Н | и | к | о | л | а | й | \0 | ||||
И | в | а | н | \0 | ||||||||
А | л | е | к | с | е | й | \0 | |||||
Рисунок 8.2 – Пример двумерного символьного массива строк
Для устранения проблемы нерационального использования памяти объявим одномерный массив символьных указателей. Каждый указатель содержит адрес сроки в памяти, и эти строки могут иметь разную длину. Вот примеры определения таких массивов:
char *names [ 5 ] =
{
{ "Сергей" },
{ "Михаил" },
{ "Николай" },
{ "Иван" },
{ "Алексей" }
};
или просто
char *names [ ] =
{
{ "Сергей" },
{ "Михаил" },
{ "Николай" },
{ "Иван" },
{ "Алексей" }
};
Это одномерные массивы. Хотя раньше ничего подобного не рассматривалось, не следует смущаться. Звездочка перед names говорит о том, что это массив указателей. Указатели имеют символьный тип. Строки не присваиваются элементам массива, но в массиве содержатся адреса этих строк. На рисунке 8.3 показан этот массив указателей. Строки могут находиться в любом месте памяти. Их местоположение не существенно, так как каждый указатель содержит адрес первого элемента соответствующей строки. Строки не занимают лишнюю память; каждая строка использует ровно столько пространства, сколько нужно для ее размещения и символа ограничителя строки. Такая структура содержит данные в форме с рваными краями.
names | |||||||||||
[0] | ![]() ![]() ![]() ![]() ![]() | С | е | р | г | е | й | \0 | |||
[1] | М | и | х | а | и | л | \0 | ||||
[2] | Н | и | к | о | л | а | й | \0 | |||
[3] | И | в | а | н | \0 | ||||||
[4] | А | л | е | к | с | е | й | \0 |
Рисунок 8.3 – Массив указателей
Для вывода первой строки можно использовать оператор cout:
cout « *names; // Печать Сергей
Для вывода второй строки используется следующий оператор cout:
cout *(names+1); // Печать Михаил
При обращении к элементу массива при помощи оператора *, получаем доступ к одной из строк массива. Можно использовать возвращаемое этим оператором значение везде, где можно использовать строковую константу или символьный массив.
Очень часто при работе с символьными массивами возникает задача их обработки (сравнение строк, объединение строк, поиск подстроки в строке и так далее). В заголовочном файле <string.h> описано множество функций для работы со строками. Например, функции:
· копирования строк (strcpy, strncpy);
· сравнения строк (strcmp, strncmp);
· объединения строк (strcat, strncat);
· поиска подстроки (strstr);
· поиска вхождения символа (strchr, strrchr, strpbrk);
· определения длины строки (strlen);
· преобразования строки в число (atoi, atoll, atof);
· и так далее.
Учитывая, что строки состоят из символов, полезно знать некоторые функции работы с символами. Для работы с символами служит библиотека ctype (заголовочный файл ctype.h).Она содержит:
1. Функции проверки на принадлежность символа множеству:
· isalmum(c) –букв и цифр (A-Z, a-z, 0-9);
· isalfa(c) –букв (A-Z, a-z);
· iscntrl(c) – управляющих символов (с кодами 0…31 и 127);
· isdigit(c) –цифр (0-9);
· isgraph(c) –печатаемых символов кроме пробела;
· islower(c) – букв нижнего регистра (a-z);
· isprint(c) –печатаемых символов;
· ispunct(c) –знаков пунктуации;
· isspace(c) – символов-разделителей;
· isupper(c) – букв верхнего регистра (A-Z);
· isxdigit(c)-шестнадцатеричных цифр.
2. Функции преобразования символов:
· tolower(c) -к нижнему регистру;
· toupper(c) – к верхнему регистру.
Лабораторная работа № 12
ЦЕЛЬ РАБОТЫ: •получение начальных навыков в объявлении, инициализации, использовании символьных массивов и в обработке строк на С++.
Выполнение работы:освоить теоретический материал, выполнить общее для всех задание I и в соответствии с вариантом составить программу (задание II).
Задание I
Используя