Г. Шилдт - Полный справочник по C++ (1109478), страница 24
Текст из файла (страница 24)
Кроме того, указатели можно вычитать. Это позволяет определить количество обьектов базового типа, расположенных между двумя указателями. Все другис арифмстичс- кие операции запрещены. В частности, указатели нельзя складывать, умножать и делить К ним нельзя применять побитовые операции, суммировать их со значениями переменных, имеющих тип в1оее, аоезз1е и т.п., а также вгачитать такие значения из них. Сравнение указатепей Указатели можно сравнивать между собой. Например, следующий оператор, в котором сравниваются указатели р и ч, является совершенно правильным.
1е(р<ч1 ртйпст("Указатель р содержит меньший адрес, чем Указатель с(тп"); Глава б. Указатели Как правило, указатели сравниваются между собой, котла они ссылаются нь олин и тот же объект, например массив. Рассмотрим в качестве примера пару функций для работы со стеками, которые записывают и извлекают целые числа. Стек — это список, в котором доступ к элементам осушествляется по принципу "первым вошел, последним вышел". Его часто сравнивают со стопкой тарелок на столе — нижняя тарелка будет снята последней. Стеки используются в компиляторах, интерпретаторах, программах обработки электронных таблиц и других системных утилитах. Чтобы создать стек, необходимы две функции: рпа]з(] и рор(]. Функция риви() заносит числа в стек, а функция рор() извлекает их оттуда.
В приведенной ниже программе они управляются функцией та1п(). При вводе числа с клавиатуры программа заталкивает его в стек. Если пользователь ввел число о, значение выталкивается из стека. Программа прекратит свою работу, когда пользователь введет число — ]. Евпсзцс)е <зМ1о.]з> Взпсзцое <зес)11]з.)з> апет) Етвп Ео чозс( рин)з(1пс з) рор(чозс))з впе *Сон, *р1, зеаск(ЯХХЕ]з хпс гьавп(чо1й ( зпе ча1це; Соз = зсасхз /* указатель Соз ссылается на вершину стека */ р1 =.
зеасин /* Инициализация указателя р1 */ до ( рттпсГ("Введите число: "); есапс("Ъз)", ача1це)з 1т(ча1це)=0) рцз]з(ча1це) з е1зе ртхпег("Число на вершине стека равно аЮп", рорю) з ) н]з11е(ча1це(=-1)з теьцтп оз ) чей рця)з(впе 1) ( р1++; 1Г(р1==(еоз~ятаЕ)) ( ртвпет("Стек переполнен.хп")з ехз.е (1]; ) *р1 ) впс рор(чоЫ) ( 1с (р1=.=сон) ртзпет("Стек исчерпан.1п') ехз.с (1); Часть!. Основы языка С++) подмножества С Р1 сееосп *(р1+1)," Как видим, стек реализован в виде массива вевс(е, Сначала указатели рз и сов ссылаются на первый элемент стека. Затем указатель р1 начинает перемещаться по стеку, а переменная сев по-прежнему хранит адрес его вершины.
Это позволяет избежать переполнения стека и обращения к пустому стеку. Функции Ривъ() и рср() можно применять сразу после инициализации стека В каждой из них выполняется проверка, не вышел ли указпель за пределы допустимого диапазона значений. В функции дивЫ) это позволяет предотвратить переполнение (значение указателя р1 не должно превышать адрес еов+вхвв, где переменная вхвв определяет размер стека). В функции рар() эта проверка позволяет избежать обращения к пустому стеку (значение указателя р1 нс лолжно быть меньше указателя сов, ссылающегося на вершину стека).
Обратите внимание на то, по в функции рор()значение, возвращаемое оператором кееикв, необходимо заключить в скобки. Без них оператор примет вид $ хееикп *р1 «1; В этом случае возвращается значение, записанное по адресу р1, увеличенное на еди- ницу, а нс значение, хранящееся в ячейке с адресом р1+1. "'-''4 Указатели и массивы Указатели и массивы тесно связаны между собой. Рассмотрим следующий фрагмент программы, '*' сваг вес(80] ° *Р1' Р1 = ает Здесь указателю Рз присвоен адрес первого элемента массива век.
Чтобы получить доступ к пятому элементу этого массива, следует выполнить один из двух операторов: $ згк(4) или В *(р1+4) Оба оператора вернут значение пятого элемента )иассива век. Напомним, что индексация массивов начинасгся с нуля, поэтому и)щекс пятого элеменга массива век равен 4. Кроме того, пятый элемент массива можно получить, прибавив 4 к указателю р1, который вначале ссылается на первый элемент. (Как известно, имя массива без индекса представляет собой указатель на начало массива, т.е. на его первый элемент.) Этот пример можно обобщить. По сути, в языке С/С++ существуют два способа обращения к элементам массива: индексация и адресная арифметика. Хотя индексация массива нагляднее, адресная арифметика иногда оказывается эффективнее.
Поскольку быстродействие программы относится к одним из ее важнейших свойств, програлгмисты на языке С/С++ широко используют указатели для обращения к элементам массива, В следующем фрагменте программы приведены два варианта функции Риевек(), иллюстрирующей доступ к элементам массива.
В первом варианте используется индексация, а во втором — адресная арифметика. Функция Рвевек() предназначена для посимвольной записи сцюки в стандартный поток вывода. Глава 5. Указатели К:- индекс,сия указателя. "/ Ь'чогц рцгясг(с)заг *я) е (( гоп'ясег ьпс $( дог(Г.--О; я[С); ++С) рчсспаг(я[С1); "* дослуп с помощью указателя. / (' чоьб рвсясг(с)гаг *ь) Ф( илг1е(*я) рпгсцаг(*вь+); ф) Г>ольшинство профессиональных программистов сочтут вторую версию более по пятной и наглядной.
На практике именно этот способ доступа к элементам массива распространен наиболсс широко. Маасиаы указателей Как и всс обычные переменныс, указатели можно помешать в массив. Обьявление массива, состояшсго из 1О целочисленных указателси, выглядит следуюшим образом [] ьпе "х[1О]; Чтобы присвоить адрес целочисленной псрслюннои чаг третьему эдеме(пу массива указателей, нужно выпол((ить оператор а х[2] = ячаг; г[тобы извлечь значение переменной чаг, используя указатель х[2], необходимо его разыменоват(к $ *х[21 Массив указа~елей передастся в функцию как обычно — достаточно указать его имя в качестве параметра. В следуюшем фрагменте на вход функции поступает массив указателей. ~т чо]б цаяр1ау аггау([пе *о[1) (пг (ог(Ь.-О( Сс10; С++) рг(пчг("Ъ(( ", *о[с]); Напомним, что перел(сипая и нс является указателем на целочисленные псременныс. се следует интерпреп(ровать как указатель на массив целочисленных указателей.
( лсловатсльно, необходимо объявить, что параметр и представляет собои массив целочисленных указателей'. Это объявление сделано в заголовке функции. Массивы часто содержат указатели на строки. Попробуем создать функцию, выволяшую ца экран сообшепис об ошибке с заданным номером. чопб яупгах еггог(ьпе пот) ( вгае[с с)1аг *егг[] "Невозможно открыть файг1п", "Оюибка при чтении[о", "Оюибка при записитп", 124 Часть 1. Осводы языка С(.гн( подмножество С "Отказ оборуггованинчп*' ); рсъпег["Ъе", еск[пцзг))г ) Указатели на строки содержатся в массиве екк. Функция ркхпех () вызывается внутри функции вупеая ехкок() и выводит на экран строку с указанным нолгсром.
Например, если параметр пою равен 2, на экране появится сообшснис "Ошибка при записи' Отметим, что аргумент командной строки вхяч также представляет собой массив указателей на символьные переменные (см, главу 3). (~~-'': Косвенная адресация Иногда указатель может ссылаться на другои указатель, который, в свою очередь, содержит адрес обычной переменной. Такая схема называется косвенной адресацией (пш!г[р)е гпдсгссйоп), или указапгегем на указапгсль (ропиег го рогпгсг). Применение косвенной адресации снижает наглядность программы. Концепция косвеннои адресации проиллюстрирована на рис.
5.3. Как видим, обычный указатель хранит адрес объскгл, содержащего требуемое значение. В случае косвенной адресации один указатель ссылае~ся на лругой указатель, а тот, в свогв очередь, солержит адрес объекта, в котором записано нужное значение. Рис. 5.3, Обычная и косвенная адресация Глубина косвенной адресации не ограничена, однако почти всегда лгожно обойтись "указателем на указатель".
Более гролюздкие схемы адресации труднее понять, следовательно, вероятность ошибок при их использовании резко возрастает. Нв путайте косвенную адрвсвцию с такими динамическими структурами двнньж, квк связвнныв списки, в кспюрых используются указатели Эпю принципиально разные понятия.