Последнее обновление: 09.10.2017

Ранее для определения классов мы использовали ключевое слово class . Однако C++ предоставляет еще один способ для определения пользовательских типов, который заключается в использовании структур. Данный способ был унаследован языком С++ еще от языка Си.

Структура в языке C++ представляет собой производный тип данных, который представляет какую-то определенную сущность, также как и класс. Нередко структуры применителько к С++ также называют классами. И в реальности различия между ними не такие большие.

Для определения структуры применяется ключевое слово struct , а сам формат определения выглядит следующим образом:

Struct имя_структуры { компоненты_структуры };

Имя_структуры представляет произвольный идентификатор, к которому применяются те же правила, что и при наименовании переменных.

После имени структуры в фигурных скобках помещаются Компоненты_структуры , которые представляют набор описаний объектов и функций, которые составляют структуру.

Например, определим простейшую структуру:

#include #include struct person { int age; std::string name; }; int main() { person tom; tom.name = "Tom"; tom.age = 34; std::cout << "Name: " << tom.name << "\tAge: " << tom.age << std::endl; return 0; }

Здесь определена структура person , которая имеет два элемента: age (представляет тип int) и name (представляет тип string).

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

Имя_переменной_структуры.имя_элемента

По сути структура похожа на класс, то есть с помощью структур также можно определять сущности для использования в программе. В то же время все члены структуры, для которых не используется спецификатор доступа (public, private), по умолчанию являются открытыми (public). Тогда как в классе все его члены, для которых не указан спецификатор доступа, являются закрытыми (private).

Кроме того мы можем инициализировать структуру, присвоив ее переменным значения с помощью синтаксиса инициализации:

Person tom = { 34, "Tom" };

Инициализация структур аналогична инициализации массивов: в фигурных скобках передаются значения для элементов структуры по порядку. Так как в структуре person первым определено свойство, которое представляет тип int - число, то в фигурных скобках вначале идет число. И так далее для всех элементов структуры по порядку.

При этом любой класс мы можем представить в виде структуры и наоборот. Возьмем, к примеру, следующий класс:

Class Person { public: Person(std::string n, int a) { name = n; age = a; } void move() { std::cout << name << " is moving" << std::endl; } void setAge(int a) { if (a > 0 && a < 100) age = a; } std::string getName() { return name; } int getAge() { return age; } private: std::string name; int age; };

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

#include #include struct user { public: user(std::string n, int a) { name = n; age = a; } void move() { std::cout << name << " is moving" << std::endl; } void setAge(int a) { if (a > 0 && a < 100) age = a; } std::string getName() { return name; } int getAge() { return age; } private: std::string name; int age; }; int main() { user tom("Tom", 22); std::cout << "Name: " << tom.getName() << "\tAge: " << tom.getAge() << std::endl; tom.setAge(31); std::cout << "Name: " << tom.getName() << "\tAge: " << tom.getAge() << std::endl; return 0; }

И в плане конечного результата программы мы не увидели бы никакой разницы.

Когда использовать структуры? Как правило, структуры используются для описания таких данных, которые имеют только набор публичных атрибутов - открытых переменных. Например, как та же структура person, которая была определена в начале статьи. Иногда подобные сущности еще называют аггрегатными классами (aggregate classes).

Пока мы рассматривали одну сложную структуру (сложный тип) - массив; одним из основных свойств массива является однотипность его компонент. Многие информационно-логические задачи связаны с обработкой документов, содержащих в себе информация разного типа (числовую, символьную и т. д.) Примеры таких документов: пла­тежные ведомости (фамилии и имена - символьная информация, де­нежные суммы - числовая), карточки больных в поликлинике, библио­течная информация. Для программирования алгоритмов обработки такой информации необходимо иметь сложный тип, объединяющий разнотипные компоненты. Таким типом является структура в Си (в Паскале запись).

Структурная переменная, или просто структура, состоит из не­скольких переменных (называемых полями), возможно, разного типа.

Структура

тип "структура" (шаблон)

переменная типа "структура "

Описание шаблона :

Описание структурной переменной

typedef struct { Тип1 Список1ИменПолей;

struct ИмяШаблона ИмяПеременной

Тип2 Список2ИменПолей;

ТипN СписокNИменПолей;

ключевое struct слово не нужно пpи

} ИмяШаблона

использовании typedef

или struct ИмяШаблона

{ Тип1 Список1ИменПолей;¦

Тип2 Список2ИменПолей;

ТипN СписокNИменПолей;

typedef struct {char author; char title;/*описание*/

int year; float price} BOOK; /*шаблона BOOK*/

/*или можно описать тот же самый шаблон так:

struct BOOK {char author; char title;

int year; float price} ;*/

struct BOOK b;/*описание структурной переменной b*/

Память, занимаемая структурой, равна сумме объемов памяти полей (если исключить из рассмотрения особенности, связанные с выравниванием). В любом случае для определения размера памяти структуры можно использовать операцию sizeof(). Шаблон ВООК, на­пример, описывает структуру размером памяти 70.

Обращение к полю структурной переменной:

ИмяСтруктуры.ИмяПоля или АдресСтруктуры ->ИмяПоля

. (точка) и ->являются операциями, соответственно, прямого и косвенного выбора компоненты структурированной переменной.

Например,

struct BOOK a,*pnta=&a;...

a.author="Byron"; pnta->author="Byron"; /*эквивалентные опера­торы*/

Пример. Задача примера 2 п.3.1.4 в нижеприведенной про­грамме выполнена с использованием структур (а не строк).

#include

#include

#include

{ /*структура сведений об игрушках*/

typedef struct {int nu;/*номер*/

char name;/*наименование*/

int minage,maxage;/*мин. и макс. возраст ребенка*/

double rub /*стоимость*/;}TOYS;

TOYS toy;/*переменная типа записьTOYS */

double max; /*максимальная стоимость*/

char namemax;/*название самого дорогого конструктора*/

int n /*число игрушек*/,i/*номер игрушки*/;

puts("Введите число наименований игpушек");

for (i=0; i

/*в цикле ввод сведений об игрушках и проверка условий*/

fflush(stdin); /*очистка буфера устройства ввода послеscanf */

printf(" Введите сведения об игpушке с номеpом %2d\n",toy.nu);

puts("наименование");

puts("мин. и макс. возpаст и стоимость");

scanf("%d%d%lf",&toy.minage,&toy.maxage,&toy.rub);

if ((strstr(toy.name,"констpуктоp")!=NULL ||

strstr(toy.name,"Констpуктоp")!=NULL) &&

(toy.maxage <= 7) &&

(toy.rub>max))

strcpy(namemax,toy.name);

puts(" Констpуктоpов для детей до семи лет нет");

{ printf("Cамый доpогой констpуктоp для детей до семи лет\n");

printf(" %s стоит %8.0f pублей\n",namemax,max);

В Си существует еще один сложный тип, описание которого формально похоже на структуру. Это тип (и переменная) объедине­ние .

Объединение - это переменная, содержащая поля разного типа, помещаемые в одно и то же место памяти. По существу объедине­ние дает способ различной интерпретация содержимого памяти. Описание шаблона (типа) объединения и переменной этого типа выполняется также, как для структуры, только вместо ключевого слова struct используетсяunion . Размер памяти, занимаемой объе­динением, равен максимальному из размеров полей.

А теперь только представьте — вы сами можете создавать, своего рода, типы данных, которые вам необходимы и с которыми вам будет удобно работать! И это несложно!

Структура — это, некое объединение различных переменных (даже с разными типами данных), которому можно присвоить имя. Например можно объединить данные об объекте Дом: город (в котором дом находится), улица, количество квартир, интернет(проведен или нет) и т.д. в одной структуре. В общем, можно собрать в одну совокупность данные обо всем, что угодно, точнее обо всем, что необходимо конкретному программисту. Всем сразу стало понятно:)

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

#include using namespace std; struct building //Создаем структуру! { char *owner; //здесь будет храниться имя владельца char *city; //название города int amountRooms; //количество комнат float price; //цена }; int main() { setlocale (LC_ALL, "rus"); building apartment1; //это объект структуры с типом данных, именем структуры, building apartment1.owner = "Денис"; //заполняем данные о владельце и т.д. apartment1.city = "Симферополь"; apartment1.amountRooms = 5; apartment1.price = 150000; cout << "Владелец квартиры: " << apartment1.owner << endl; cout << "Квартира находится в городе: " << apartment1.city << endl; cout << "Количество комнат: " << apartment1.amountRooms << endl; cout << "Стоимость: " << apartment1.price << " $" << endl; return 0; }

В строках 4 — 10 мы создаем структуру. Чтобы ее объявить используем зарезервированное слово struct и даем ей любое, желательно логичное, имя. В нашем случае — building . С правилами именования переменных, вы можете ознакомиться в этой статье . Далее открываем фигурную скобку { , перечисляем 4 элемента структуры через точку с запятой; , закрываем фигурную скобку } и в завершении ставим точку с запятой; . Это будет нашим шаблоном (формой) структуры.

В строке 16 объявляем объект структуры. Как и для обычных переменных, необходимо объявить тип данных. В этом качестве выступит имя нашей созданной структуры — building .

Как же заполнить данными (инициализировать) элементы структуры? Синтаксис таков: Имя объекта далее оператор точка. и имя элемента структуры. Например: apartment1.owner . Таким образом, в строках 18-21 присваиваем данные элементам структуры.

И так, данные мы внесли. Следующий вопрос: «Как к ним обратиться, как работать и использовать их в программе?» Ответ — «Очень просто — так же, как и при инициализации, используя точку. и имя элемента структуры». В строках 23 — 26 выводим заполненные элементы структуры на экран.

И вот что мы увидим в результате, когда скомпилируем нашу программу:

Владелец квартиры: Денис Квартира находится в городе: Симферополь Количество комнат: 5 Стоимость: 150000 $

Что ещё важно знать:

  • Объект структуры можно объявить до функции main() . Это выглядело бы так:
struct building { char *owner char *city; int amountRooms; float price; }apartment1; //объявление объекта типа building
  • Инициализировать структуру можно и таким способом:
building apartment1 = {"Денис", "Симферополь", 5, 150000};

но так делают крайне редко;

  • Структуру можно вкладывать в другие структуры (это мы рассмотрим в следующем примере).

Дополним предыдущий пример, чтобы увидеть дополнительные возможности работы со структурами.

Пример:

#include using namespace std; struct date //создаем еще одну структуру, чтобы вложить ее в структуру building // дата постройки { char *month; // Месяц постройки дома int year; // Год }; struct building { char *owner; char *city; int amountRooms; float price; date built; //вкладываем одну структуру в определение второй }; void show(building object) //создаем функцию, которая принимает структуру, как параметр { cout << "Владелец квартиры: " << object.owner << endl; cout << "Квартира находится в городе: " << object.city << endl; cout << "Количество комнат: " << object.amountRooms << endl; cout << "Стоимость: " << object.price << " $" << endl; cout << "Дата постройки: " << object.built.month << " " << object.built.year << endl; } int main() { setlocale (LC_ALL, "rus"); building apartment1; apartment1.owner = "Денис"; apartment1.city = "Симферополь"; apartment1.amountRooms = 5; apartment1.price = 150000; apartment1.built.month = "январь"; apartment1.built.year = 2013; struct building *pApartment; //это указатель на структуру pApartment = &apartment1; //Обратите внимание, как нужно обращаться к элементу структуры через указатель //используем оператор -> cout << "Владелец квартиры: " << pApartment->owner << endl; cout << "Квартира находится в городе: " << pApartment->city << endl; cout << "Количество комнат: " << pApartment->amountRooms << endl; cout << "Стоимость: " << pApartment->price << " $" << endl; cout << "Дата постройки: " << pApartment->built.month << " " << pApartment->built.year << "\n\n\n"; building apartment2; //создаем и заполняем второй объект структуры apartment2.owner = "Игорь"; apartment2.city = "Киев"; apartment2.amountRooms = 4; apartment2.price = 300000; apartment2.built.month = "январь"; apartment2.built.year = 2012; building apartment3 = apartment2; //создаем третий объект структуры и присваиваем ему данные объекта apartment2 show(apartment3); cout << endl << endl; return 0; }

Коментарии по коду программы:

Строка 17 — создание объекта built типа date в определении структуры building . Строки 42 — 43 : создаем указатель на структуру struct building *pApartment; и далее присваиваем ему адрес уже созданного и заполненного данными объекта pApartment = &apartment1; . Обращаясь к элементам структуры через указатель мы используем оператор -> (тире и знак >) . Это видно в строках 47 — 51.

В строке 62 показано, как можно инициализировать структуру. А именно, можно создать новый объект структуры и присвоить ему одним целым, уже созданный и заполненный данными, объект. В функцию show() передаем объект структуры, как параметр — строка 64. Результат:

Владелец квартиры: Денис
Квартира находится в городе: Симферополь
Количество комнат: 5
Стоимость: 150000 $
Дата постройки: январь 2013
Владелец квартиры: Игорь
Квартира находится в городе: Киев
Количество комнат: 4
Стоимость: 300000 $
Дата постройки: январь 2012
Для продолжения нажмите любую клавишу. . .

Разобрав этот пример, мы увидели на практике следующее:

  • структуру можно вкладывать в другую структуру;
  • увидели, как создаётся указатель на структуру;
  • как нужно обращаться к элементу структуры через указатель. А именно, используя оператор -> ; В примере это было так: apartment0->owner , но можно и так (*apartment0).owner . Круглые скобки, во втором случае, обязательны.
  • данные одной структуры можно присвоить другой структуре;
  • можно структуру передать в функцию, как параметр (кстати, элементы структуры так же можно передавать в функцию, как параметры).

В дополнение ко всему, следует отметить, что функции могут так же возвращать структуры в результате своей работы. Например:

Building Set() { building object; // формирование объекта //... код функции return object; }

Вот так, вкратце, мы познакомились со структурами в языке С++, попрактиковались на примерах и узнали основы. Это только начало!

Структура - это удобное хранилище для разнородных данных, которые хочется объединить. К примеру, вы можете создать структуру, описывающую параметры вашего устройства - сетевые настройки, таймаут спящего режима, его идентификатор и прочее подобное, типа какой-нибудь строки приветствия и состояния светодиода. Раз все параметры будут храниться в одном месте - они всегда будут на виду, да и нормальные IDE будут вам подсказывать поля структуры при обращении к ним. Ещё мы рассмотрим хранение и восстановление структур из архива, а также их передачу по сети.

Объявление такой структуры:

Struct { uint32_t ID; char IP; uint16_t timeout; bool led; char text; } params;

Как это работает?

В си довольно удобный синтаксис, в том плане что многие вещи записываются как «тип_данных переменная», начиная с «int i» заканчивая «void main() {}». Так и здесь, кодовое слово struct начинает объявление структуры, и весь кусок кода «struct { … }» просто задаёт новый тип. Соответственно, params - это уже готовая переменная (экземпляр типа), которую можно использовать. Внутри фигурных скобок перечислены все поля структуры, которые потом будут доступны так: params.ID или params.IP. Длина полей должна быть фиксированной, поэтому нельзя использовать строки вида *text, только массивы вида text.

Можно было сделать немного иначе: объявить только тип, а переменную завести позже. Для этого мы использовали бы ключевое слово typedef и написали так:

Typedef struct { uint32_t ID; char IP; uint16_t timeout; bool led; char text; } params_struct; params_struct params;

Так появляется возможность оставить все объявления структурных типов в отдельном файле (header), а в главном файле просто использовать уже готовые структурные типы для объявления структур прямо по месту.

Конечно, в обоих вариантах вы можете объявить сколько угодно экземпляров структур, или создать массив из них:

Struct { uint32_t ID; char IP; uint16_t timeout; bool led; char text; } params1, params2, params;

Вариант с массивом особенно удобен для сервера в клиент-серверной топологии сети - на каждом клиенте хранятся в структуре его собственные параметры, а на мастер-устройстве располагается таблица параметров всех клиентов в виде массива структур.

В принципе, ничего сложного в структурах нет, а с темой серверов и клиентов мы плавно подошли к более интересной теме:

Хранение, передача и синхронизация структур

Для многих будет удивлением то, что данные структуры хранятся в памяти в виде плоского списка, все поля структуры просто идут в памяти друг за другом. Поэтому становится возможным обращаться с этой структурой как с простым массивом байт! Проверим, создадим массив «поверх» этой структуры.

Начальное смещение получим так:

Char *Bytes = ¶ms;

мы объявили указатель char и поместили в него адрес params. Теперь Bytes указывает на первый байт структуры, и при последовательном чтении мы побайтно прочитаем всю структуру. Но сколько байт нужно прочитать? Для этого рассмотрим две интересных функции.

sizeof и offsetof

Это даже не функции, а встроенные макросы языка Си. Начнём с более простой, sizeof .

Компилятор заменяет все записи вида sizeof X на значение длины Х. В качестве X может выступать как тип, так и экзмепляр типа, т.е. в нашем случае можно подставить в sizeof и тип структуры (если мы его заводили с помощью typedef), и саму переменную структуры так: sizeof params_struct или sizeof params. Она пройдёт по всем полям структуры, сложит их длины и отдаст сумму, которая и будет длиной структуры.

offsetof - настоящий макрос, который принимает два параметра (структуру _s_ и поле _m_ в ней) и отдаёт положение этого поля в структуре, его смещение относительно начала структуры. Выглядит этот макрос очень просто:

Offsetof(s, m) (size_t)&(((s *)0)-›m).

Как он работает?

  1. Берём число 0
  2. Преобразуем его к типу «указатель на структуру s»: (s*)0
  3. Обращаемся к полю m из этой структуры: ((s*)0)->m
  4. Вычисляем его адрес: &(((s*)0)->m)
  5. Преобразуем адрес к целому числу: (size_t)&(((s*)0)->m)

Магия именно в первом шаге, в котором мы берём 0. Благодаря этому на четвёртом шаге абсолютный адрес поля, вычисленный компилятором, оказывается отсчитан относительно начала структуры - структуру-то мы положили в адрес 0. Таким образом, после выполнения этого макроса мы реально имеем смещение поля относительно начала структуры. Понятно, что этот макрос правильно определит смещения даже в сложных и вложенных структурах.

Здесь нужно сделать небольшое отступление. Дело в том, что я рассматривал самый простой случай, когда поля упакованы точно вслед друг за другом. Есть и другие методы упаковки, которые называются «выравнивание». К примеру, можно выдавать каждому полю «слот», кратный 4 байтам, или 8 байтам. Тогда даже char будет занимать 8 байт, и общий размер структуры вырастет, а все смещения сдвинутся и станут кратны выравниванию. Эта штука полезна при программировании для компьютера, поскольку из-за грануляции ОЗУ процессор гораздо быстрее умеет извлекать из памяти выровненные данные, ему требуется на это меньше операций.

Работа с массивом из структуры

Окей, теперь мы умеем представлять любую структуру в виде массива байт, и обратно. Вы поняли фишку? У нас теперь одна и та же область памяти имеет роли «структура» и «массив». Изменяем что-то в структуре - меняется массив, меняем массив - меняется структура.

В этом - суть процесса! У нас нет отдельного массива, потому что сама структура - это уже массив, и мы просто обращаемся к памяти разными методами. И у нас нет никаких копирующих циклов по полям или по байтам, этот цикл будет уже сразу в функции передачи.

Теперь осталось лишь научиться удобно с этим всем работать.

Хранение и передача структуры

Чтобы создать архивную копию структуры, для передачи по сети или для складывания её в надёжное место - отдайте в вашу функцию передачи данных адрес этого массива. К примеру, моя функция записи массива данных в EEPROM выглядит так: I2C_burst_write (I2Cx, HW_address, addr, n_data, *data). Вам просто нужно вместо n_data передать sizeof params, а вместо *data - ¶ms:

I2C_burst_write (I2Cx, HW_address, addr, sizeof params, ¶ms)

Функции передачи данных по сети обычно выглядят примерно так же. В качестве данных передавайте ¶ms, а в качестве длины данных - sizeof params.

Приём и восстановление структуры

Всё точно так же. Моя функция чтения массива из EEPROM: I2C_burst_read (I2Cx, HW_address, addr, n_data, *data). n_data = sizeof params, *data = ¶ms:

I2C_burst_read (I2Cx, HW_address, addr, sizeof params, ¶ms)

Не забывайте, что вы сразу пишете принятые байты непосредственно в структуру. При медленной или ненадёжной передаче имеет смысл записать данные во временный буфер, и после их проверки передать их в структуру через

Memcpy(¶ms, &temp_buffer, sizeof params).

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

Хранение/восстановление отдельных полей

И зачем же мы так долго рассматривали макрос offsetof? Его очень удобно использовать для чтения и записи отдельных полей структуры, например так:

I2C_burst_write (I2Cx, HW_address, addr + offsetof(params, IP), sizeof params.IP, ¶ms.IP) I2C_burst_read (I2Cx, HW_address, addr + offsetof(params, IP), sizeof params.IP, ¶ms.IP)

Ну и вообще, было бы неплохо сделать удобные макросы-обёртки для этой цели.

#define store(structure, field) I2C_burst_write (I2Cx, HW_address, addr + offsetof(structure, field), sizeof(structure.field), &(structure.field)) #define load(structure, field) I2C_burst_read (I2Cx, HW_address, addr + offsetof(structure, field), sizeof(structure.field), &(structure.field))

Тип переменной определяет: её размер в памяти, тип данных, которые она может хранить и операции, которые можно производить с этой переменной.

Тип данных является категорией. В языке С++ программист может создать любой тип данных на основе базовых типов. Новые типы данных необходимо создавать для решения конкретных практических задач. Например: реализация работы деканата.

Успех программы часто зависит от удачного выбора способа представления данных. С помощью структур возможно моделировать сложные объекты, возникающие при решении задач. Структуры представляют средство для доступа к записям, которые содержат поля одного или нескольких типов.

Для использования структуры необходимо:
1. установить шаблон для структуры
2. объявить переменную, соответствующую этому шаблону
3. осуществить доступ к компонентам структуры.

Шаблон структуры

Шаблон - это схема, описывающая содержание структуры. Установка структурного шаблона телефонный справочник:

struct sprav {
char fio;
long num;
};

Данный шаблон описывает структуру с именем типа структуры sprav, состоящую из двух компонентов: строки fio и целой переменной num типа long. Имя типа структуры sprav необязательно и используется для ссылки на эту структуру. Компоненты структуры - данные любого типа, включая и другие структуры. Имя внутри структуры может быть таким же, как имя объекта вне структуры. Если шаблон описан внутри функции - он доступен только этой функции, если шаблон описан вне функции - он доступен любой функции программы. Установка шаблона не вызывает никаких действий в программе.

Структурные переменные

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

1. Установить структурный шаблон:

struct sprav {
char fio;
long num;
};

Объявить простую переменную, массив структур, указатель на структуру: struct sprav tel1, tel2, *tel3;

2. Установить структурный шаблон с помощью макроопределения:

#define SPRAV struct sprav
SPRAV {
char fio;
long num;
};

Объявить переменные:

SPRAV sp1, sp2, *sp3;

3. Объявить переменные одновременно с установкой шаблона (если на данную структуру вы больше не ссылаетесь):

struct {
char fio;
long num;
} tel1, tel2, *tel3;

4. Ввести новый тип данных (TEL)-структура определенного вида:

typedef struct {
char fio;
long num;
} TEL;

Объявить переменные нового типа:

TEL tel1, tel2, *tel3;

Если программа достаточно объемна, представляется более удобным четвертый способ.

Инициализация структуры

Инициализировать можно только внешние или статические структуры.

static struct {
char fio;
long num;
} tel={
"Иванов Ф.А.", 456756,
"Петров В.П.", 632345
};

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

Доступ к компонентам структуры продемонстрируем с помощью примеров.

/* Обращение к элементам структуры через имя переменной */
#include
#include
void main(void)
{
struct{
char fio; /* фамилия */
long num; /* телефон */
} tel1, tel2;

puts("введите фио абонента-");
gets(tel1.fio);
puts("введите его номер-");
scanf("%ld",&tel1.num);
tel2=tel1; /* нельзя так же сравнивать структуры */
puts("Введено:");
printf("Фамилия:%s номер: %ld\n",tel2.fio,tel2.num);
}

/* Динамическое выделение памяти для структуры */

#include
#include
#include
struct sprav {
char fio;
long num;
};

void main(void)
{
struct sprav *tel1, *tel2;

clrscr();
/* Выделение памяти для структуры */
tel1=(struct sprav *)malloc(sizeof(struct sprav));
tel2=(struct sprav *)malloc(sizeof(struct sprav));

gets(tel1->fio);
puts("введите его номер-");
scanf("%ld",&tel1->num);
*tel2= *tel1;
puts("Введено:");
printf("Фамилия:%s номер: %ld\n",(*tel2).fio,(*tel2).num);
}

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

/* Массив структур. Обращение к элементам структуры через */
/* имя элемента массива */
#include
#include
#include
#define SPRAV struct sprav

void main(void)
{
SPRAV{
char fio;
long num;
};

SPRAV tel; /* массив структур - 5 элементов */
char fio_tek;
int i;

clrscr();
/* ввод данных в массив структур */
for(i=0; i<5; i++)
{
puts("введите фио абонента-");
gets(tel[i].fio);
puts("введите его номер-");
scanf("%ld",&tel[i].num);
getchar();
}

gets(fio_tek);
/* поиск структуры по фамилии абонента */
for(i=0; i<5; i++)
if(!strcmp(fio_tek,tel[i].fio)) break;
if(i!=5) /* цикл закончен по break */

tel[i].num);
else /* цикл выполнился полностью */
puts("Абонент не найден");
}

/* Массив структур. Память выделяется динамически. */
/* Обращение к элементам структуры через указатель */
#include
#include
#include
#include

typedef struct{
char fio;
long num;
} TEL;

void main(void)
{
TEL *tel;
char fio_tek;
int i;

clrscr();
/* Выделение памяти для массива - 3 элемента */
tel=(TEL *)malloc(sizeof(TEL)*3);
for(i=0; i<3; i++)
{
puts("введите фио абонента-");
gets((tel+i)->fio);
puts("введите его номер-");
scanf("%ld",&(tel+i)->num);
getchar();
}
puts("Выбор телефона по фамилии");
gets(fio_tek);
for(i=0; i<5; i++,tel++)
if(!strcmp(fio_tek,tel->fio)) break;
if(i!=5)
printf("номер абонента %s равен %ld\n",fio_tek, \
tel->num);
else
puts("Абонент не найден");
}

Передача структуры в функцию

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

/* Передача структуры в функцию через указатель на структуру */
/* Определение комплексного числа через структуру и действия */
/* над комплексными числами (ввод, вывод, вычисление суммы) */

#include
typedef struct { float a; /* действительная часть */
float b; /* мнимая часть */
} COMPLEX;
void vvod(COMPLEX *,float,float);
void sum(COMPLEX *,COMPLEX *,COMPLEX *);
void out(COMPLEX *);
void main(void)
{
COMPLEX x,y,z;
vvod(&x,2.5,6.7);
vvod(&y,6.89,8.45);
puts("Введены числа:");
out(&x);
out(&y);
sum(&x,&y,&z);
puts("Сумма комплексных чисел равна:");
out(&z);
}
/* Вывод комплексного числа */
void out(COMPLEX *p)
{
printf("(%.2f,%.2f)\n", (*p).a,(*p).b);
return;
}

/* Вычисление суммы двух комплексных чисел */
void sum(COMPLEX *p1,COMPLEX *p2,COMPLEX *p3)
{
(*p3).a=(*p1).a+(*p2).a;
(*p3).b=(*p1).b+(*p2).b;
return;
}

/* Ввод значений для элементов структуры */
void vvod(COMPLEX *p,float a, float b)
{
p->a=a;
p->b=b;
return;
}

Вложенные структуры

Структура, являющаяся компонентом другой структуры, называется вложенной.

/* Даны четыре точки - центры четырех окружностей. Заполнить структуру окружность, если все окружности проходят через начало координат. */

#include
#include
#include
#include
struct POINT {
float x;
float y;
};
struct CIRCLE {
struct POINT point; /* вложенная структура */
double r;
} circle, *p;
void main (void)
{
int i,j;
float a,b,c,d;
clrscr();
gotoxy(17,1);
cputs("ВВЕДИТЕ КООРДИНАТЫ ТОЧЕК:\r\n");
for(i=0;i<2;i++)
{
cprintf ("\n\n ВВЕДИТЕ X: ");
cprintf ("X[%d]= ",i+1);
cscanf("%f",&circle[i].point.x);
cprintf ("\n ВВЕДИТЕ Y: ");
cprintf ("Y[%d]= ",i+1);
cscanf ("%f",&circle[i].point.y);
}
p=circle;
gotoxy(17,12);
cputs("РЕЗУЛЬТАТ:\r\n\n");
for(i=0;i<2;i++)
{
a=p->point.x;
b=p->point.y;
c=sqrt(a*a+b*b);
p->r=c;
cprintf("\nРАДИУС: %lf ЦЕНТР (%f,%f)\r\n",p->r,a,b);
p++;
}