Массив элементов типа char может быть объявлен следующим образом:
char v[6]; // Массив из шести символов
Таким образом можно объявить указатель на символ:
char* p; // указатель на символ
В объявлении, [] означают массив, тогда как * означает указатель. Индексация массива начинается с 0, таким образом массив v будет иметь шесть элементов, от v[0] до v[5]** . Размер массива должен быть константным выражением. Переменная указателя может содержать адрес объекта соответствующего типа.
char∗ p = &v[3]; // p указывает на четвёртый элемент массива v char x = ∗p; // *p является объектом, на который указывает p
В выражении унарный префикс * означает "содержимое чего-либо", тогда как унарный префикс & означает "адрес чего-либо". Представим результат инициализации графически:
Рассмотрим копирование десяти элементов одного массива в другой:
void copy_fct() { int v1[10] = {0,1,2,3,4,5,6,7,8,9}; int v2[10]; // Массив для копируемых элементов for (auto i=0; i!=10; ++i) // копируем элементы v2[i]=v1[i]; }
Это for выражение может быть прочитано как "Устанавливаем I в 0 и, инкрементируя i, копируем элементы массива до тех пор, пока i не станет равно 10. Инкрементирование означает добавление 1 и выражается оператором ++. C++11 также позволяет использовать синтаксис for для контейнеров, что делает код более простым и чистым.
void print() { int v[] = {0,1,2,3,4,5,6,7,8,9}; for (auto x : v) cout << x << '\n'; // Для каждого x в массиве v for (auto x : {10,21,32,43,54,65}) cout << x << '\n'; }
Первый range-for-statement может быть прочитан как "каждый элемент массива, с первого до последнего, копируется в x и печатается на экране". Заметьте, что мы не указываем границу массива, когда инициализируем его списком. range-for-statement может использоваться для любых последовательностей элементов.
Если вы не хотите копировать значения из массива v в переменную x, то возьмите элементы по ссылке:
void increment() { int v[] = {0,1,2,3,4,5,6,7,8,9}; for (auto& x : v) ++x; }
Объявление с унарным оператором & означает ссылка на. Ссылка похожа на указатель за исключением того, что для доступа к элементам не требуется производить разыменование указателя, то есть использовать унарный префикс *.
T a[n]; // Массив n элементов T T∗ p; // указатель на T T& r; // ссылка на T T f(A); // функция, принимающая аргумент типа A и возвращающая результат типа T
Всегда старайтесь, чтобы указатель указывал на объект, чтобы разыменование было действительным. Когда мы не имеем объекта или нужно представить, что указатель ни на что не указывает, для этого используем nullptr. Только nullptr может использоваться для всех типов указателей.
double∗ pd = nullptr; Link<Record>∗ lst = nullptr; // указатель на Link на Record int x = nullptr; // ошибка: nullptr является указателем, а не целочисленным типом
Таким образом часто требуется проверка, что аргумент действительно является указателем, который содержит реальный объект.
int count_x(char∗ p, char x) // счётчик числа вхождений x в p[] // p является последовательность символов, которые завершаются 0 { if (p==nullptr) return 0; int count = 0; for (; ∗p!=0; ++p) if (∗p==x) ++count; return count; }
Заметьте, что мы можем переместить указатель с помощью оператора ++ , чтобы он указывал на следующий элемент в массиве.
В устаревшем коде вместо nullptr использовались бы 0 или NULL. Однако использование nullptr устраняет потенциальную путаницу между целыми числами (такими как 0 или NULL) и указателями (такими как nullptr ).