Чтение онлайн

ЖАНРЫ

Язык программирования Си. Издание 3-е, исправленное

Ритчи Деннис М.

Шрифт:

В качестве другого примера приведем функцию ptinrect, которая проверяет: находится ли точка внутри прямоугольника, относительно которого мы принимаем соглашение, что в него входят его левая и нижняя стороны, но не входят верхняя и правая.

/* ptinrect: возвращает 1, если p в r, и 0 в противном случае */

int ptinrect(struct point р, struct rect r) {

 return p.x >= r.pt1.x && p.x < r.pt2.x && p.y >= r.pt1.y && p.y < r.pt2.y;

}

Здесь предполагается, что прямоугольник представлен

в стандартном виде, т.е. координаты точки pt1 меньше соответствующих координат точки pt2. Следующая функция гарантирует получение прямоугольника в каноническом виде.

#define min(a, b) ((a) < (b) ? (a) : (b))

#define max(a, b) ((a) > (b) ? (a) : (b))

/* canonrect: канонизация координат прямоугольника */

struct rect canonrect(struct rect r)

{

 struct rect temp;

 temp.pt1.x = min(r.pt1.x, r.pt2.x);

 temp.ptl.y = min(r.pt1.y, r.pt2.у);

 temp.pt2.x = max(r.pt1.x, r.pt2.x);

 temp.pt2.y = max(r.pt1.y, r.pt2.y);

 return temp;

}

Если функции передается большая структура, то, чем копировать ее целиком, эффективнее передать указатель на нее. Указатели на структуры ничем не отличаются от указателей на обычные переменные. Объявление

struct point *pp;

сообщает, что pp– это указатель на структуру типа struct point. Если pp указывает на структуру point, то *pp– это сама структура, а (*pp).x и (*pp).y– ее элементы. Используя указатель pp, мы могли бы написать

struct point origin, *pp;

pp =&origin;

printf("origin: (%d,%d)\n", (*pp).x, (*pp).y);

Скобки в (*pp).x необходимы, поскольку приоритет оператора . выше, чем приоритет *. Выражение *pp.x будет проинтерпретировано как *(pp.x), что неверно, поскольку pp.x не является указателем.

Указатели на структуры используются весьма часто, поэтому для доступа к ее элементам была придумана еще одна, более короткая форма записи. Если p– указатель на структуру, то

р-›

элемент-структуры

есть ее отдельный элемент. (Оператор – › состоит из знака -, за которым сразу следует знак ›.) Поэтому printf можно переписать в виде

printf("origin: (%d,%d)\n", pp-›х, pp-›y);

Операторы. и -› выполняются слева направо. Таким образом, при наличии объявления

struct rect r, *rp = &r;

следующие четыре выражения будут эквивалентны:

r.pt1.x rp-›pt1.x (r.pt1).x (rp-›pt1).x

Операторы доступа к элементам структуры . и – › вместе

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

struct {int len; char *str;} *p;

то

++p-›len

увеличит на 1 значение элемента структуры len, а не указатель p, поскольку в этом выражении как бы неявно присутствуют скобки: ++(p-›len). Чтобы изменить порядок выполнения операций, нужны явные скобки. Так, в (++р)-›len, прежде чем взять значение len, программа прирастит указатель p. В (р++)-›len указатель p увеличится после того, как будет взято значение len (в последнем случае скобки не обязательны).

По тем же правилам *p-›str обозначает содержимое объекта, на который указывает str; *p-›str++ прирастит указатель str после получения значения объекта, на который он указывал (как и в выражении *s++), (*p-›str)++ увеличит значение объекта, на который указывает str; *p++-›str увеличит p после получения того, на что указывает str.

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

Рассмотрим программу, определяющую число вхождений каждого ключевого слова в текст Си-программы. Нам нужно уметь хранить ключевые слова в виде массива строк и счетчики ключевых слов в виде массива целых. Один из возможных вариантов - это иметь два параллельных массива:

char *keyword[NKEYS];

int keycount[NKEYS];

Однако именно тот факт, что они параллельны, подсказывает нам другую организацию хранения - через массив структур. Каждое ключевое слово можно описать парой характеристик

char *word;

int count;

Такие пары составляют массив. Объявление

struct key {

 char *word;

 int count;

} keytab[NKEYS];

объявляет структуру типа key и определяет массив keytab, каждый элемент которого является структурой этого типа и которому где-то будет выделена память. Это же можно записать и по-другому:

struct key {

 char *word;

 int count;

};

struct key keytab[NKEYS];

Так как keytab содержит постоянный набор имен, его легче всего сделать внешним массивом и инициализировать один раз в момент определения. Инициализация структур аналогична ранее демонстрировавшимся инициализациям - за определением следует список инициализаторов, заключенный в фигурные скобки:

struct key {

 char *word;

Поделиться с друзьями: