Заглавная страница Избранные статьи Случайная статья Познавательные статьи Новые добавления Обратная связь FAQ Написать работу КАТЕГОРИИ: ТОП 10 на сайте Приготовление дезинфицирующих растворов различной концентрацииТехника нижней прямой подачи мяча. Франко-прусская война (причины и последствия) Организация работы процедурного кабинета Смысловое и механическое запоминание, их место и роль в усвоении знаний Коммуникативные барьеры и пути их преодоления Обработка изделий медицинского назначения многократного применения Образцы текста публицистического стиля Четыре типа изменения баланса Задачи с ответами для Всероссийской олимпиады по праву
Мы поможем в написании ваших работ! ЗНАЕТЕ ЛИ ВЫ?
Влияние общества на человека
Приготовление дезинфицирующих растворов различной концентрации Практические работы по географии для 6 класса Организация работы процедурного кабинета Изменения в неживой природе осенью Уборка процедурного кабинета Сольфеджио. Все правила по сольфеджио Балочные системы. Определение реакций опор и моментов защемления |
Указатели и аргументы функцийСодержание книги
Поиск на нашем сайте Указатели и массивы В языке "C" существует сильная взаимосвязь между указа-телями и массивами, настолько сильная, что указатели и мас-сивы действительно следует рассматривать одновременно. Любуюоперацию, которую можно выполнить с помощью индексов масси-ва, можно сделать и с помощью указателей. вариант с указате-лями обычно оказывается более быстрым, но и несколько болеетрудным для непосредственного понимания, по крайней мере дляначинающего. описание INT A[10] определяет массив размера 10, т.е. Набор из 10 последова-тельных объектов, называемых A[0], A[1],..., A[9]. ЗаписьA[I] соответствует элементу массива через I позиций от нача-ла. Если PA - указатель целого, описанный как INT *PA то присваивание PA = &A[0] приводит к тому, что PA указывает на нулевой элемент массиваA; это означает, что PA содержит адрес элемента A[0]. Теперьприсваивание X = *PA будет копировать содержимое A[0] в X. Если PA указывает на некоторый определенный элемент мас-сива A, то по определению PA+1 указывает на следующий эле-мент, и вообще PA-I указывает на элемент, стоящий на I пози-ций до элемента, указываемого PA, а PA+I на элемент, стоящийна I позиций после. Таким образом, если PA указывает наA[0], то *(PA+1) ссылается на содержимое A[1], PA+I - адрес A[I], а *(PA+I) -содержимое A[I]. Эти замечания справедливы независимо от типа переменныхв массиве A. Суть определения "добавления 1 к указателю", атакже его распространения на всю арифметику указателей, сос-тоит в том, что приращение масштабируется размером памяти,занимаемой объектом, на который указывает указатель. Такимобразом, I в PA+I перед прибавлением умножается на размеробъектов, на которые указывает PA. Очевидно существует очень тесное соответствие между ин-дексацией и арифметикой указателей. в действительности ком-пилятор преобразует ссылку на массив в указатель на началомассива. В результате этого имя массива является указатель-ным выражением. Отсюда вытекает несколько весьма полезныхследствий. Так как имя массива является синонимом местополо-жения его нулевого элемента, то присваивание PA=&A[0] можнозаписать как PA = A Еще более удивительным, по крайней мере на первый взг-ляд, кажется тот факт, что ссылку на A[I] можно записать ввиде *(A+I). При анализировании выражения A[I] в языке "C"оно немедленно преобразуется к виду *(A+I); эти две формысовершенно эквивалентны. Если применить операцию & к обеимчастям такого соотношения эквивалентности, то мы получим,что &A[I] и A+I тоже идентичны: A+I - адрес I-го элемента отначала A. С другой стороны, если PA является указателем, тов выражениях его можно использовать с индексом: PA[I] иден-тично *(PA+I). Короче, любое выражение, включающее массивы ииндексы, может быть записано через указатели и смещения инаоборот, причем даже в одном и том же утверждении. Имеется одно различие между именем массива и указателем,которое необходимо иметь в виду. указатель является перемен-ной, так что операции PA=A и PA++ имеют смысл. Но имя масси-ва является константой, а не переменной: конструкции типаA=PA или A++,или P=&A будут незаконными. Когда имя массива передается функции, то на самом делеей передается местоположение начала этого массива. Внутривызванной функции такой аргумент является точно такой же пе-ременной, как и любая другая, так что имя массива в качествеаргумента действительно является указателем, т.е. Перемен-ной, содержащей адрес. мы можем использовать это обстоятель-ство для написания нового варианта функции STRLEN, вычисляю-щей длину строки. STRLEN(S) /* RETURN LENGTH OF STRING S */ CHAR *S; { INT N; FOR (N = 0; *S!= '\0'; S++) N++; RETURN(N); } Операция увеличения S совершенно законна, поскольку этапеременная является указателем; S++ никак не влияет на сим-вольную строку в обратившейся к STRLEN функции, а толькоувеличивает локальную для функции STRLEN копию адреса. Опи-сания формальных параметров в определении функции в виде CHAR S[];CHAR *S; совершенно эквивалентны; какой вид описания следует предпо-честь, определяется в значительной степени тем, какие выра-жения будут использованы при написании функции. Если функциипередается имя массива, то в зависимости от того, что удоб-нее, можно полагать, что функция оперирует либо с массивом,либо с указателем, и действовать далее соответвующим обра-зом. Можно даже использовать оба вида операций, если это ка-жется уместным и ясным. Можно передать функции часть массива, если задать в ка-честве аргумента указатель начала подмассива. Например, еслиA - массив, то как F(&A[2]) как и F(A+2) передают функции F адрес элемента A[2], потому что и &A[2],и A+2 являются указательными выражениями, ссылающимися натретий элемент A. внутри функции F описания аргументов могутприсутствовать в виде: F(ARR)INT ARR[];{...} или F(ARR)INT *ARR;{...} Что касается функции F, то тот факт, что ее аргумент в дейс-твительности ссылается к части большего массива,не имеет длянее никаких последствий.Адресная арифметика Если P является указателем, то каков бы ни был сортобъекта, на который он указывает, операция P++ увеличивает Pтак, что он указывает на следующий элемент набора этихобъектов, а операция P +=I увеличивает P так, чтобы он ука-зывал на элемент, отстоящий на I элементов от текущего эле-мента.эти и аналогичные конструкции представляют собой самыепростые и самые распространенные формы арифметики указателейили адресной арифметики. Язык "C" последователен и постоянен в своем подходе кадресной арифметике; объединение в одно целое указателей,массивов и адресной арифметики является одной из наиболеесильных сторон языка. Давайте проиллюстрируем некоторые изсоответствующих возможностей языка на примере элементарной(но полезной, несмотря на свою простоту) программы распреде-ления памяти. Имеются две функции: функция ALLOC(N) возвра-щает в качестве своего значения указатель P, который указы-вает на первую из N последовательных символьных позиций, ко-торые могут быть использованы вызывающей функцию ALLOC прог-раммой для хранения символов; функция FREE(P) освобождаетприобретенную таким образом память, так что ее в дальнейшемможно снова использовать. программа является "элементарной",потому что обращения к FREE должны производиться в порядке,обратном тому, в котором производились обращения к ALLOC.Таким образом, управляемая функциями ALLOC и FREE память яв-ляется стеком или списком, в котором последний вводимый эле-мент извлекается первым. Стандартная библиотека языка "C"содержит аналогичные функции, не имеющие таких ограничений,и, кроме того, в главе 8 мы приведем улучшенные варианты.Между тем, однако, для многих приложений нужна только триви-альная функция ALLOC для распределения небольших участковпамяти неизвестных заранее размеров в непредсказуемые момен-ты времени. Простейшая реализация состоит в том, чтобы функция раз-давала отрезки большого символьного массива, которому мыприсвоили имя ALLOCBUF. Этот массив является собственностьюфункций ALLOC и FREE. Так как они работают с указателями, ане с индексами массива, никакой другой функции не нужнознать имя этого массива. Он может быть описан как внешнийстатический, т.е. Он будет локальным по отношению к исходно-му файлу, содержащему ALLOC и FREE, и невидимым за его пре-делами. При практической реализации этот массив может дажене иметь имени; вместо этого он может быть получен в резуль-тате запроса к операционной системе на указатель некоторогонеименованного блока памяти. Другой необходимой информацией является то, какая частьмассива ALLOCBUF уже использована. Мы пользуемся указателемпервого свободного элемента, названным ALLOCP. Когда к функ-ции ALLOC обращаются за выделением N символов, то она прове-ряет, достаточно ли осталось для этого места в ALLOCBUF. Ес-ли достаточно, то ALLOC возвращает текущее значение ALLOCP(т.е. Начало свободного блока), затем увеличивает его на N,с тем чтобы он указывал на следующую свободную область. Фун-кция FREE(P) просто полагает ALLOCP равным P при условии,что P указывает на позицию внутри ALLOCBUF. DEFINE NULL 0 /* POINTER VALUE FOR ERROR REPORT */DEFINE ALLOCSIZE 1000 /* SIZE OF AVAILABLE SPACE */ TATIC CHAR ALLOCBUF[ALLOCSIZE];/* STORAGE FOR ALLOC */TATIC CHAR *ALLOCP = ALLOCBUF; /* NEXT FREE POSITION */ HAR *ALLOC(N) /* RETURN POINTER TO N CHARACTERS */INT N;(IF (ALLOCP + N <= ALLOCBUF + ALLOCSIZE) { ALLOCP += N; RETURN(ALLOCP - N); /* OLD P */} ELSE /* NOT ENOUGH ROOM */ RETURN(NULL);) REE(P) /* FREE STORAGE POINTED BY P */HAR *P;(IF (P >= ALLOCBUF && P < ALLOCBUF + ALLOCSIZE) ALLOCP = P;) Дадим некоторые пояснения. Вообще говоря, указатель мо-жет быть инициализирован точно так же, как и любая другаяпеременная, хотя обычно единственными осмысленными значения-ми являются NULL (это обсуждается ниже) или выражение, вклю-чающее адреса ранее определенных данных соответствующего ти-па. Описание STATIC CHAR *ALLOCP = ALLOCBUF; определяет ALLOCP как указатель на символы и инициализируетего так, чтобы он указывал на ALLOCBUF, т.е. На первую сво-бодную позицию при начале работы программы. Так как имя мас-сива является адресом его нулевого элемента, то это можнобыло бы записать в виде STATIC CHAR *ALLOCP = &ALLOCBUF[0]; используйте ту запись, которая вам кажется более естествен-ной. С помощью проверки IF (ALLOCP + N <= ALLOCBUF + ALLOCSIZE) выясняется, осталось ли достаточно места, чтобы удовлетво-рить запрос на N символов. Если достаточно, то новое значе-ние ALLOCP не будет указывать дальше, чем на последнюю пози-цию ALLOCBUF. Если запрос может быть удовлетворен, то ALLOCвозвращает обычный указатель (обратите внимание на описаниесамой функции). Если же нет, то ALLOC должна вернуть некото-рый признак, говорящий о том, что больше места не осталось.В языке "C" гарантируется, что ни один правильный указательданных не может иметь значение нуль, так что возвращение ну-ля может служить в качестве сигнала о ненормальном событии,в данном случае об отсутствии места. Мы, однако, вместо нуляпишем NULL, с тем чтобы более ясно показать, что это специ-альное значение указателя. Вообще говоря, целые не могут ос-мысленно присваиваться указателям, а нуль - это особый слу-чай. Проверки вида IF (ALLOCP + N <= ALLOCBUF + ALOOCSIZE)и IF (P >= ALLOCBUF && P < ALLOCBUF + ALLOCSIZE) демонстрируют несколько важных аспектов арифметики указате-лей. Во-первых, при определенных условиях указатели можносравнивать. Если P и Q указывают на элементы одного и тогоже массива, то такие отношения, как <, >= и т.д., работаютнадлежащим образом. Например, P < Q истинно, если P указывает на более ранний элемент массива,чем Q. Отношения == и!= тоже работают. Любой указатель мож-но осмысленным образом сравнить на равенство или неравенствос NULL. Но ни за что нельзя ручаться, если вы используетесравнения при работе с указателями, указывающими на разныемассивы. Если вам повезет, то на всех машинах вы получитеочевидную бессмыслицу. Если же нет, то ваша программа будетправильно работать на одной машине и давать непостижимые ре-зультаты на другой. Во-вторых, как мы уже видели, указатель и целое можноскладывать и вычитать. Конструкция P + N подразумевает N-ый объект за тем, на который P указывает внастоящий момент. Это справедливо независимо от того, на ка-кой вид объектов P должен указывать; компилятор сам масшта-бирует N в соответствии с определяемым из описания P разме-ром объектов, указываемых с помощью P. например, на PDP-11масштабирующий множитель равен 1 для CHAR, 2 для INT иSHORT, 4 для LONG и FLOAT и 8 для DOUBLE. Вычитание указателей тоже возможно: если P и Q указываютна элементы одного и того же массива, то P-Q - количествоэлементов между P и Q. Этот факт можно использовать для на-писания еще одного варианта функции STRLEN: STRLEN(S) /* RETURN LENGTH OF STRING S */ CHAR *S; { CHAR *P = S; WHILE (*P!= '\0') P++; RETURN(P-S); } При описании указатель P в этой функции инициализированпосредством строки S, в результате чего он указывает на пер-вый символ строки. В цикле WHILE по очереди проверяется каж-дый символ до тех пор, пока не появится символ конца строки\0. Так как значение \0 равно нулю, а WHILE только выясняет,имеет ли выражение в нем значение 0, то в данном случае яв-ную проверку можно опустить. Такие циклы часто записывают ввиде WHILE (*P) P++; Так как P указывает на символы, то оператор P++ передви-гает P каждый раз так, чтобы он указывал на следующий сим-вол. В результате P-S дает число просмотренных символов, т.е. Длину строки. Арифметика указателей последовательна:если бы мы имели дело с переменными типа FLOAT, которые за-нимают больше памяти, чем переменные типа CHAR, и если бы Pбыл указателем на FLOAT, то оператор P++ передвинул бы P наследующее FLOAT. таким образом, мы могли бы написать другойвариант функции ALLOC, распределяющей память для FLOAT,вместо CHAR, просто заменив всюду в ALLOC и FREE описательCHAR на FLOAT. Все действия с указателями автоматически учи-тывают размер объектов, на которые они указывают, так чтобольше ничего менять не надо. За исключением упомянутых выше операций (сложение и вы-читание указателя и целого, вычитание и сравнение двух ука-зателей), вся остальная арифметика указателей является неза-конной. Запрещено складывать два указателя, умножать, де-лить, сдвигать или маскировать их, а также прибавлять к нимпеременные типа FLOAT или DOUBLE.
|
||
|
Последнее изменение этой страницы: 2016-08-26; просмотров: 303; Нарушение авторского права страницы; Мы поможем в написании вашей работы! infopedia.su Все материалы представленные на сайте исключительно с целью ознакомления читателями и не преследуют коммерческих целей или нарушение авторских прав. Обратная связь - 216.73.216.10 (0.014 с.) |