Массивы и указатели 389
Что касается языка С, то два выражения ar [1] и * (ar+ i ) по смыслу эквивалентны. Оба работают, если ar является именем массива, и оба работают, если ar — это переменная типа указателя. Тем не менее, выражение наподобие ar++ работает только в тех случаях, когда ar представляет собой переменную типа указателя.
Запись с применением указателей, особенно когда она сопровождается операцией инкремента, ближе к машинному языку, и некоторые компиляторы обеспечивают в таком случае более эффективный код. Однако многие программисты придерживаются мнения о том, что их основная задача заключается в обеспечении корректности и ясности кода, а его оптимизация должна быть оставлена компилятору.
Операции с указателями
Что же разрешено делать с указателями? Язык С предлагает множество базовых операций, которые можно выполнять над указателями, и в следующей программе демонстрируются восемь из имеющихся возможностей. Чтобы показать результаты каждой операции, программа выводит значение указателя (адрес, на который он указывает), значение, хранящееся по указанному адресу, и адрес самого указателя. (Если ваш компилятор не поддерживает спецификатор %р, попробуйте воспользоваться для вывода адресов спецификатором %u или, возможно, %lu. Если компилятор не поддерживает спецификатор %td, предназначенный для вывода разности адресов, попробуйте применить %d или, возможно, %ld.)
В листинге 10.13 представлены восемь базовых операций, которые можно выполнять над переменными типа указателя. В дополнение к этим операциям можно использовать операции отношений для сравнения указателей.
Листинг 10.13. Программа ptr ops.с

390 глава 10

Ниже показаны результаты выполнения этой программы в одной из систем:
значение указателя, разыменованный указатель, адрес указателя: ptr1 = 0x7fff5fbff8d0, *ptг1 =100, &ptrl = 0x7fff5fbff8с8
сложение значения int с указателем:
ptr1 + 4 = 0x7fff5fbff8e0, *(ptr4 + 3) = 400
значения после выполнения операции ptrl++:
ptr 1 = 0x7fff5fbff8d4, *ptrl =200, &ptrl = 0x7fff5fbff8c8
значения после выполнения операции —ptr2:
ptr2 = 0x7fff5fbff8d4, *ptr2 = 200, &ptr2 = 0x7fff5fbff8c0
восстановление исходных значений указателей: ptr 1 = 0x7fff5fbff8d0, ptr2 = 0x7fff5fbff8d8
вычитание одного указателя из другого:
ptr2 = 0x7fff5fbff8d8, ptrl = 0x7fff5fbff8d0, ptr2 - ptrl = 2
вычитание из указателя значения типа int: ptr3 = 0x7fff5fbff8е0, ptr3 - 2 = 0x7fff5fbff8d8
Ниже описаны базовые операции, которые можно выполнять с переменными типа указателей.
• Присваивание. Указателю можно присвоить адрес. Присваиваемым значением может быть, например, имя массива, переменная, которой предшествует операция взятия адреса (&), или другой указатель. В листинге 10.13 переменной ptrl присваивается адрес начала массива urn. Этим адресом оказался номер ячейки памяти 0x7f ff5fbf f8d0. Переменная ptr2 получает адрес третьего (последнего) элемента, urn [2]. Обратите внимание, что адрес должен быть совместим с типом указателя. Другими словами, вы не можете присваивать адрес значения double указателю на int, во всяком случае, не делая неосмотрительное приведение типа. Это правило требуют стандарты С99/С11.
• Нахождение значения (разыменование). Операция * дает значение, хранящееся в ячейке, на которую указывает указатель. Таким образом, первоначально *ptrl равно 100, т.е. значению, хранящемуся в ячейке 0x7fff5fbff8d0.
• Взятие адреса указателя. Подобно всем переменным, переменная типа указателя имеет адрес и значение. Операция & сообщает, где хранится сам указатель. В рассмотренном примерю ptrl хранится в ячейке 0x7fff5fbff 8с8. Содержимым этой ячейки памяти является 0x7fff5fbff8d0, т.е. адрес массива urn. В итоге &ptl - указатель на ptl, что, в свою очередь, представляет собой указатель на urn[0].
Массивы и указатели 391
• Добавление целого числа к указателю. С помощью операции + можно добавить целое число к указателю или указатель к целому числу. В любом случае целое число умножается на количество байтов в типе данных, на который указывает указатель, и результат добавляется к исходному адресу. Это делает выражение ptrl + 4 эквивалентным &urn[4]. Результат сложения не определен, если он находится за пределами массива, на который указывает исходный указатель; исключением будет адрес, следующий за последним элементом массива, который считается допустимым.
Читать дальше