Опубликован: 05.07.2006 | Доступ: свободный | Студентов: 4628 / 878 | Оценка: 4.12 / 3.74 | Длительность: 18:59:00
Лекция 7:

Структуры

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

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

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

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

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

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

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