Заглавная страница Избранные статьи Случайная статья Познавательные статьи Новые добавления Обратная связь FAQ Написать работу КАТЕГОРИИ: ТОП 10 на сайте Приготовление дезинфицирующих растворов различной концентрацииТехника нижней прямой подачи мяча. Франко-прусская война (причины и последствия) Организация работы процедурного кабинета Смысловое и механическое запоминание, их место и роль в усвоении знаний Коммуникативные барьеры и пути их преодоления Обработка изделий медицинского назначения многократного применения Образцы текста публицистического стиля Четыре типа изменения баланса Задачи с ответами для Всероссийской олимпиады по праву
Мы поможем в написании ваших работ! ЗНАЕТЕ ЛИ ВЫ?
Влияние общества на человека
Приготовление дезинфицирующих растворов различной концентрации Практические работы по географии для 6 класса Организация работы процедурного кабинета Изменения в неживой природе осенью Уборка процедурного кабинета Сольфеджио. Все правила по сольфеджио Балочные системы. Определение реакций опор и моментов защемления |
Структуры, ссылающиеся на себяСодержание книги
Поиск на нашем сайте Поиск в таблице Для иллюстрации дальнейших аспектов использования струк-тур в этом разделе мы напишем программу, представляющую со-бой содержимое пакета поиска в таблице. Эта программа явля-ется типичным представителем подпрограмм управления символь-ными таблицами макропроцессора или компилятора. Рассмотрим,например, оператор #DEFINE языка "C". Когда встречаетсястрока вида #DEFINE YES 1 то имя YES и заменяющий текст 1 помещаются в таблицу. Позд-нее, когда имя YES появляется в операторе вида INWORD = YES; Oно должно быть замещено на 1. Имеются две основные процедуры, которые управляют имена-ми и заменяющими их текстами. Функция INSTALL(S,T) записыва-ет имя S и заменяющий текст T в таблицу; здесь S и T простосимвольные строки. Функция LOOKUP(S) ищет имя S в таблице ивозвращает либо указатель того места, где это имя найдено,либо NULL, если этого имени в таблице не оказалось. При этом используется поиск по алгоритму хеширования -поступающее имя преобразуется в маленькое положительное чис-ло, которое затем используется для индексации массива указа-телей. Элемент массива указывает на начало цепочных блоков,описывающих имена, которые имеют это значение хеширования.Если никакие имена при хешировании не получают этого значе-ния, то элементом массива будет NULL. Блоком цепи является структура, содержащая указатели насоответствующее имя, на заменяющий текст и на следующий блокв цепи. Нулевой указатель следующего блока служит признакомконца данной цепи. STRUCT NLIST \(/* BASIC TABLE ENTRY */ CHAR *NAME; CHAR *DEF; STRUCT NLIST *NEXT; /* NEXT ENTRY IN CHAIN */\); Массив указателей это просто DEFINE HASHSIZE 100 TATIC STRUCT NLIST *HASHTAB[HASHSIZE] /* POINTER TABLE */ Значение функции хеширования, используемой обеими функ-циями LOOKUP и INSTALL, получается просто как остаток отделения суммы символьных значений строки на размер массива.(Это не самый лучший возможный алгоритм, но его достоинствосостоит в исключительной простоте). HASH(S) /* FORM HASH VALUE FOR STRING */ CHAR *S; \(INT HASHVAL; FOR (HASHVAL = 0; *S!= '\0';) HASHVAL += *S++; RETURN(HASHVAL % HASHSIZE); \) В результате процесса хеширования выдается начальный ин-декс в массиве HASHTAB; если данная строка может бытьгде-то найдена, то именно в цепи блоков, начало которой ука-зано там. Поиск осуществляется функцией LOOKUP. Если функцияLOOKUP находит, что данный элемент уже присутствует, то онавозвращает указатель на него; если нет, то она возвращаетNULL. STRUCT NLIST *LOOKUP(S) /* LOOK FOR S IN HASHTAB */CHAR *S;\(STRUCT NLIST *NP; FOR (NP = HASHTAB[HASH(S)]; NP!= NULL;NP=NP->NEXT) IF (STRCMP(S, NP->NAME) == 0) RETURN(NP); /* FOUND IT */RETURN(NULL); /* NOT FOUND */ Функция INSTALL использует функцию LOOKUP для определе-ния, не присутствует ли уже вводимое в данный момент имя;если это так, то новое определение должно вытеснить старое.В противном случае создается совершенно новый элемент. Еслипо какой-либо причине для нового элемента больше нет места,то функция INSTALL возвращает NULL. STRUCT NLIST *INSTALL(NAME, DEF) /* PUT (NAME, DEF) */ CHAR *NAME, *DEF; \(STRUCT NLIST *NP, *LOOKUP(); CHAR *STRSAVE(), *ALLOC(); INT HASHVAL; IF((NP = LOOKUP(NAME)) == NULL) \(/* NOT FOUND */ NP = (STRUCT NLIST *) ALLOC(SIZEOF(*NP)); IF (NP == NULL) RETURN(NULL); IF ((NP->NAME = STRSAVE(NAME)) == NULL) RETURN(NULL); HASHVAL = HASH(NP->NAME); NP->NEXT = HASHTAB[HASHVAL]; HASHTAB[HASHVAL] = NP; \) ELSE /* ALREADY THERE */ FREE((NP->DEF);/* FREE PREVIOUS DEFINITION */ IF ((NP->DEF = STRSAVE(DEF)) == NULL) RETURN (NULL); RETURN(NP); \) Функция STRSAVE просто копирует строку, указанную в ка-честве аргумента, в место хранения, полученное в результатеобращения к функции ALLOC. Мы уже привели эту функцию в гла-ве 5. Так как обращение к функции ALLOC и FREE могут проис-ходить в любом порядке и в связи с проблемой выравнивания,простой вариант функции ALLOC из главы 5 нам больше не под-ходит; смотрите главы 7 и 8. Упражнение 6-7 --------------- Напишите процедуру, которая будет удалять имя и опреде-ление из таблицы, управляемой функциями LOOKUP и INSTALL. Упражнение 6-8 --------------- Разработайте простую, основанную на функциях этого раз-дела, версию процессора для обработки конструкций #DEFINE,пригодную для использования с "C"-программами. Вам могуттакже оказаться полезными функции GETCHAR и UNGETCH.Поля Когда вопрос экономии памяти становится очень существен-ным, то может оказаться необходимым помещать в одно машинноеслово несколько различных объектов; одно из особенно расп-росраненных употреблений - набор однобитовых признаков вприменениях, подобных символьным таблицам компилятора. внеш-не обусловленные форматы данных, такие как интерфейсы аппа-ратных средств также зачастую предполагают возможность полу-чения слова по частям. Представьте себе фрагмент компилятора, который работаетс символьной таблицей. С каждым идентификатором программысвязана определенная информация, например, является он илинет ключевым словом, является ли он или нет внешним и/илистатическим и т.д. Самый компактный способ закодировать та-кую информацию - поместить набор однобитовых признаков в от-дельную переменную типа CHAR или INT. Обычный способ, которым это делается, состоит в опреде-лении набора "масок", отвечающих соответствущим битовым по-зициям, как в #DEFINE KEYWORD 01 #DEFINE EXTERNAL 02 #DEFINE STATIC 04 (числа должны быть степенями двойки). Тогда обработка битовсведется к "жонглированию битами" с помощью операций сдвига,маскирования и дополнения, описанных нами в главе 2. Некоторые часто встречающиеся идиомы: FLAGS \!= EXTERNAL \! STATIC; включает биты EXTERNAL и STATIC в FLAGS, в то время как FLAGS &= \^(еXTERNAL \! STATIC); их выключает, а IF ((FLAGS & (EXTERNAL \! STATIC)) == 0)... истинно, если оба бита выключены. Хотя этими идиомами легко овладеть, язык "C" в качествеальтернативы предлагает возможность определения и обработкиполей внутри слова непосредственно, а не посредством побито-вых логических операций. Поле - это набор смежных битоввнутри одной переменной типа INT. Синтаксис определения иобработки полей основывается на структурах. Например, сим-вольную таблицу конструкций #DEFINE, приведенную выше, можнобы было заменить определением трех полей: STRUCT \(UNSIGNED IS_KEYWORD: 1; UNSIGNED IS_EXTERN: 1; UNSIGNED IS_STATIC: 1; \) FLAGS; Здесь определяется переменная с именем FLAGS, которая содер-жит три 1-битовых поля. Следующее за двоеточием число задаетширину поля в битах. Поля описаны как UNSIGNED, чтобы под-черкнуть, что они действительно будут величинами без знака. На отдельные поля можно ссылаться, как FLAGS.IS_STATIE,FLAGS. IS_EXTERN, FLAGS.IS_KEYWORD И т.д., то есть точно также, как на другие члены структуры. Поля ведут себя подобнонебольшим целым без знака и могут участвовать в арифметичес-ких выражениях точно так же, как и другие целые. Таким обра-зом, предыдущие примеры более естественно переписать так: FLAGS.IS_EXTERN = FLAGS.IS_STATIC = 1; для включения битов; FLAGS.IS_EXTERN = FLAGS.IS_STATIC = 0; для выключения битов; IF (FLAGS.IS_EXTERN == 0 &&FLAGS.IS_STATIC == 0)... для их проверки. Поле не может перекрывать границу INT; если указаннаяширина такова, что это должно случиться, то поле выравнива-ется по границе следующего INT. Полям можно не присваиватьимена; неименованные поля (только двоеточие и ширина) ис-пользуются для заполнения свободного места. Чтобы вынудитьвыравнивание на границу следующего INT, можно использоватьспециальную ширину 0. При работе с полями имеется ряд моментов, на которыеследует обратить внимание. По-видимому наиболее существеннымявляется то, что отражая природу различных аппаратных сред-ств, распределение полей на некоторых машинах осуществляетсяслева направо, а на некоторых справа налево. Это означает,что хотя поля очень полезны для работы с внутренне опреде-ленными структурами данных, при разделении внешне определяе-мых данных следует тщательно рассматривать вопрос о том, ка-кой конец поступает первым. Другие ограничения, которые следует иметь в виду: поляне имеют знака; они могут храниться только в переменных типаINT (или, что эквивалентно, типа UNSIGNED); они не являютсямассивами; они не имеют адресов, так что к ним не применимаоперация &.Объединения Oбъединения - это переменная, которая в различные момен-ты времени может содержать объекты разных типов и размеров,причем компилятор берет на себя отслеживание размера и тре-бований выравнивания. Объединения представляют возможностьработать с различными видами данных в одной области памяти,не вводя в программу никакой машинно-зависимой информации. В качестве примера, снова из символьной таблицы компиля-тора, предположим, что константы могут быть типа INT, FLOATили быть указателями на символы. значение каждой конкретнойконстанты должно храниться в переменной соотвествующего ти-па, но все же для управления таблицей самым удобным было бы,если это значение занимало бы один и тот же объем памяти ихранилось в том же самом месте независимо от его типа. это иявляется назначением объединения - выделить отдельную пере-менную, в которой можно законно хранить любую одну из пере-менных нескольких типов. Как и в случае полей, синтаксис ос-новывается на структурах. UNION U_TAG \(INT IVAL; FLOAT FVAL; CHAR *PVAL; \) UVAL; Переменная UVAL будет иметь достаточно большой размер,чтобыхранить наибольший из трех типов, независимо от машины, накоторой осуществляется компиляция, - программа не будет за-висить от характеристик аппаратных средств. Любой из этихтрех типов может быть присвоен UVAR и затем использован ввыражениях, пока такое использование совместимо: извлекаемыйтип должен совпадать с последним помещенным типом. Делопрограммиста - следить за тем, какой тип хранится в объеди-нении в данный момент; если что-либо хранится как один тип,а извлекается как другой, то результаты будут зависеть отиспользуемой машины. Синтаксически доступ к членам объединения осуществляетсяследующим образом: имя объединения.член --------------------или указатель объединения ->член ---------------------------- то есть точно так же, как и в случае структур. если для отс-леживания типа, хранимого в данный момент в UVAL, использу-ется переменная UTYPE, то можно встретить такой участокпрограммы: IF (UTYPE == INT) PRINTF("%D\N", UVAL.IVAL); ELSE IF (UTYPE == FLOAT) PRINTF("%F\N", UVAL.FVAL); ELSE IF (UTYPE == STRING) PRINTF("%S\N", UVAL.PVAL); ELSE PRINTF("BAD TYPE %D IN UTYPE\N", UTYPE); Объединения могут появляться внутри структур и массивови наоборот. Запись для обращения к члену объединения вструктуре (или наоборот) совершенно идентична той, котораяиспользуется во вложенных структурах. например, в массивеструктур, определенным следующим образом STRUCT \(CHAR *NAME; INT FLAGS; INT UTYPE; UNION \(INT IVAL; FLOAT FVAL; CHAR *PVAL; \) UVAL; \) SYMTAB[NSYM]; на переменную IVAL можно сослаться как SYMTAB[I].UVAL.IVAL а на первый символ строки PVAL как *SYMTAB[I].UVAL.PVAL В сущности объединение является структурой, в которой всечлены имеют нулевое смещение. Сама структура достаточно ве-лика, чтобы хранить "самый широкий" член, и выравниваниепригодно для всех типов, входящих в объединение. Как и вслучае структур, единственными операциями, которые в настоя-щее время можно проводить с объединениями, являются доступ к члену и извлечение адреса; объединения не могут быть присво-ены, переданы функциям или возвращены ими. указатели объеди-нений можно использовать в точно такой же манере, как и ука-затели структур. Программа распределения памяти, приводимая в главе 8,показывает, как можно использовать объединение, чтобы сде-лать некоторую переменную выровненной по определенному видуграницы памяти.Определение типа В языке "C" предусмотрена возможность, называемая TYPEDEFдля введения новых имен для типов данных. Например, описание TYPEDEF INT LENGTH; делает имя LENGTH синонимом для INT. "Тип" LENGTH может бытьиспользован в описаниях, переводов типов и т.д. Точно такимже образом, как и тип INT: LENGTH LEN, MAXLEN; LENGTH *LENGTHS[]; Аналогично описанию TYPEDEF CHAR *STRING; делает STRING синонимом для CHAR*, то есть для указателя насимволы, что затем можно использовать в описаниях вида STRING P, LINEPTR[LINES], ALLOC(); Обратите внимание, что объявляемый в конструкции TYPEDEFтип появляется в позиции имени переменной, а не сразу засловом TYPEDEF. Синтаксически конструкция TYPEDEF подобнаописаниям класса памяти EXTERN, STATIC и т. Д. мы также ис-пользовали прописные буквы, чтобы яснее выделить имена. В качестве более сложного примера мы используем конст-рукцию TYPEDEF для описания узлов дерева, рассмотренных ра-нее в этой главе: TYPEDEF STRUCT TNODE \(/* THE BASIC NODE */ CHAR *WORD; /* POINTS TO THE TEXT */ INT COUNT; /* NUMBER OF OCCURRENCES */ STRUCT TNODE *LEFT; /* LEFT CHILD */ STRUCT TNODE *RIGHT; /* RIGHT CHILD */ \) TREENODE, *TREEPTR; В результате получаем два новых ключевых слова: TREENODE(структура) и TREEPTR (указатель на структуру). Тогда функ-цию TALLOC можно записать в виде TREEPTR TALLOC() \(CHAR *ALLOC(); RETURN((TREEPTR) ALLOC(SIZEOF(TREENODE))); \) Необходимо подчеркнуть, что описание TYPEDEF не приводитк созданию нового в каком-либо смысле типа; оно только до-бавляет новое имя для некоторого существующего типа. приэтом не возникает и никакой новой семантики: описанные такимспособом переменные обладают точно теми же свойствами, что ипеременные, описанные явным образом. По существу конструкцияTYPEDEF сходна с #DEFINE за исключением того, что она интер-претируется компилятором и потому может осуществлять подста-новки текста, которые выходят за пределы возможностей мак-ропроцессора языка "C". Например, TYPEDEF INT (*PFI) (); создает тип PFI для "указателя функции, возвращающей значе-ние типа INT", который затем можно было бы использовать впрограмме сортировки из главы 5 в контексте вида PFI STRCMP, NUMCMP, SWAP; Имеются две основные причины применения описанийTYPEDEF. Первая причина связана с параметризацией программы,чтобы облегчить решение проблемы переносимости. Если для ти-пов данных, которые могут быть машинно-зависимыми, использо-вать описание TYPEDEF, то при переносе программы на другуюмашину придется изменить только эти описания. Одна из типич-ных ситуаций состоит в использовании определяемых с помощьюTYPEDEF имен для различных целых величин и в последующемподходящем выборе типов SHORT, INT и LONG для каждой имею-щейся машины.Второе назначение TYPEDEF состоит в обеспечении лучшей доку-ментации для программы - тип с именем TREEPTR может оказать-ся более удобным для восприятия, чем тип, который описантолько как указатель сложной структуры.И наконец, всегда существует вероятность, что в будущем ком-пилятор или некоторая другая программа, такая как LINT, смо-жет использовать содержащуюся в описаниях TYPEDEF информациюдля проведения некоторой дополнительной проверки программы.* 7. Ввод и вывод * Средства ввода/вывода не являются составной частью языка"с", так что мы не выделяли их в нашем предыдущем изложении.Однако реальные программы взаимодействуют со своей окружаю-щей средой гораздо более сложным образом, чем мы видели досих пор. В этой главе будет описана "стандартная библиотекаввода/вывода", то есть набор функций, разработанных дляобеспечения стандартной системы ввода/вывода для "с"- прог-рамм. Эти функции предназначены для удобства программногоинтерфейса, и все же отражают только те операции, которыемогут быть обеспечены на большинстве современных операцион-ных систем. Процедуры достаточно эффективны для того, чтобыпользователи редко чувствовали необходимость обойти их "радиэффективности", как бы ни была важна конкретная задача. И,наконец, эти процедуры задуманы быть "переносимыми" в томсмысле, что они должны существовать в совместимом виде налюбой системе, где имеется язык "с", и что программы, кото-рые ограничивают свои взаимодействия с системой возможностя-ми, предоставляемыми стандартной библиотекой, можно будетпереносить с одной системы на другую по существу без измене-ний. Мы здесь не будем пытаться описать всю библиотеку вво-да/вывода; мы более заинтересованы в том, чтобы продемонст-рировать сущность написания "с"-программ, которые взаимодей-ствуют со своей операционной средой.
|
||
|
Последнее изменение этой страницы: 2016-08-26; просмотров: 389; Нарушение авторского права страницы; Мы поможем в написании вашей работы! infopedia.su Все материалы представленные на сайте исключительно с целью ознакомления читателями и не преследуют коммерческих целей или нарушение авторских прав. Обратная связь - 216.73.217.21 (0.009 с.) |