Теперь мы можем более четко определить, что означают понятия “указатель на int”, “указатель на float” и “указатель на любой другой объект данных”.
• Значение указателя — это адрес объекта, на который он указывает. Внутреннее представление адреса зависит от оборудования. Многие компьютеры, включая IBM PC и Macintosh, адресуемы по байтам, т.е. байты памяти нумеруются последовательно. Адресом большого объекта, такого как переменная типа double, как правило, является адрес первого байта объекта.
Массивы и указатели 383
• Применение операции * к указателю дает значение, хранящееся в объекте, на который ссылается указатель.
• Добавление 1 к указателю увеличивает его значение на размер в байтах типа, на который он указывает.
Мастерство языка С позволяет обеспечивать следующие равенства:
*dates + 2 == &date[2] // тот же адрес
*(dates + 2) == dates[2] // то же значение
Такие отношения подводят итог под тесной связью между массивами и указателями. Это значит, что вы можете использовать указатель для идентификации отдельного элемента массива и для получения его значения. По существу мы имеем две разных формы записи для одного и того же действия. В действительности, стандарт языка С описывает массивы в терминах указателей. То есть стандарт определяет ar[n] как * (ar + n). Второе выражение можно интерпретировать как “перейти к ячейке памяти ar, перемеситься на n единиц и извлечь хранящееся там значение”.
Кстати, не путайте *(dates + 2) с *dates + 2. Операция разыменования (*) имеет более высокий приоритет, чем операция +, так что второе выражение означает (*dates)+2:
*(dates + 2) // значение 3-го элемента массива dates
*dates +2 // добавление 2 к значению 1-го элемента

Наличие такой связи между массивами и указателями позволяет применять при написании программы любой из подходов. Например, программа в листинге 10.9 после компиляции и запуска генерирует тот же вывод, что и программа из листинга 10.1.
Здесь days — это адрес первого элемента массива, индекс days + index — адрес элемента days [index] и * (days + index) — значение этого элемента, в точности как days [index]. Цикл по очереди ссылается на каждый элемент массива и выводит обнаруженное содержимое.
Есть ли какое-то преимущество в написании программы подобным образом? Вообще говоря, нет — для любой формы записи компилятор генерирует один и тот же код. Основная цель кода в листинге 10.9 состояла в том, чтобы показать, что формы записи через массивы и через указатели являются эквивалентными. Этот пример демонстрирует возможность использования формы записи с указателями при работе с массивами. Обратное утверждение также верно; при работе с указателями можно применять форму записи с массивами. Это становится важным, когда имеется функция, принимающая массив в качестве аргумента.
384 глава 10
Функции, массивы и указатели
Предположим, что необходимо написать функцию, которая оперирует на массиве. Например, пусть нужна функция, возвращающая сумму элементов массива. Представим, что marbles — это имя массива значений int. Как будет выглядеть вызов такой функции? Разумно предположить, что он должен иметь следующий вид:
total = sum (marbles); // возможный вызов функции
А каким должен быть прототип этой функции? Вспомните, что имя массива является адресом его первого элемента, так что фактический аргумент marbles, будучи адресом значения int, должен присваиваться формальному параметру, который представляет собой указатель на тип int:
int sumlint * ar); // соответствующий прототип
Какую информацию функция sum() получает из этого аргумента? Она получает адрес первого элемента массива и узнает, что в этой ячейке она найдет значение int. Обратите внимание, что данная информация ничего не говорит о количестве элементов в массиве. Мы поставлены перед выбором одного из двух вариантов получения этой информации функцией. Первый вариант предусматривает кодирование внутри функции фиксированного размера массива:
int sumlint * ar) // соответствующее определение
{
int i;
int total = 0;
for ( i = 0; i < 10; i++) // предполагается наличие 10 элементов
total += ar[i]; // ar[i] - то же самое, что и *(ar + i)
return total;
}
Здесь используется тот факт, что аналогично применению указателей с именами массивов, форму записи массивов можно использовать с указателями. Кроме того, вспомните, что операция += добавляет значение своего правого операнда к левому операнду. Следовательно, total является текущей суммой элементов массива.
Читать дальше