Между указателями и массивами существует очень тесная связь. Чтобы её понять, рассмотрим простой пример.
Задача. Найти сумму значений элементов массива, состоящего из n вещественных чисел.
Решим основную часть задачи (суммирование элементов массива) несколькими способами.
Возможный 1-й вариант решения (приводим полный текст программы):
#include <iostream>
using namespace std;
int main()
{
int n = 5;
double x[n];
double *u; // Указатель пригодится в других вариантах
int i;
double s;
cout << "Input " << n << " numbers:" << endl;
for(i = 0; i < n; i++)
cin >> x[i];
cout << "1 variant:" << endl;
//------------------------------
for(s = 0, i = 0; i < n; i++)
s += x[i];
cout << "s=" << s << endl;
return 0;
}
Этот 1-й вариант — обычное использование массива. Именно так мы решали задачи с массивами до сих пор.
Вариант №2 — используем указатель u для доступа к элементам массива. В заголовке цикла операцией u++ каждый раз переключаем указатель на следующий элемент массива x. Приведём для краткости не весь текст программы, а только цикл с суммированием:
for(s = 0, u = &x[0], i = 0; i < n; i++, u++)
s += *u;
Если изменённый вариант программы запустить на исполнение, то можно убедиться, что всё отлично работает. И ответ получится тот же, что и для 1-го варианта.
Вариант №3 — указатель u остаётся настроенным на начало массива, а переключение на последующие элементы массива делается за счёт изменения переменной цикла i:
for(s = 0, u = &x[0], i = 0; i < n; i++)
s += *(u + i);
Также всё нормально. Ответ совпадает с прежним.
Вариант №4 — а что будет, если написать так:
for(s = 0, i = 0; i < n; i++)
s += *(x + i);
Результат снова получается правильным. Обращение к элементу массива x[i] заменено на разыменование для адресного выражения *(x+i). Почему это работает? Дело в том, что все выражения типа x[i] компилятор рассматривает как *(x+i). Напрашивается вопрос: массив — это указатель? Ответ: да, но с некоторыми ограничениями, которые разберём чуть позже. А пока — немного развлечений.
Вариант №5 — продолжим эксперимент:
for(s = 0, i = 0; i < n; i++)
s += i[x];
Выглядит абсурдно (простая переменная i стала как бы массивом, а массив — как бы индексом), но работает преотлично! Причина — смотри вариант 4. Выражение i[x] для компилятора означает то же, что и *(i+x), которое, естественно, равносильно *(x+i).
Вариант №6 — «усугубим» ситуацию. Изменим начальное значение переменной цикла i=-2, а это изменение компенсируем добавлением числа 2 в индексное выражение, т.е. «нормальное» обращение к элементу массива выглядело бы как x[i+2], у нас же всё гораздо «веселее»:
for(s = 0, i = -2; i < n-2; i++)
s += 2[i+x];
А теперь и целое число «прикидывается» массивом. Причина правильной работы такой ненормальности та же, что и для вариантов 4, 5.
Конечно, в программах, предназначенных для всеобщего обозрения, надо использовать только «правильные» конструкции вида x[i] или *(x+i), чтобы ни кого не ставить в тупик своей оригинальностью. А вот для экспериментов можно (и нужно!) пробовать всё. Это полезно для лучшего понимания языков программирования.
Вариант №7 — в варианте 4 мы убедились, что наш обычный массив x — это указатель, но некоторые сомнения-то остались, не правда ли? Попробуем работать с массивом x так, как работали с указателем u во втором варианте, т.е. применим к x операцию автоувеличения:
for(s = 0, i = 0; i < n; i++,
x++) // Выражение x++ записано отдельной строкой
// специально для того, чтобы убедится, что
// именно это выражение «не нравится»
// компилятору
s += *x;
И только сейчас компилятор фиксирует ошибку и именно для выражения, что показано отдельной строкой, т.е. x++. В чём причина?
Дело в том, что память для массива выделяется при компиляции. Начальный адрес массива, количество элементов и как следствие — объём памяти, отводимой под массив, фиксируются на этапе компиляции и продолжают оставаться неизменными в процессе выполнения программы. Для указателя настройка на нужный участок памяти выполняется на этапе исполнения программы, что позволяет перенастраивать указатель по ходу работы программы многократно и на разные участки памяти. Таким образом, указатель ведёт себя как переменная величина, а массив — как константа. Поэтому массив и не является полноценным указателем. Массив — это константный указатель.