Структуры и функции

Язык С
Строк программы, исключая математическое обеспечение Является учебным введением в центральную часть языка “C” Hачинаем. Единственный способ освоить новый язык Оператор FOR Набор полезных программ Подсчет символов Подсчет слов Функции Аргументы - вызов по значению Область действия: внешние переменные Резюме Константы Описания Преобразование типов До 9 и буквы от а до F Операции и выражения присваивания Старшинство и порядок вычисления Операторы и блоки Переключатель Цикл DO - WHILE Оператор CONTINUE Основные сведения Функции, возвращающие нецелые значения Еще об аргументах функций Правила, определяющие область действия Статические переменные Блочная структура Рекурсия Указатели и адреса Указатели и массивы Адресная арифметика Указатели символов и функции Указатели - не целые До 12, а не от 0 до 11. Так как за экономию памяти у нас пока не награждают, такой способ проще, чем подгонка индек-сов Инициализация массивов указателей Указатели на функции Структуры Структуры и функции Указатели на структуры Мы продемонстрируем, как правильно выполнить эту задачу Поля Определение типа Обращение к стандартной библиотеке Форматный вывод - функция PRINTF Форматный ввод - функция SCANF Форматное преобразование в памяти Обработка ошибок - STDERR и EXIT Обращение к системе Низкоуровневый ввод/вывод - операторы READ и WRITE Произвольный доступ - SEEK и LSEEK Пример - распечатка справочников Пример - распределитель памяти Константы Синтаксическая нотация Преобразования Первичные выражения Унарные операции Аддитивные операции Операция присваивания Спецификаторы типа Описание структур и объединений Инициализация TYPEDEF Оператор SWITCH Внешнее определение функции Область действия внешних идентификаторов Неявные описания Явные преобразования указателей Анахронизмы Операторы
439386
знаков
0
таблиц
0
изображений

6.2. Структуры и функции.

В языке “C” существует ряд ограничений на использование

структур. Обязательные правила заключаются в том, что единс-

твенные операции, которые вы можете проводить со структура-

ми, состоят в определении ее адреса с помощью операции & и

доступе к одному из ее членов. Это влечет за собой то, что

структуры нельзя присваивать или копировать как целое, и что

они не могут быть переданы функциям или возвращены ими. (В

последующих версиях эти ограничения будут сняты). На указа-

тели структур эти ограничения однако не накладываются, так

что структуры и функции все же могут с удобством работать

совместно. И наконец, автоматические структуры, как и авто-

матические массивы, не могут быть инициализированы; инициа-

лизация возможна только в случае внешних или статических

структур.

Давайте разберем некоторые из этих вопросов, переписав с

этой целью функции перобразования даты из предыдущей главы

так, чтобы они использовали структуры. Так как правила зап-

рещают непосредственную передачу структуры функции, то мы

должны либо передавать отдельно компоненты, либо передать

указатель всей структуры. Первая возможность демонстрируется

на примере функции DAY_OF_YEAR, как мы ее написали в главе

5:

D.YEARDAY = DAY_OF_YEAR(D.YEAR, D.MONTH, D.DAY);

·    
131 -

другой способ состоит в передаче указателя. если мы опишем

HIREDATE как

 

STRUCT DATE HIREDATE;

и перепишем DAY_OF_YEAR нужным образом, мы сможем тогда на-

писать

HIREDATE YEARDAY = DAY_OF_YEAR(&HIREDATE);

передавая указатель на HIREDATE функции DAY_OF_YEAR . Функ-ция должна быть модифицирована, потому что ее аргумент те-перь является указателем, а не списком переменных.

DAY_OF_YEAR(PD) /* SET DAY OF YEAR FROM MONTH, DAY */ STRUCT DATE *PD;

\(

INT I, DAY, LEAP;

DAY = PD->DAY;

LEAP = PD->YEAR % 4 == 0 && PD->YEAR % 100 != 0

\!\! PD->YEAR % 400 == 0;

FOR (I =1; I < PD->MONTH; I++)

DAY += DAY_TAB[LEAP][I];

RETURN(DAY);

\)

 

 

Описание

STRUCT DATE *PD;

говорит, что PD является указателем структуры типа DATE.

Запись, показанная на примере

 

PD->YEAR

является новой. Если P - указатель на структуру, то

P-> член структуры

обращается к конкретному члену. (Операция -> - это знак ми-

нус, за которым следует знак “>”.)

Так как PD указывает на структуру, то к члену YEAR можно

обратиться и следующим образом

(*PD).YEAR

но указатели структур используются настолько часто, что за-

пись -> оказывается удобным сокращением. Круглые скобки в

(*PD).YEAR необходимы, потому что операция указания члена


·     132 -

стуктуры старше , чем * . Обе операции, “->” и “.”, ассоции-

руются слева направо, так что конструкции слева и справа

зквивалентны

 

P->Q->MEMB (P->Q)->MEMB

EMP.BIRTHDATE.MONTH (EMP.BIRTHDATE).MONTH

 

Для полноты ниже приводится другая функция, MONTH_DAY, пере-

писанная с использованием структур.

MONTH_DAY(PD) /* SET MONTH AND DAY FROM DAY OF YEAR */ STRUCT DATE *PD;

\(

INT I, LEAP;

LEAP = PD->YEAR % 4 == 0 && PD->YEAR % 100 != 0

\!\! PD->YEAR % 400 == 0;

PD->DAY = PD->YEARDAY;

FOR (I = 1; PD->DAY > DAY_TAB[LEAP][I]; I++)

PD->DAY -= DAY_TAB[LEAP][I];

PD->MONTH = I;

\)

 

Операции работы со структурами “->” и “.” наряду со ()

для списка аргументов и [] для индексов находятся на самом

верху иерархии страшинства операций и, следовательно, связы-

ваются очень крепко. Если, например, имеется описание

 

STRUCT \(

INT X;

INT *Y;

\) *P;

то выражение

++P->X

увеличивает х, а не р, так как оно эквивалентно выражению

++(P->х). Для изменения порядка выполнения операций можно

использовать круглые скобки: (++P)->х увеличивает P до дос-

тупа к х, а (P++)->X увеличивает P после. (круглые скобки в

последнем случае необязательны. Почему ?)

Совершенно аналогично *P->Y извлекает то, на что указы-

вает Y; *P->Y++ увеличивает Y после обработки того, на что

он указывает (точно так же, как и *S++); (*P->Y)++ увеличи-

вает то, на что указывает Y; *P++->Y увеличивает P после вы-

борки того, на что указывает Y.


·     133 -

6.3. Массивы сруктур.

Структуры особенно подходят для управления массивами

связанных переменных. Рассмотрим, например, программу подс-

чета числа вхождений каждого ключевого слова языка “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 \),

. . .


·     134 -

Но когда инициализаторы являются простыми переменными или

символьными строками и все они присутствуют, то во внутрен-

них фигурных скобках нет необходимости. Как обычно, компиля-

тор сам вычислит число элементов массива 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 каждый раз, как она находит слово,

и копирует это слово в свой первый аргумент.

·      
135 -

Величина 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©;

\)


·     136 -

WHILE (--LIM > 0) \(

T = TYPE(C = *W++ = GETCH());

IF (T ! = LETTER && T ! = DIGIT) \(

UNGETCH©;

BREAK;

\)

\)

*(W-1) - '\0';

RETURN(LETTER);

\)

 

Функция GETWORD использует функции GETCH и UNGETCH, которые

мы написали в главе 4: когда набор алфавитных символов пре-

рывается, функция GETWORD получает один лишний символ. В ре-

зультате вызова UNGETCH этот символ помещается назад во ввод

для следующего обращения.

Функция GETWORD обращается к функции TYPE для определе-

ния типа каждого отдельного символа из файла ввода. Вот ва-

риант, справедливый только для алфавита ASCII.

TYPE© /* 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©;

\)

 

 

Символические константы LETTER и DIGIT могут иметь любые

значения, лишь бы они не вступали в конфликт с символами,

отличными от буквенно-цифровых, и с EOF; очевидно возможен

следующий выбор

 

#DEFINE LETTER 'A'

#DEFINE DIGIT '0'

 

функция GETWORD могла бы работать быстрее, если бы обращения

к функции TYPE были заменены обращениями к соответствующему

массиву TYPE[ ]. В стандартной библиотеке языка “C” предус-

мотрены макросы ISALPHA и ISDIGIT, действующие необходимым

образом.

Упражнение 6-1.

Сделайте такую модификацию функции GETWORD и оцените,

как изменится скорость работы программы.

Упражнение 6-2.

Напишите вариант функции TYPE, не зависящий от конкрет-

ного наборасимволов.

·      
137 -

Упражнение 6-3.

Напишите вариант программы подсчета ключевых слов, кото-

рый бы не учитывал появления этих слов в заключенных в ка-

вычки строках.


Информация о работе «Язык С»
Раздел: Информатика, программирование
Количество знаков с пробелами: 439386
Количество таблиц: 0
Количество изображений: 0

Похожие работы

Скачать
48443
0
0

... основаниям. При этом философская абстракция языка оказывается неразрывно связана с основными темами и движениями философии в целом. Более конкретно, на ранние стадии традиционно рассматриваемого в рамках АФ анализа обыденного языка глубокое влияние оказала философия Дж. Э. Мура, особенно его учение о здравом смысле, согласно которому такие понятия, как «человек», «мир», «я», «внешний мир», « ...

Скачать
43709
0
0

... и других странах СНГ, а также облегчение доступа к русской и мировой культуре и науке. Таким образом, судя по данным наших исследований, востребованность русского языка осталась в республике достаточно высокой. Многие представители современной молдавской молодежи продолжают, как их отцы и деды, тянуться к русской культуре, научным и техническим достижениям России. Русский язык остается языком ...

Скачать
39778
0
1

... рисуночное словесно-слоговое письмо). Памятники среднеэламского периода (14—12 вв. до н.э.) выполнены аккадской клинописью. Памятники новоэламского периода относятся к 8—6 вв. до н.э. Был официальным языком в персидском государстве Ахеменидов в 6—4 вв. предполагается, что он, подвергшись влиянию древнеперсидского, сохранился до раннего средневековья. 7. Бурушаски язык Язык бурушаски ( ...

Скачать
64931
0
0

... /диалект), скифский, согдийский, среднеперсидский, таджикский, таджриши (язык/диалект), талышский, татский, хорезмийский, хотаносакский, шугнано-рушанская группа языков, ягнобский, язгулямский и др. Они относятся к индоиранской ветви индоевропейских языков. Области распространения: Иран, Афганистан, Таджикистан, некоторые районы Ирака, Турции, Пакистана, Индии, Грузии, Российской Федерации. Общее ...

0 комментариев


Наверх