Указатели и массивы

Взаимосвязь между массивами и указателями

Между указателями и массивами существует очень тесная связь. Чтобы её понять, рассмотрим простой пример.

Задача. Найти сумму значений элементов массива, состоящего из 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++. В чём причина?

Дело в том, что память для массива выделяется при компиляции. Начальный адрес массива, количество элементов и как следствие — объём памяти, отводимой под массив, фиксируются на этапе компиляции и продолжают оставаться неизменными в процессе выполнения программы. Для указателя настройка на нужный участок памяти выполняется на этапе исполнения программы, что позволяет перенастраивать указатель по ходу работы программы многократно и на разные участки памяти. Таким образом, указатель ведёт себя как переменная величина, а массив — как константа. Поэтому массив и не является полноценным указателем. Массив — это константный указатель.

Hosted by uCoz
Назад
На верх
Вперёд
Hosted by uCoz