Выбрать главу

Действительно, операторы

int years[ ];

int *years;

синонимы. Оба они объявляют переменную years указателем массива целых чисел. Однако главное их отличие состоит в том, что первый из них напоминает нам, что указатель years ссылается на массив.

     Как теперь связать его с массивом ages? Вспомним, что при использовании указателя в качестве аргумента, функция взаимодействует с соответствующей переменной в вызывающей программе, т. е. операторы, использующие указатель years в функции соnvert(), фактически работают с массивом ages, находящимся в теле функции main().

     Посмотрим, как работает этот механизм. Во-первых, вызов функции инициализирует указатель years, ссылаясь на ages[0]. Теперь предположим, что где-то внутри функции convert( ) есть выражение years[3]. Как вы видели в предыдущем разделе, оно аналогично *(years + 3). Однако если years указывает на ages[0], то years+3 ссылается на ages[3]. Это приводит к тому, что *(years+3) означает ages[3]. Если внимательно проследить данную цепочку, то мы увидим, что years[3] аналогично *(years + 3), которое в свою очередь совпадает с ages[3]. Что и требовалось доказать, т. е. операции над указателем years приводят к тем же результатам, что и операции над массивом ages.

Короче говоря, когда имя массива применяется в качестве аргумента, функции передается указатель. Затем функция использует этот указатель для выполнения изменений в исходном массиве, принадлежащем программе, вызвавшей функцию. Рассмотрим пример.

ИСПОЛЬЗОВАНИЕ УКАЗАТЕЛЕЙ ПРИ РАБОТЕ С МАССИВАМИ

     Попробуем написать функцию, использующую массивы, а затем перепишем ее, применяя указатели.

     Рассмотрим простую функцию, которая находит (или пытается найти) среднее значение массива целых чисел. На входе функции мы имеем имя массива и количество элементов. На выходе получаем среднее значение, которое передастся при помощи оператора return. Оператор вызова функции может выглядеть следующим образом:

printf("Среднее из заданных значений %d.\n", mean(numbs,size));

/* находит среднее значение массива из n целых чисел */

int mean(array, n);

int array[ ], n;

{

int index;

long sum; /* Если целых слишком много, их можно

            суммировать в формате long int */

if(n > 0)

{

for(index = 0, sum = 0; index < n; index++)

    sum + = array[index];

return((int)(sum/n)); /* возвращает int * / }

else {

printf("Нет массива. \n");

return(0); }

}

     Эту программу легко переделать, применяя указатели. Объявим ра указателем на тип int. Затем заменим элемент массива array[index] на соответствующее значение: *(ра + index).

/* Использование указателей для нахождения

        среднего значения массива n целых чисел */

int mean(pa, n) int oра, n;

{

int index;

long sum; /*Если целых слишком много,

    их можно суммировать в формате long int */

if(n > 0)

{

for(index=0, sum=0; index < n; index++)

sum + = *(pa + index);

return((int)(sum/n)); /* Возвращает целое */ }

else {

printf("Нет массива.\n");

return(0); }

}

Это оказалось несложным, но возникает вопрос: должны ли мы изменить при этом вызов функции, в частности numbs, который был именем массива в операторе mean(numbs, size)? Ничего не нужно менять, поскольку имя массива является указателем. Как мы уже говорили в предыдущем разделе, операторы описания:

int ра[ ];

и

int *ра;

идентичны по действию: оба объявляют ра указателем. В программе можно применять любой из них, хотя до сих пор мы использовали второй в виде *(ра + index).

     Понятно ли вам, как работать с указателями? Указатель устанавливается на первый элемент массива, и значение, находящееся там, добавляется в sum. Затем указатель передвигается на следующий элемент (к указателю прибавляется единица), и значение, находящееся в нем, также прибавляется к sum и т. д. Это похоже на механизм работы с массивом, где индекс действует как стрелка часов, показывающая по очереди на каждый элемент массива.

Теперь у нас есть два подхода; какой же из них выбрать? Во-первых, хотя массивы и указатели тесно связаны, у них есть отличия. Указатели являются более общим и широко применяемым средством, однако многие пользователи (по крайней мере начинающие) считают, что массивы более привычны к понятны. Во-вторых, при использовании указателей у нас нет простого эквивалента для задания размера массива. Самую типичную ситуацию, в которой можно применять указатель, мы уже показали: это функция, работающая с массивом, который находится где-то в другой части программы. Мы предлагаем использовав любой из подходов по вашему желанию. Однако несомненное преимущество использования указателей в приведенном выше примере должно научить вас легко применять их, когда в этом возникает необходимость.

ОПЕРАЦИИ С УКАЗАТЕЛЯМИ 

    Что же мы теперь умеем делать с указателями? Язык Си предлагает пять основных операций, которые можно применять к указателям, а нижеследующая программа демонстрирует эти возможности. Чтобы показать результаты каждой операции, мы будем печатать значение указателя (являющегося адресом, на который ссылается указатель), значение, находящееся по этому адресу, и адрес самого указателя.

/* операции с указателями */

#define PR(X)

printf("X = %u,*X = %d, &X = %u\n",X, *X,&X);

/* печатает значение указателя (адрес),

            значение, находящееся по */

/* этому адресу, и адрес самого указателя */

main( )

static int urn[ ] = [100, 200, 300];

int *ptrl, *ptr2;

{

ptrl = urn; /* присваивает адрес указателю */

ptr2 = &urn [2]; /* то же самое */

PR(ptrl); /* см. макроопределение, указанное выше */

ptrl++; /* увеличение указателя */

PR(ptrl);

PR(ptr2);

++рtr2; /* выходит за конец массива */

PR(ptr2);

printf("ptr2 - ptrl = %u\n", ptr2 - ptrl);