УКАЗАТЕЛИ МАССИВОВ

УКАЗАТЕЛИ МАССИВОВ

     Как было сказано в гл. 9, указатели позволяют нам работать с символическими адресами. Поскольку в реализуемых аппаратно командах вычислительной машины интенсивно используются адреса, указатели предоставляют возможность применять адреса примерно так, как это делается в самой машине, и тем самым повышать эффективность программ. В частности, указатели позволяют эффективно организовать работу с массивами. Действительно, как мы могли убедиться, наше обозначение массива представляет собой просто скрытую форму использования указателей.

     Например, имя массива определяет также его первый элемент, т. е. если flizny[] - массив, то

flizny == &flizny[0]

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

/* прибавление к указателю */

main( )

{

int dates[4], *pti, index;

float bills [4], *ptf;

pti = dates; /* присваивает адрес указателю массива */

ptf = bills;

for(index = 0; index < 4; index++)

printf(" указатели + %d:  %10  u  %10u ", index, pti + index, ptf + index);

}

Вот результат:

указатели + 0    56014    56026

указатели + 1     56016   56030

указатели + 2    56018    56034

указатели + 3    56020    56038

     Первая напечатанная строка содержит начальные адреса двух массивов, а следующая строка - результат прибавления единицы к адресу и т. д. Почему так получается?

56014 + 1 = 56016? 56026 + 1 = 56030?

     Не знаете, что сказать? В нашей системе единицей адресации является байт, но тип int использует два байта, а тип float - четыре. Что произойдет, если вы скажете: "прибавить единицу к указателю?" Компилятор языка Си добавит единицу памяти. Для массивов это означает, что мы перейдем к адресу следующего элемента, а не следующего байта. Вот почему мы должны специально оговаривать тип объекта, на который ссылается указатель; одного адреса здесь недостаточно, так как машина должна знать, сколько байтов потребуется для запоминания объекта. (Это справедливо также для указателей на скалярные переменные; иными словами, при помощи операции *pt нельзя получить значение.)

РИС. 12.1. Увеличение указателя массива.

Благодаря тому что компилятор языка Си умеет это делать, мы имеем следующие равенства:

dates + 2 == &dates[2] /* один и тот же адрес */

*(dates + 2) == dates[2] /* одно и то же значение */

     Эти соотношения суммируют тесную связь между массивами и указателями. Они показывают, что можно использовать указатель для определения отдельного элемента массива, а также для получения его значения. По существу мы имеем два различных обозначения для одного и того же. Действительно, компилятор превращает обозначение массива в указатели, поэтому метод указателей более предпочтителен.

     Между прочим, постарайтесь различать выражения *(dates + 2), и *dates + 2. Операция (*) имеет более высокий приоритет, чeм +, поэтому последнее выражение означает

(*dates) + 2:

*(dates + 2) /* значение 3-го элемента массива dates */

*dates +2    /* 2 добавляется к значению 1-го элемента массива */

Связь между массивами и указателями часто позволяет нам применять оба подхода при создании программ. Одним из примеров этого является функция с массивом в качестве аргумента.