Указатели и массивы
В языке C существует сильная взаимосвязь между указателями и массивами ,
настолько сильная, что указатели и массивы действительно следует рассматривать
одновременно. Любую операцию, которую можно выполнить с помощью индексов
массива, можно сделать и с помощью указателей. Вариант с указателями обычно
оказывается более быстрым, но и несколько более трудным для непосредственного
понимания, по крайней мере для начинающего. Описание
определяет массив размера 10,
т.е. набор из 10 последовательных объектов, называемых a[0], a[1], ...,
a[9]. Запись a[i] соответствует элементу массива через i
позиций от начала. Если pa - указатель целого, описанный как
то присваивание
приводит к тому, что
pa указывает на нулевой элемент массива a. Это означает, что
pa содержит адрес элемента a[0]. Теперь присваивание
будет копировать содержимое a[0] в
x.
Если ра указывает на некоторый определенный элемент массива a,
то по определению pa+1 указывает на следующий элемент, и вообще
pa-i указывает на элемент, стоящий на i позиций до элемента,
указываемого pa, а pa+i на элемент, стоящий на i позиций
после. Таким образом, если pa указывает на a[0], то
ссылается на содержимое a[1],
pa+i - адрес a[i], а *(pa+i) - содержимое a[i].
Эти замечания справедливы независимо от типа переменных в массиве a.
Суть определения "добавления 1 к указателю", а также его распространения на всю
арифметику указателей, состоит в том, что приращение масштабируется размером
памяти, занимаемой объектом, на который указывает указатель. Таким образом,
i в pa+i перед прибавлением умножается на размер объектов, на
которые указывает pa.
Очевидно существует очень тесное соответствие между индексацией и арифметикой
указателей. В действительности компилятор преобразует ссылку на массив в
указатель на начало массива. В результате этого имя массива является
указательным выражением. Отсюда вытекает несколько весьма полезных следствий.
Так как имя массива является синонимом местоположения его нулевого элемента, то
присваивание
можно записать как pa =
a.
Еще более удивительным, по крайней мере на первый взгляд, кажется тот факт,
что ссылку на a[i] можно записать в виде *(a+i). При
анализировании выражения a[i] в языке C оно немедленно преобразуется к
виду *(a+i); эти две формы совершенно эквивалентны. Если применить
операцию & к обеим частям такого соотношения эквивалентности, то мы
получим, что &a[i] и a+i тоже идентичны: a+i - адрес
i-го элемента от начала a. С другой стороны, если pa
является указателем, то в выражениях его можно использовать с индексом:
pa[i] идентично *(pa+i). Короче, любое выражение, включающее
массивы и индексы, может быть записано через указатели и смещения и наоборот,
причем даже в одном и том же утверждении.
Имеется одно различие между именем массива и указателем, которое необходимо
иметь в виду. Указатель является переменной, так что операции pa=a и
pa++ имеют смысл. Но имя массива является константой, а не переменной:
конструкции типа a=pa или a++,или p=&a будут
незаконными.
Когда имя массива передается функции, то на самом деле ей передается
местоположение начала этого массива. Внутри вызванной функции такой аргумент
является точно такой же переменной, как и любая другая, так что имя массива в
качестве аргумента действительно является указателем, т.е. переменной,
содержащей адрес.
/* показывает на экран массив m */
void ShowElements(int *m, int size)
{
int n;
for (n = 0; n < size; m++,n++)
cout<<*m<<"\t";
}
|
Операция увеличения m совершенно законна,
поскольку эта переменная является указателем, m++ никак не влияет на
массив в обратившейся к ShowElements функции, а только увеличивает
локальную для функции ShowElements копию адреса.
Описания формальных параметров в определении функции в виде
и
совершенно эквивалентны; какой вид описания
следует предпочесть, определяется в значительной степени тем, какие выражения
будут использованы при написании функции. Если функции передается имя массива,
то в зависимости от того, что удобнее, можно полагать, что функция оперирует
либо с массивом, либо с указателем, и действовать далее соответвующим образом.
Можно даже использовать оба вида операций, если это кажется уместным и ясным.