![]() Заглавная страница Избранные статьи Случайная статья Познавательные статьи Новые добавления Обратная связь FAQ Написать работу КАТЕГОРИИ: ![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() ТОП 10 на сайте Приготовление дезинфицирующих растворов различной концентрацииТехника нижней прямой подачи мяча. Франко-прусская война (причины и последствия) Организация работы процедурного кабинета Смысловое и механическое запоминание, их место и роль в усвоении знаний Коммуникативные барьеры и пути их преодоления Обработка изделий медицинского назначения многократного применения Образцы текста публицистического стиля Четыре типа изменения баланса Задачи с ответами для Всероссийской олимпиады по праву ![]() Мы поможем в написании ваших работ! ЗНАЕТЕ ЛИ ВЫ?
Влияние общества на человека
Приготовление дезинфицирующих растворов различной концентрации Практические работы по географии для 6 класса Организация работы процедурного кабинета Изменения в неживой природе осенью Уборка процедурного кабинета Сольфеджио. Все правила по сольфеджио Балочные системы. Определение реакций опор и моментов защемления |
Указатели и многомерные массивыСодержание книги
Поиск на нашем сайте
Командная строка аргументов Системные средства, на которые опирается реализация язы-ка "с", позволяют передавать командную строку аргументов илипараметров начинающей выполняться программе. Когда функцияMAIN вызывается к исполнению, она вызывается с двумя аргу-ментами. Первый аргумент (условно называемый ARGC) указываетчисло аргументов в командной строке, с которыми происходитобращение к программе; второй аргумент (ARGV) является ука-зателем на массив символьных строк, содержащих эти аргумен-ты, по одному в строке. Работа с такими строками - это обыч-ное использование многоуровневых указателей. Самую простую иллюстрацию этой возможности и необходимыхпри этом описаний дает программа ECHO, которая просто печа-тает в одну строку аргументы командной строки, разделяя ихпробелами. Таким образом, если дана команда ECHO HELLO, WORLD то выходом будет HELLO, WORLD по соглашению ARGV[0] является именем, по которому вызывает-ся программа, так что ARGC по меньшей мере равен 1. В приве-денном выше примере ARGC равен 3, а ARGV[0], ARGV[1] иARGV[2] равны соответственно "ECHO", "HELLO," и "WORLD".Первым фактическим агументом является ARGV[1], а последним -ARGV[ARGC-1]. Если ARGC равен 1, то за именем программы неследует никакой командной строки аргументов. Все это показа-но в ECHO: MAIN(ARGC, ARGV) /* ECHO ARGUMENTS; 1ST VERSION */INT ARGC;CHAR *ARGV[];\(INT I; FOR (I = 1; I < ARGC; I++) PRINTF("%S%C", ARGV[I], (I 0) PRINTF("%S%C",*++ARGV, (ARGC > 1)? ' ': '\N');\) Так как ARGV является указателем на начало массива строк-ар-гументов, то, увеличив его на 1 (++ARGV), мы вынуждаем егоуказывать на подлинный аргумент ARGV[1], а не на ARGV[0].Каждое последующее увеличение передвигает его на следующийаргумент; при этом *ARGV становится указателем на этот аргу-мент. одновременно величина ARGC уменьшается; когда она об-ратится в нуль, все аргументы будут уже напечатаны. Другой вариант: MAIN(ARGC, ARGV) /* ECHO ARGUMENTS; 3RD VERSION */ INT ARGC; CHAR *ARGV[]; \(WHILE (--ARGC > 0) PRINTF((ARGC > 1)? "%S": "%S\N", *++ARGV); \) Эта версия показывает, что аргумент формата функции PRINTFможет быть выражением, точно так же, как и любой другой. Та-кое использование встречается не очень часто, но его все жестоит запомнить. Как второй пример, давайте внесем некоторые усовершенст-вования в программу отыскания заданной комбинации символовиз главы 4. Если вы помните, мы поместили искомую комбинациюглубоко внутрь программы, что очевидно является совершеннонеудовлетворительным. Следуя утилите GREP системы UNIX, да-вайте изменим программу так, чтобы эта комбинация указыва-лась в качестве первого аргумента строки. #DEFINE MAXLINE 1000 MAIN(ARGC, ARGV) /* FIND PATTERN FROM FIRST ARGUMENT */ INT ARGC; CHAR *ARGV[]; \(CHAR LINE[MAXLINE]; IF (ARGC!= 2) PRINTF ("USAGE: FIND PATTERN\N"); ELSE WHILE (GETLINE(LINE, MAXLINE) > 0) IF (INDEX(LINE, ARGV[1] >= 0) PRINTF("%S", LINE); \) Теперь может быть развита основная модель, иллюстрирую-щая дальнейшее использование указателей. Предположим, чтонам надо предусмотреть два необязательных аргумента. Одинутверждает: "напечатать все строки за исключением тех, кото-рые содержат данную комбинацию", второй гласит: "перед каж-дой выводимой строкой должен печататься ее номер". Общепринятым соглашением в "с"-программах является то,что аргумент, начинающийся со знака минус, вводит необяза-тельный признак или параметр. Если мы, для того, чтобы сооб-щить об инверсии, выберем -X, а для указания о нумерациинужных строк выберем -N("номер"), то команда FIND -X -N THE при входных данных NOW IS THE TIME FOR ALL GOOD MEN TO COME TO THE AID OF THEIR PARTY. Должна выдать 2:FOR ALL GOOD MEN Нужно, чтобы необязательные аргументы могли располагать-ся в произвольном порядке, и чтобы остальная часть программыне зависела от количества фактически присутствующих аргумен-тов. в частности, вызов функции INDEX не должен содержатьссылку на ARGV[2], когда присутствует один необязательныйаргумент, и на ARGV[1], когда его нет. Более того, для поль-зователей удобно, чтобы необязательные аргументы можно былообъединить в виде: FIND -NX THE вот сама программа: #DEFINE MAXLINE 1000 MAIN(ARGC, ARGV) /* FIND PATTERN FROM FIRST ARGUMENT */INT ARGC;CHAR *ARGV[];\(CHAR LINE[MAXLINE], *S; LONG LINENO = 0; INT EXCEPT = 0, NUMBER = 0; WHILE (--ARGC > 0 && (*++ARGV)[0] == '-') FOR (S = ARGV[0]+1; *S!= '\0'; S++) SWITCH (*S) \(CASE 'X': EXCEPT = 1; BREAK; CASE 'N': NUMBER = 1; BREAK; DEFAULT: PRINTF("FIND: ILLEGAL OPTION %C\N", *S); ARGC = 0; BREAK; \)IF (ARGC!= 1) PRINTF("USAGE: FIND -X -N PATTERN\N");ELSE WHILE (GETLINе(LINE, MAXLINE) > 0) \(LINENO++; IF ((INDEX(LINE, *ARGV) >= 0)!= EXCEPT) \ IF (NUMBER) PRINTF("%LD: ", LINENO); PRINTF("%S", LINE); \) \)\) Аргумент ARGV увеличивается перед каждым необязательнымаргументом, в то время как аргумент ARGC уменьшается. еслинет ошибок, то в конце цикла величина ARGC должна равняться1, а *ARGV должно указывать на заданную комбинацию. Обратитевнимание на то, что *++ARGV является указателем аргументнойстроки; (*++ARGV)[0] - ее первый символ. Круглые скобкиздесь необходимы, потому что без них выражение бы принялосовершенно отличный (и неправильный) вид *++(ARGV[0]). Дру-гой правильной формой была бы **++ARGV. Упражнение 5-7 -------------- Напишите программу ADD, вычисляющую обратное польскоевыражение из командной строки. Например, ADD 2 3 4 + * вычисляет 2*(3+4). Упражнение 5-8 -------------- Модифицируйте программы ENTAB и DETAB (указанные в ка-честве упражнений в главе 1) так, чтобы они получали списоктабуляционных остановок в качестве аргументов. Если аргумен-ты отсутствуют, используйте стандартную установку табуляций. Упражнение 5-9 -------------- Расширьте ENTAB и DETAB таким образом, чтобы они воспри-нимали сокращенную нотацию ENTAB M +N означающую табуляционные остановки через каждые N столбцов,начиная со столбца M. Выберите удобное (для пользователя)поведение функции по умолчанию. Упражнение 5-10 --------------- Напишите программу для функции TAIL, печатающей послед-ние N строк из своего файла ввода. Пусть по умолчанию N рав-но 10, но это число может быть изменено с помощью необяза-тельного аргумента, так что TAIL -N печатает последние N строк. программа должна действовать ра-ционально, какими бы неразумными ни были бы ввод или значе-ние N. Составьте программу так, чтобы она оптимальным обра-зом использовала доступную память: строки должны храниться,как в функции SORT, а не в двумерном массиве фиксированногоразмера.Указатели на функции
* 6. Структуры *
Основные сведения Давайте снова обратимся к процедурам преобразования датыиз главы 5. Дата состоит из нескольких частей таких, какдень, месяц, и год, и, возможно, день года и имя месяца. Этипять переменных можно объеденить в одну структуру вида: STRUCT DATE \(INT DAY; INT MONTH; INT YEAR; INT YEARDAY; CHAR MON_NAME[4]; \); Описание структуры, состоящее из заключенного в фигурныескобки списка описаний, начинается с ключевого слова STRUCT.За словом STRUCT может следовать необязательное имя, называ-емое ярлыком структуры (здесь это DATе). Такой ярлык именуетструктуры этого вида и может использоваться в дальнейшем каксокращенная запись подробного описания. Элементы или переменные, упомянутые в структуре, называ-ются членами. Ярлыки и члены структур могут иметь такие жеимена, что и обычные переменные (т.е. Не являющиеся членамиструктур), поскольку их имена всегда можно различить по кон-тексту. Конечно, обычно одинаковые имена присваивают толькотесно связанным объектам. Точно так же, как в случае любого другого базисного ти-па, за правой фигурной скобкой, закрывающей список членов,может следовать список переменных.Оператор STRUCT \(...\) X,Y,Z; синтаксически аналогичен INT X,Y,Z; в том смысле, что каждый из операторов описывает X, Y и Z вкачестве переменных соотвествующих типов и приводит к выде-лению для них памяти. Описание структуры, за которым не следует списка пере-менных, не приводит к выделению какой-либо памяти; оно толь-ко определяет шаблон или форму структуры. Однако, если такоеописание снабжено ярлыком, то этот ярлык может быть исполь-зован позднее при определении фактических экземпляров струк-тур. Например, если дано приведенное выше описание DATE, то STRUCT DATE D; определяет переменную D в качестве структуры типа DATE.Внешнюю или статическую структуру можно инициализировать,поместив вслед за ее определением список инициализаторов дляее компонент: STRUCT DATE D=\(4, 7, 1776, 186, "JUL"\); Член определенной структуры может быть указан в выраже-нии с помощью конструкции вида имя структуры. Член --------------------Операция указания члена структуры "." связывает имя структу-ры и имя члена. В качестве примера определим LEAP (признаквисокосности года) на основе даты, находящейся в структуреD, LEAP = D.YEAR % 4 == 0 && D.YEAR % 100!= 0 \!\! D.YEAR % 400 == 0; или проверим имя месяца IF (STRCMP(D.MON_NAME, "AUG") == 0)... Или преобразуем первый символ имени месяца так, чтобы ононачиналось со строчной буквы D.MON_NAME[0] = LOWER(D.MON_NAME[0]); Структуры могут быть вложенными; учетная карточка служа-щего может фактически выглядеть так: STRUCT PERSON \(CHAR NAME[NAMESIZE]; CHAR ADDRESS[ADRSIZE]; LONG ZIPCODE; /* почтовый индекс */ LONG SS_NUMBER; /* код соц. Обеспечения */ DOUBLE SALARY; /* зарплата */ STRUCT DATE BIRTHDATE; /* дата рождения */ STRUCT DATE HIREDATE; /* дата поступления на работу */ \); Структура PERSON содержит две структуры типа DATE. Если мыопределим EMP как STRUCT PERSON EMP; то EMP.BIRTHDATE.MONTH будет ссылаться на месяц рождения. Операция указания членаструктуры "." ассоциируется слева направо.Структуры и функции
Массивы сруктур Структуры особенно подходят для управления массивамисвязанных переменных. Рассмотрим, например, программу подс-чета числа вхождений каждого ключевого слова языка "C". Намнужен массив символьных строк для хранения имен и массив це-лых для подсчета. одна из возможностей состоит в использова-нии двух параллельных массивов KEYWORD и KEYCOUNT: CHAR *KEYWORD [NKEYS];INT KEYCOUNT [NKEYS]; Но сам факт, что массивы параллельны, указывает на возмож-ность другой организации. Каждое ключевое слово здесь по су-ществу является парой: CHAR *KEYWORD;INT KEYCOUNT; и, следовательно, имеется массив пар. Описание структуры STRUCT KEY \(CHAR *KEYWORD; INT KEYCOUNT;\) KEYTAB [NKEYS]; оперделяет массив KEYTAB структур такого типа и отводит дляних память. Каждый элемент массива является структурой. Этоможно было бы записать и так: STRUCT KEY \(CHAR *KEYWORD; INT KEYCOUNT;\);STRUCT KEY KEYTAB [NKEYS]; Так как структура KEYTAB фактически содержит постоянныйнабор имен, то легче всего инициализировать ее один раз идля всех членов при определении. Инициализация структурвполне аналогична предыдущим инициализациям - за определени-ем следует заключенный в фигурные скобки список инициализа-торов: STRUCT KEY \(CHAR *KEYWORD; INT KEYCOUNT; \) KEYTAB[] =\("BREAK", 0, "CASE", 0, "CHAR", 0, "CONTINUE", 0, "DEFAULT", 0, /*... */ "UNSIGNED", 0, "WHILE", 0 \); Инициализаторы перечисляются парами соответственно членамструктуры. Было бы более точно заключать в фигурные скобкиинициализаторы для каждой "строки" или структуры следующимобразом: \("BREAK", 0 \), \("CASE", 0 \),... Но когда инициализаторы являются простыми переменными илисимвольными строками и все они присутствуют, то во внутрен-них фигурных скобках нет необходимости. Как обычно, компиля-тор сам вычислит число элементов массива KEYTAB, если иници-ализаторы присутствуют, а скобки [] оставлены пустыми. Программа подсчета ключевых слов начинается с определе-ния массива KEYTAB. ведущая программа читает свой файл вво-да, последовательно обращаясь к функции GETWORD, которая из-влекает из ввода по одному слову за обращение. Каждое словоищется в массиве KEYTAB с помощью варианта функции бинарногопоиска, написанной нами в главе 3. (Конечно, чтобы эта функ-ция работала, список ключевых слов должен быть расположен впорядке возрастания). #DEFINE MAXWORD 20 MAIN() /* COUNT "C" KEYWORDS */ \(INT N, T; CHAR WORD[MAXWORD]; WHILE ((T = GETWORD(WORD,MAXWORD))!= EOF) IF (T == LETTER) IF((N = BINARY(WORD,KEYTAB,NKEYS)) >= 0) KEYTAB[N].KEYCOUNT++; FOR (N =0; N < NKEYS; N++) IF (KEYTAB[N].KEYCOUNT > 0) PRINTF("%4D %S\N", KEYTAB[N].KEYCOUNT, KEYTAB[N].KEYWORD); \) BINARY(WORD, TAB, N) /* FIND WORD IN TAB[0]...TAB[N-1] */ CHAR *WORD; STRUCT KEY TAB[]; INT N; \(INT LOW, HIGH, MID, COND; LOW = 0; HIGH = N - 1; WHILE (LOW <= HIGH) \(MID = (LOW+HIGH) / 2; IF((COND = STRCMP(WORD, TAB[MID].KEYWORD)) < 0) HIGH = MID - 1; ELSE IF (COND > 0) LOW = MID + 1; ELSE RETURN (MID); \) RETURN(-1); \)Мы вскоре приведем функцию GETWORD; пока достаточно сказать,что она возвращает LETTER каждый раз, как она находит слово,и копирует это слово в свой первый аргумент. Величина NKEYS - это количество ключевых слов в массивеKEYTAB. Хотя мы можем сосчитать это число вручную, гораздолегче и надежнее поручить это машине, особенно в том случае,если список ключевых слов подвержен изменениям. Одной извозможностей было бы закончить список инициализаторов указа-нием на нуль и затем пройти в цикле сквозь массив KEYTAB,пока не найдется конец. Но, поскольку размер этого массива полностью определен кмоменту компиляции, здесь имеется более простая возможность.Число элементов просто есть SIZE OF KEYTAB / SIZE OF STRUCT KEY дело в том, что в языке "C" предусмотрена унарная операцияSIZEOF, выполняемая во время компиляции, которая позволяетвычислить размер любого объекта. Выражение SIZEOF(OBJECT) выдает целое, равное размеру указанного объекта. (Размер оп-ределяется в неспецифицированных единицах, называемых "бай-тами", которые имеют тот же размер, что и переменные типаCHAR). Объект может быть фактической переменной, массивом иструктурой, или именем основного типа, как INT или DOUBLE,или именем производного типа, как структура. В нашем случаечисло ключевых слов равно размеру массива, деленному на раз-мер одного элемента массива. Это вычисление используется вутверждении #DEFINE для установления значения NKEYS: #DEFINE NKEYS (SIZEOF(KEYTAB) / SIZEOF(STRUCT KEY)) Теперь перейдем к функции GETWORD. Мы фактически написа-ли более общий вариант функции GETWORD, чем необходимо дляэтой программы, но он не на много более сложен. ФункцияGETWORD возвращает следующее "слово" из ввода, где словомсчитается либо строка букв и цифр, начинающихся с буквы, ли-бо отдельный символ. Тип объекта возвращается в качетве зна-чения функции; это - LETTER, если найдено слово, EOF дляконца файла и сам символ, если он не буквенный. GETWORD(W, LIM) /* GET NEXT WORD FROM INPUT */ CHAR *W; INT LIM; \(INT C, T; IF (TYPE(C=*W++=GETCH())!=LETTER) \(*W='\0'; RETURN(C); \) WHILE (--LIM > 0) \(T = TYPE(C = *W++ = GETCH()); IF (T! = LETTER && T! = DIGIT) \(UNGETCH(C); BREAK; \) \) *(W-1) - '\0'; RETURN(LETTER); \) Функция GETWORD использует функции GETCH и UNGETCH, которыемы написали в главе 4: когда набор алфавитных символов пре-рывается, функция GETWORD получает один лишний символ. В ре-зультате вызова UNGETCH этот символ помещается назад во вводдля следующего обращения. Функция GETWORD обращается к функции TYPE для определе-ния типа каждого отдельного символа из файла ввода. Вот ва-риант, справедливый только для алфавита ASCII. TYPE(C) /* RETURN TYPE OF ASCII CHARACTER */ INT C; \(IF (C>= 'A' && C<= 'Z' \!\! C>= 'A' && C<= 'Z') RETURN(LETTER); ELSE IF (C>= '0' && C<= '9') RETURN(DIGIT); ELSE RETURN(C); \) Символические константы LETTER и DIGIT могут иметь любыезначения, лишь бы они не вступали в конфликт с символами,отличными от буквенно-цифровых, и с EOF; очевидно возможенследующий выбор #DEFINE LETTER 'A' #DEFINE DIGIT '0' функция GETWORD могла бы работать быстрее, если бы обращенияк функции TYPE были заменены обращениями к соответствующемумассиву TYPE[ ]. В стандартной библиотеке языка "C" предус-мотрены макросы ISALPHA и ISDIGIT, действующие необходимымобразом. Упражнение 6-1 -------------- Сделайте такую модификацию функции GETWORD и оцените,как изменится скорость работы программы. Упражнение 6-2 -------------- Напишите вариант функции TYPE, не зависящий от конкрет-ного наборасимволов. Упражнение 6-3 -------------- Напишите вариант программы подсчета ключевых слов, кото-рый бы не учитывал появления этих слов в заключенных в ка-вычки строках.Указатели на структуры Чтобы проиллюстрировать некоторые соображения, связанныес использованием указателей и массивов структур, давайтеснова составим программу подсчета ключевых строк, используяна этот раз указатели, а не индексы массивов. Внешнее описание массива KEYTAB не нужно изменять, нофункции MAIN и BINARY требуют модификации. MAIN() /* COUNT C KEYWORD; POINTER VERSION */ \(INT T; CHAR WORD[MAXWORD]; STRUCT KEY *BINARY(), *P; WHILE ((T = GETWORD(WORD, MAXWORD;)!=EOF) IF (T==LETTER) IF ((P=BINARY(WORD,KEYTAB,NKEYS))!=NULL) P->KEYCOUNT++; FOR (P=KEYTAB; P>KEYTAB + NKEYS; P++) IF (P->KEYCOUNT > 0) PRINTF("%4D %S/N", P->KEYCOUNT, P->KEYWORD); \) STRUCT KEY *BINARY(WORD, TAB, N) /* FIND WORD */ CHAR *WORD /* IN TAB[0]...TAB[N-1] */ STRUCT KEY TAB []; INT N; \(INT COND; STRUCT KEY *LOW = &TAB[0]; STRUCT KEY *HIGH = &TAB[N-1]; STRUCT KEY *MID; WHILE (LOW <= HIGH) \(MID = LOW + (HIGH-LOW) / 2; IF ((COND = STRCMP(WORD, MID->KEYWORD)) < 0) HIGH = MID - 1; ELSE IF (COND > 0) LOW = MID + 1; ELSE RETURN(MID); \) RETURN(NULL); \) Здесь имеется несколько моментов, которые стоит отме-тить. Во-первых, описание функции BINARI должно указывать,что она возвращает указатель на структуру типа KEY, а не нацелое; это объявляется как в функции MAIN, так и в BINARY.Если функция BINARI находит слово, то она возвращает указа-тель на него; если же нет, она возвращает NULL. Во-вторых, все обращения к элементам массива KEYTAB осу-ществляются через указатели. Это влечет за собой одно сущес-твенное изменение в функции BINARY: средний элемент большенельзя вычислять просто по формуле MID = (LOW + HIGH) / 2 потому что сложение двух указателей не дает какого-нибудьполезного результата (даже после деления на 2) и в действи-тельности является незаконным. эту формулу надо заменить на MID = LOW + (HIGH-LOW) / 2 в результате которой MID становится указателем на элемент,расположенный посередине между LOW и HIGH. Вам также следует разобраться в инициализации LOW иHIGH. указатель можно инициализировать адресом ранее опреде-ленного объекта; именно как мы здесь и поступили. В функции MAIN мы написали FOR (P=KEYTAB; P < KEYTAB + NKEYS; P++) Если P является указателем структуры, то любая арифметика сP учитывает фактический размер данной структуры, так что P++увеличивает P на нужную величину, в результате чего P указы-вает на следующий элемент массива структур. Но не считайте,что размер структуры равен сумме размеров ее членов, - из-затребований выравнивания для различных объектов в структуремогут возникать "дыры". И, наконец, несколько второстепенный вопрос о форме за-писи программы. Если возвращаемая функцией величина имееттип, как, например, в STRUCT KEY *BINARY(WORD, TAB, N) Tо может оказаться, что имя функции трудно выделить средитекста. В связи с этим иногда используется другой стиль за-писи: STRUCT KEY * BINARY(WORD, TAB, N) Это главным образом дело вкуса; выберите ту форму, котораявам нравится, и придерживайтесь ее.
|
|||||||||
Последнее изменение этой страницы: 2016-08-26; просмотров: 242; Нарушение авторского права страницы; Мы поможем в написании вашей работы! infopedia.su Все материалы представленные на сайте исключительно с целью ознакомления читателями и не преследуют коммерческих целей или нарушение авторских прав. Обратная связь - 3.20.233.113 (0.013 с.) |