КИТ и Э КБГУ Понедельник, 06.05.2024, 22:55
Приветствую Вас Гость | RSS
Меню сайта

Наш опрос
Оцените мой сайт
Всего ответов: 118

Статистика

Онлайн всего: 1
Гостей: 1
Пользователей: 0

Структуры и объединения

  1. Понятия структуры и функции. Массивы структур.
  2. Объединения.

    Лабораторная работа 10. Работа со структурами и массивами структур

    Практическая работа 10. Работа с объединениями

1. Понятия структуры и функции. Массивы структур

Структура ("запись" в терминах языка Паскаль) - это составной объект, в который входят компоненты любых типов, за исключением функций.

1.1 Определение структуры

Существует 3 способа определения структур в программе.

1 способ.

struct {

<список описаний>

} <описатели>;

где <список описаний> - это описание компонентов (полей, элементов) структуры (должен быть указан хотя бы один компонент); 

      <описатели> - это обычно имена переменных, массивов, указателей и функций.

Пример 1. Переменные a и b определяются как структуры, каждая из которых состоит из двух компонентов x и y. Переменная c определяется как массив из 9 таких структур.

struct {

double x,y;

}  a,b,c[9];

Пример 2. Каждая из двух переменных-структур date1 и date2 состоит из 3 компонент

struct {

int year

short mont,day;

} date1,date2;

2 способ

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

typedef struct {

<список описаний>

} <имя типа>

Пример 3. Описан тип структуры с именем empl.

typedef struct {

char name[30];

int d;

} empl;

empl e1,e2;               /* Переменные e1,e2 - это структуры типа empl */

3 способ.

Обоснован на применении меток, или шаблонов, структуры (аналогично меткам перечисляемого типа). Метка структуры описывается следующим образом:

struct <метка> {

<список описаний>

};

где <метка> - это идентификатор.

Структуры определяются с помощью меток структур следующим образом :

struct <метка> <список идентификаторов>;

Использование меток структуры необходимо для описания рекурсивных структур.

Пример 4.

struct student {                     /* student - метка структуры */

char name[23];

int id,age;

char pol;

};

struct student s1,s2;                     /* s1,s2 - это структуры, тип которых задан меткой student */

struct student studgrup[30];              /* studgrup - это массив структур шаблона student */

 

Задание шаблона структуры и объявление переменных можно производить одновременно:

struct student {                     /* student - метка структуры */

char name[23];

int id,age;

char pol;

} s1, s2, studgrup[30];

 

1.2 Доступ к компонентам структуры

Доступ к компонентам структуры имеет вид:

<имя структуры-переменной>.<имя компоненты>

Например (здесь и далее см. пример 4 выше): s1.id, s2.pol.

Следует иметь в виду, что запись studgrup[20].age дает доступ к полю age 21-го элемента массива studgrup, а запись s1.name[5] дает доступ к 6-му элементу поля name переменной s1.

Если объявлены две переменные типа структуры с одним и тем же именем типа или шаблона, то их можно присвоить одна другой; например, s1=s2.

Переменные типа структуры нельзя сравнивать на "=" или " ≠" .

К структуре, как к любой переменной, можно применить операцию & для вычисления ее адреса.

Пример 5.  Дан список автомобилей, каждая строка которого содержит: марку автомобиля, год выпуска, цену. Распечатать список тех автомобилей, год выпуска которых не ранее некоторого заданного года, а цена не превышает некоторой заданной цены.

Стандартная функция fflush (описание в stdio.h) обычно используется для очистки входного потока stdin. В начале работы программы входной поток очищается автоматически.

Рекомендуется перед любой стандартной подпрограммой буферизованного ввода очищать входной поток, что обеспечит защиту от предшествующего некорректного ввода.

#include <stdio.h>

main()

{

struct{

char marka[10];                  /* Марка авто */

int year;                             /* Год выпуска*/

float money;                      /* Цена */

} avto[20];                         /* Массив структур - список данных по автомобилям */

int n,                                /* Количество элементов в массиве avto */

i,                                     /* Индекс массива avto */

y;                                    /* Контрольный год */

float m;                           /* Контрольная цена */

printf("\n Введите количество автомобилей n (n<20): ");

scanf("%d",&n);

printf("Введите список из %d автомобилей:\n",n);

for (i=0;i<n;i++)

{ fflush(stdin); gets(avto[i].marka);

fflush(stdin); scanf(“%d”,&avto[i].year);

fflush(stdin); scanf(“%d”,&avto[i].money); }

printf("Введите контрольный год и цену: ");

scanf("%d%f",&y,&m);

for (i=0;i<n;i++)

if (avto[i].year>=y&&avto[i].money<=m)

printf("\n%12s %4d %7.3f", avto[i].marka, avto[i].year, avto[i].money); }

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

Единственно возможные операции над структурами — это их копирование, присваивание, взятие адреса с помощью & и осуществление доступа к ее элементам. Копирование и присваивание также включают в себя передачу функциям аргументов и возврат ими значений. Структуры нельзя сравнивать. Инициализировать структуру можно списком константных значений ее элементов; автоматическую структуру также можно инициализировать присваиванием.

Существует по крайней мере три подхода: передавать компоненты по отдельности, передавать всю структуру целиком и передавать указатель на структуру. Каждый подход имеет свои плюсы и минусы.

Пример 6. Функция, makepoint, получает два целых значения и возвращает структуру point.

/* makepoint: формирует точку по компонентам х и y */

struct point makepoint(int x, int y)

{

struct point temp;

temp.x = x;

temp.у = у;

return temp;

}

Заметим: никакого конфликта между именем аргумента и именем элемента структуры не возникает; более того, сходство подчеркивает родство обозначаемых им объектов.

Теперь с помощью makepoint можно выполнять динамическую инициализацию любой структуры или формировать структурные аргументы для той или иной функции:

struct rect screen;

struct point middle;

struct point makepoint(int, int);

screen.pt1 = makepoint(0, 0);

screen.pt2 = makepoint(XMAX, YMAX);

middle = makepoint((screen.pt1.x + screen. pt2.x)/2,

(screen.pt1.y + screen.pt2.y)/2);

Следующий шаг состоит в определении ряда функций, реализующих различные операции над точками:

/* addpoint: сложение двух точек */

struct point addpoint(struct point p1, struct point p2)

{

p1.x += p2.x;

p1.y += p2.y;

return p1;

}

Здесь оба аргумента и возвращаемое значение — структуры. Мы увеличиваем компоненты прямо в р1 и не используем для этого временной переменной, чтобы подчеркнуть, что структурные параметры передаются по значению так же, как и любые другие.

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

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

int ptinrect(struct point p, struct rect r)

{

return p.x >= r.ptl.x && p.x < r.pt2.x

&& p.y >= r.ptl.y && p.y < r.pt2.y;

}

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

#define min(a, b) ((a) < (b) ? (а) : (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.y);

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

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

return temp;

}

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

1.4 Указатели на структуры 

Объявление

struct point *pp;

сообщает, что рр — это указатель на структуру типа struct point. Если рр указывает на структуру point, то *рр — это сама структура, а (*рр).х и (*рр).y — ее элементы.

Используя указатель рр, можно написать

struct point origin, *pp;

рр = &origin;

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

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

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

Если р — указатель на структуру, то

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

есть ее отдельный элемент. (Оператор -> состоит из знака -, за которым сразу следует знак >.)

Поэтому можно переписать в виде     printf("origin: (%d,%d)\n", pp->x, pp->y);

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

struct rect r, *rp = &r;

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

r.pt1.х

rp->pt1.x

(r.pt1).x

(rp->pt1).x

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

struct {

int len;

char *str;

} *p;

то

++p->len

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

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

(*p->str)++ увеличит значение объекта, на который указывает str; *p++->str увеличит р после получения того, на что указывает str.

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

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

Пусть дана информация : name (имя), street (улица), city (город), state (штат) и zip (почтовый код, индекс). Вся эта информация находится в массиве структур типа addr:

struct addr {

  char name[30];

  char street[40];

  char city[20];

  char state[3];

  unsigned long int zip;

} addr_list[100];

Это выражение создаст 100 наборов переменных, каждый из которых организован так, как определено в структуре addr.

Чтобы получить доступ к определенной структуре, указывайте имя массива с индексом. Например, чтобы вывести ZIP-код из третьей структуры, напишите следующее:

printf("%d", addr_list[2].zip);

Как и в других массивах переменных, в массивах структур индексирование начинается с 0.

Чтобы указать определенную структуру, находящуюся в массиве структур, необходимо указать имя этого массива с определенным индексом. А если нужно указать индекс определенного элемента в структуре, то необходимо указать индекс этого элемента. Таким образом, в результате выполнения следующего выражения первому символу члена name, находящегося в третьей структуре из addr_list, присваивается значение 'X'.

addr_list[2].name[0] = 'X';

2. Объединения

Объединение — это переменная, которая может содержать (в разные моменты времени) объекты различных типов и размеров. Все требования относительно размеров и выравнивания выполняет компилятор.

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

Объединение подобно структуре, однако в каждый момент времени может использоваться (или являться активным) только один из компонентов. Как и структуру, объединение можно определить 3 способами (см. п.7.2.1.).

Согласно 1-му способу можно записать:

union {

<описание компонента 1>

<описание компонента 2>

... ... ... ... ... ...

<описание компонента n>

} <описатели>;

Для каждого из этих компонентов 1,2,...n выделяется одна и та же область памяти, то есть они перекрываются. Причем места в памяти выделяется ровно столько, сколько надо тому элементу объединения, который имеет наибольший размер в байтах.

Объединения применяются для:

1)минимизации объема памяти, если в каждый момент времени только один объект из многих является активным;

2)интерпретации основного представления объекта одного типа, как если бы этому объекту был присвоен другой тип.

Доступ к компонентам объединения осуществляется тем же способом, что и к компонентам структур.

имя-объединения.элемент

или

указатель-на-объединение->элемент

Пример 8.

union {

float radius;                     /* Окружность */

float a[2];                       /* Прямоугольник */

int b[3];                         /* Треугольник */

position p;                  /* Точка. position - тип, описанный пользователем */

} geom_fig

Имеет смысл обрабатывать лишь активный компонент, который последним получил свое значение. Так, после присваивания значения компоненту radius не имеет смысла обращаться к массиву b[3].

Объединения могут входить в структуры и массивы, и наоборот. Запись доступа к элементу объединения, находящегося в структуре (как и структуры, находящейся в объединении), такая же, как и для вложенных структур.

Пример 9. Пусть дан массив структур

struct {

char *name;

int flags;

int utype;

union {

int ival;

float fval;

char *sval;

} u;

} symtab[NSYM];

к ival обращаются следующим образом: symtab[i].u.ival

а к первому символу строки sval можно обратиться любым из следующих двух способов:

*symtab[i].u.sval

symtab[i].u.sval[0]

Фактически объединение — это структура, все элементы которой имеют нулевое смещение относительно ее базового адреса и размер которой позволяет поместиться в ней самому большому ее элементу, а выравнивание этой структуры удовлетворяет всем типам объединения.

Операции, применимые к структурам, годятся и для объединений, т. е. законны присваивание объединения и копирование его как единого целого, взятие адреса от объединения и доступ к отдельным его элементам.

Инициализировать объединение можно только значением, имеющим тип его первого элемента; таким образом, упомянутую выше переменную u можно инициализировать лишь значением типа

Вход на сайт

Поиск

Календарь
«  Май 2024  »
ПнВтСрЧтПтСбВс
  12345
6789101112
13141516171819
20212223242526
2728293031

Архив записей

Друзья сайта
  • Официальный блог
  • Сообщество uCoz
  • FAQ по системе
  • Инструкции для uCoz

  • Copyright Fatima_Zh © 2024Бесплатный хостинг uCoz