Смекни!
smekni.com

Методические указания к выполнению контрольных работ по дисциплине "Основы программирования" (стр. 32 из 40)

int year;

int yearday;

char mon_name[4];

};

Описание структуры, состоящее из заключенного в фигурные скобки списка описаний, начинается с ключевого слова struct. За словом struct может следовать необязательное имя, называемое ярлыком структуры (здесь это date). Такой ярлык именует структуры этого вида и может использоваться в дальнейшем как сокращенная запись подробного описания.

Переменные, упомянутые в структуре, называются элементами (или членами) структуры. Ярлыки и элементы структур могут иметь такие же имена, что и обычные переменные (т.е. не являющиеся элементами структур), поскольку их имена всегда можно различить по контексту. Конечно, обычно одинаковые имена присваивают только тесно связанным объектам.

Точно так же, как в случае любого другого базисного типа, за правой фигурной скобкой, закрывающей список элементов, может следовать список переменных. Оператор:

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

будет ссылаться на месяц рождения.

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

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

В языке «C» существует ряд ограничений на использование структур. Обязательные правила заключаются в том, что единственные операции, которые вы можете проводить со структурами, состоят в определении ее адреса с помощью операции & и доступе к одному из ее членов. Это влечет за собой то, что структуры нельзя присваивать или копировать как целое, и что они не могут быть переданы функциям или возвращены ими. (В последующих версиях эти ограничения будут сняты). На указатели структур эти ограничения, однако, не накладываются, так что структуры и функции все же могут с удобством работать совместно. И, наконец, автоматические структуры, как и автоматические массивы, не могут быть инициализированы; инициализация возможна только в случае внешних или статических структур.

Пример 7-1. Давайте разберем некоторые из этих вопросов, переписав с этой целью функции преобразования даты из предыдущей главы так, чтобы они использовали структуры. Так как правила запрещают непосредственную передачу структуры функции, то мы должны либо передавать отдельно компоненты, либо передать указатель всей структуры. Первая возможность демонстрируется на примере функции day_of_year, как мы ее написали в главе 6:

d.yearday = day_of_year(d.year, d.month, d.day);

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

struct date hiredate;

и перепишем day_of_year нужным образом, мы сможем тогда написать:

hiredate.yearday = day_of_year(&hiredate);

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

// Установить день года по месяцу и дню

day_of_year(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 необходимы, потому что операция указания члена структуры старше, чем *. Обе операции, "->" и ".", ассоциируются слева направо, так что конструкции в двух примерах слева и справа эквивалентны:

p->q->memb (p->q)->memb
Emp.birthdate.month (emp.birthdate).month

Для полноты изложения ниже приводится другая функция, month_day, переписанная с использованием структур.

// Установить месяц и день по дню года

month_day(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;

char *y;

} *p;

то выражение:

++p->x

увеличивает х, а не р, так как оно эквивалентно выражению ++(p->х).

Для изменения порядка выполнения операций можно использовать круглые скобки: (++p)->х увеличивает p до доступа к х, а (p++)->x увеличивает х после доступа. (Круглые скобки в последнем случае необязательны. Почему?)

Совершенно аналогично:

*p->y извлекает то, что содержится в элементе y;
*p->y++ увеличивает y после обработки того, на что он указывает (точно так же, как и *s++);
(*p->y)++ увеличивает то, на что указывает y;
*p++->y увеличивает P после выборки того, на что указывает y.

7.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 },

...

Но когда инициализаторы являются простыми переменными или символьными строками и все они присутствуют, то во внутренних фигурных скобках нет необходимости. Как обычно, компилятор сам вычислит число элементов массива keytab, если инициализаторы присутствуют, а скобки [] оставлены пустыми.

Пример 7-2. Программа подсчета ключевых слов начинается с определения массива keytab. ведущая программа читает свой файл ввода, последовательно обращаясь к функции getword, которая извлекает из ввода по одному слову за обращение. Каждое слово ищется в массиве keytab с помощью варианта функции бинарного поиска, написанной нами в главе 4. (Конечно, чтобы эта функция работала, список ключевых слов должен быть расположен в порядке возрастания).

#define maxword 20

main() // Подсчет ключевых слов «С»

{

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&bsol;n",

keytab[n].keycount, keytab[n].keyword);

}

// Функция binary: найти слова в tab[0]...tab[n-1]

binary(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)