В C++ имя массива является константным указателем на его первый элемент. То есть:
int mas[20]; int *pmas; pmas = &mas[0]; // в pmas – адрес первого элемента массива
pmas = mas; // то же самое, что в предыдущей строке
Последние 2 строки в примере равнозначны и помещают в pmas адрес первого элемента массива. После этого указатель pmas можно использовать для перебора элементов массива с использованием адресной арифметики.
В C++ любое выражение вида указатель[индекс] по определению трактуется как *(указатель + индекс). Это позволяет работать с элементами массива через указатель на него следующим образом:
*pmas; // обращение к mas[0]
*(pmas + 1); // обращение к mas[1]
pmas++; // pmas теперь ссылается на 2й элемент массива – mas[1]
*pmas; // обращение к mas[1]
pmas += 5; // pmas теперь ссылается на 7й элемент массива – mas[6]
*pmas; // обращение к mas[6]
Указатели, ссылающиеся на один массив, можно сравнивать, используя операции ==, <, >. Например, выражение p < qистинно, если p указывает на более ранний элемент массива, чем q. Любой указатель также можно сравнить на равенство или неравенство с так называемым нулевым указателем NULL, который ни на что не указывает. Не рекомендуется сравнивать указатели, ссылающиеся на различные массивы.
Практикуется и вычитание указателей, ссылающихся на один массив. Если p и q указывают на элементы одного и того же массива, то p–qдает количество элементов массива между pи q. Например:
int mas[20];
int *p1 = mas;
int *p2 = &mas[4];
cout << p2 - p1; // выведет 4
Строки
Традиционно строка в C++ представляется в виде массива символов, завершающегося нулем (нулевым байтом '\0'). Такая строка также называется null-terminated string. Нулевой байт является признаком конца строки и если он отсутствует, то длина строки будет определена неверно.
Чаще всего для представления строк достаточно элементов типа char, размером обычно в 1 байт. Но для некоторых языков используются многобайтные наборы символов. В С/С++ для этого существует специальный тип wchar_t и специальные функции для работы со строками, состоящими из таких символов. Размер wchar_t не фиксирован в стандарте и определяется реализацией компилятора. На многих платформах и компиляторах это два байта, соответствующих кодировке Unicode.
Другой способ представления строк – с помощью класса string, объявленного в одноименном заголовочном файле. Этот класс в полном соответствии с идеологией объектно-ориентированного программирования предоставляет дополнительные методы, облегчающие работу со строками. Но в его основе также лежит традиционное представление строки в виде массива символов. Рассматривать класс string мы не будем, т.к. зная принципы работы со строками-массивами и принципы ООП, он осваивается довольно быстро.
Наиболее простой способ объявить строку – задать символьный массив нужной длины + 1 байт для завершающего нуля. При объявлении строки-константы размер массива можно не указывать. Если же длина строки заранее неизвестна, размер массива выбирают «с запасом».
char name[20]; // строка из 19 символов (с учетом концевого нуля - из 20)
char constStr[] = "Строка-константа";
char str[256]; // размер взят с запасом
char empty[] = ""; //пустая строка
char* dstr; // строка - динамический массив. Нужно выделить память // перед использованием!
const char* constStr1 = "Строка-константа";
cout << "Введите строку\n";
cin >> str;
cout << "Ваша строка:\n" << str << '\n';
При объявлении строковых литералов, т.е. наборов символов, заключенных в двойные кавычки, вставлять символ окончания строки не обязательно. Компилятор делает это автоматически. В приведённом примере использованы 5 таких литералов, причем один из них – пустая строка в виде "". Такая строка содержит только один элемент – нулевой байт.
Как видно из примера, строки также можно объявлять в виде указателей на тип char. Так как в C++ массивы и указатели очень тесно связаны, а имя массива является указателем на его первый элемент, то строки очень часто объявляются в виде типа данных char*. Нужно понимать, что строка dstr из примера не готова к использованию, поскольку для нее не выделена память. Выделение памяти и динамические массивы будут рассмотрены позднее.
Из примера можно также увидеть простоту считывания строк с клавиатуры и вывода их на экран. Несмотря на то, что str является массивом, потоки cin и cout могут обрабатывать его целиком, а не поэлементно, как это происходит с обычными массивами.