Використання структур

Структура — це сукупність різнотипних елементів, яким присвоюється одне ім’я (воно може бути відсутнім), що займає одну ділянку пам’яті. Елементи, що складають структуру, на­зиваються полями.

Змінна типу структура, як і будь-яка змінна, повинна бути описана. Цей опис складається з двох кроків: опису шаблону (тобто складу) або типу структури та опису змінних структур­ного типу.

Синтаксис опису структури має вигляд:

struct [<ім’я структури>]
{ <тип 1> ім’я поля 1; 
  <тип 2> ім’я поля 2 . . .;
} р1, р2 . . .;

де struct — службове слово;

  <ім’я структури> — ім’я типу структура (може бути відсутнім);

  <тип 1>, <тип 2> — імена стандартних або визначених типів;

  ім’я поля 1, ім’я поля 2,… — імена полів структури;

  р1, р2 . . .; — імена змінних типу структура.

Наприклад, для знаходження середнього бала, отриманого студентами в період сесії з дисциплін «Математика», «Фізика» та «Програмування», визначимо таку структуру:

struct stud

{ char fam [25];            // фамилия и инициалы

int mat, fiz, prg;          // предметы

float sb;                    // средний балл 

 st1, st2;

Змінні st1 і st2 можна оголосити окремим оператором, наприклад:

struc stud stl st2;.

Ініціювання полів структури слід здійснювати або при її описі, або в тілі програми. При описі структури ініціювання полів виглядає, наприклад, так:

struct stud 

{ char fam [25]; 

int mat,fiz, prg;

float sb;

}

st1 = {"Кравченко И. С.", 4, 5, 5};

st2 = {"Тесленко А. М.", 3, 4, 5};

Якщо ініціювання виконується в тілі програми, то для звер­нення до імені поля треба спочатку записати ім’я структурної змінної, а потім ім’я поля. Ці обидва записи відокремлюються крапкою і являють собою складене ім’я.

Отже, у випадку появи змінної st1 у програмі для її ініцію­вання можна записати stud st1 = {“Кравченко И. С.”, 4, 5, 5}; або ініціювання виконується за допомогою складених полів. Роз­глянемо ілюстраційну програму:

#include <iostream.h>

#include <string.h>

#include <stdio.h>

#include <conio.h>

main ( )

{ struct stud     //-------------------- описание структуры

  { char fam [20]; 

    int mat, fiz, prg;

    float sb;

  } st1, st2;

  strcpy (st1.fam, "Кравченко И. С."); 

  st1 .mat = 4;

  st1 .fiz = 5;

  st1 .prg = 5;

  st1.sb = float (st1.fiz + st1.mat + st1.prg)/3;

  st2 = st1;  

  puts (st2.fam);      //---------------- вывод фамилии

  cout << st2.mat << st2.fiz << st2.prg << st2.sb << endl;

  getch();     //задержка экрана вывода результата

}

У наведеній програмі організується присвоювання всім по­лям структури st1 відповідних значень. Слід зауважити, що поле st1.fam одержує значення шляхом застосування функції strcpy (st1.fam, “Кравченко И. С.”);. Структурна змінна st2 того ж типу, що і st1, тому справедлива операція st2 = st1;.

Якщо функція використовує тільки один структурний тип, то цей тип можна оголосити без імені. Тоді раніше розглянуту структуру можна оголосити таким чином:

struct  

{ char fam [25];

  int mat, fiz, prg;     

  float sb; 

} stl, st2;

Коли при описі структур у деякій функції або в межах ви­димості змінних у різних функціях є багато (але не всі) одна­кових полів, то їх слід об’єднати в окрему структуру. Її можна застосовувати при описі інших структур, тобто поля структури можуть самі бути типу struct. Це називається вкладеністю структур — її можна використати, наприклад, якщо треба обробляти списки студентів та викладачів університету. Студентські списки містять дані: прізвище та ініціали, дата (день, місяць, рік) народження, група та середній бал успішності, а в списках викладачів присутні такі дані: прізвище, ініціали, дата народження, кафедра, посада. У процесі обробки списку студентів і списку викладачів можна оголосити відповідно такі структури:

struct stud                     

{ char fio [25];              

  int den, god;                     

  char mes [10];                 

  char grup;                        

  flout sb; } 

struct prep
{ char fio [25];
int den, god;
char mes [10];
char kaf, dolg;
}

В оголошених типах однакові поля можна об’єднати в ок­рему структуру і застосовувати її при описі інших типів. Поетапно це виглядає так:

  • загальна структура —

struct spd

{ char fio [25];

int den, god;

char mas[10]; }

  • структура для опису інформації про студентів —

struct stud

{ spd dr;

char grup;

float sb}

st1, st2;

  • структура для опису інформації про викладачів —

struct prep

{ spd dr;

char kaf [10];

char dolg [15];

} pr1, pr2;

У структурах stud і prep для оголошення поля, що містить дані про прізвище і дату народження, використовується раніше описаний тип spd. Тепер до поля fio, den, god, mes можна звер­нутися, використовуючи запис st1.dr.fio, наприклад, при зверненні до функції введення:

gets (st1.dr.fio);

або    gets (pr1.dr.fio);.

Після оголошення структурного типу змінних для роботи з їхніми полями можна застосовувати і покажчики, тоді опис структури матиме вигляд:

struct stud

{ char fam [25]; 

  int mat, fiz, prg;  

  float sb;

} st1, *pst;

Доступ до полів може здійснюватися двома способами:

  • з використанням операції розіменування «*», тобто
    gets ((*pst).fam);    (*pst).fiz = 5;
  • з використанням покажчика *->*, наприклад,
    gets (pst -> fam);      pst-> fiz = 5; тощо.

Крім того, до полів змінної st1 можна звертатися, вказую­чи поля через операцію «.», як це робилося раніше.

Дані типу структура можна об’єднати в масиви, тоді для розглянутого вище ілюстраційного прикладу з урахуванням кількості студентів, структуру можна записати так:

struct stud

{ char fam [20];

  int mat, fiz, prg;

  float sb;

} spis[15], *sp = &spis[0];.

У випадку, коли масив описується десь у тексті програми, тобто не саме після опису структури, його можна оголосити у вигляді: stud spis [15]; — масив типу структура з ім’ям stud, що містить відповідну інформацію про групу із 15 студентів.

Доступ до елементів масиву типу структура здійснюється із застосуванням індексу або через покажчик-константу, яким є ім’я масиву, тобто одним з таких способів:

strcpy (spis[1].fam, ” “);

spis[1].fiz = 5;

або

strcpy ((sp + 1) -> fam, ” “);

(sp + 1) -> fiz = 5;

або

strcpy ((*(spis + 1)).fam, ” “);

(*(sp + 1)).fiz = 5;.

У останньому виразі (*(spis + 1)).fiz = 5; потрібна зовнішня пара дужок, тому що операція «.» («крапка») має пріоритет вище, ніж операція розіменування «*».

Поля структури можуть також бути масивами. Наприклад, у розглянутій структурі stud можна оцінки з різних предметів об’єднати в масив. Тоді структуру слід описати у вигляді:

struct stud1

{ char fam [25]; 

int pred [3]; 

float sb 

} spis[15], *ps = &spis[0];.

Звернення до полів здійснюватиметься одним із способів:

((*ps).fam)

(ps->pred [0]),

наприклад,

gets ((*ps).fam);
       сіn >> ps -> pred[0] >> ps -> pred[1] >> ps -> pred[2];

або   сіn >> ps -> *(pred + 0) >> ps -> *(pred + 1) >> ps -> *(pred + 2).

Доцільно також зазначити, що бібліотека stdlib.h містить спеціальні функції для пошуку та сортування структурних змінних.

Розглянемо використання структурного типу на прикладах.

Приклад 8.1. Увести в комп’ютер відомість успішності групи сту­дентів, які здали сесію з дисциплін «Математика», «Фізика» і «Програмування», та обчислити:

  • середній бал кожного студента;
  • середній бал групи за кожним предметом;
  • вивести на екран прізвища відмінників з програмування.

Розглянемо перший варіант (Р8_1_1.СРР) реалізації постав­леної задачі, що може мати вигляд:

// Р8_1_ 1.СРР — обработка ведомости успеваемости группы студентов

//------------------------------- использование данных типа структура

#include <iostream.h>

#include <string.h>

#include <conio.h>

void main()

{ const n = 5;     //n — количество студентов  

int і;  

float sm, sf, sp;

/* sm, sf.sp — сумма оценок группы соответственно по математике, физике, программированию */

struct stud

{ char fam[25];   

int mat, fiz, prg;  

float sb; 

} ved[n];       //ved[n] — массив студентов (ведомость)   

sm = sf = sp = 0; 

//---- ввод и обработка информации о студентах и их успеваемости

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

{ cout <<"***** Введите информацию о "<< (i+1) << " студенте\n";   

{ cout<<<<"Введите фамилию и инициалы\n";    

gets(ved[i]. fam);    

cout <<"Оценки по матем., физике и программир.\n";     

cin >> ved[i].mat >> ved[i].fiz >> ved[i].prg;//--------------------- вычисление среднего балла студента за сессию    

ved[i].sb = (float(ved[i].mat + ved[i].fiz + ved[i].prg))/3;//--------------------- суммирование оценок в группе по предметам    

sm += ved[i].mat;     

sf += ved[i].fiz;     

sp += ved[i].prg;  

}

}

//--------------------- вывод результатов вычислений

cout << ''\n******* Результаты сессии\n";

cout.precision(3);

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

cout<<i+1<< " "<< ved[i].fam << " матем. = " << ved[i].mat << " физика = "<< ved[i].fiz << "програм. = " <<ved[i].prg << " ср. балл = " << ved[i].sb << "\n";

cout << "\n\nСредний балл группы по математике = "<< sm/n; 

cout << "\nСредний балл группы по физике = "<< sf/n;

cout << "\nСредний балл группы по программированию = "<<sp/n;

cout << "\n\n***** Отличники по программированию: \n"; 

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

if (ved[i].prg == 5) cout << ved[i].fam <<"\n";  

getch ();

}

Результати виконання програми:

***** Введите информацию о 1 студенте

Введите фамилию и инициалы

Адаменко Р. Л.

Оценки по матем., физике и программир.

4 5

***** Введите информацию о 2 студенте

Введите фамилию и инициалы

Бочаренко П. П.

Оценки но матем., физике и программир.

5 4

***** Введите информацию о 3 студенте

Введите фамилию и инициалы

Волошко А. А.

Оценки но матем., физике и программир.

4 3

***** Введите информацию о 4 студенте

Введите фамилию и инициалы

Долбня В. Е.

Оценки по матем., физике и программир.

5 5

***** Введите информацию о 5 студенте

Введите фамилию и инициалы

Ильченко Г. Г.

Оценки по матем., физике и программир.

3 3 4

******* Результаты сессия

Адаменко Р. Л. матем. = 4 физика = 4 програм. = 5 ср. балл = 4.33

Бочаренко П. П. матем. = 5 физика = 5 програм. = 4 ср. балл = 4.67

Волошко А. А. матем. = 4 физика = 4 прогам. = 3 ср. балл = 3.67

Долбня В. Е. матем. = 5 физика = 5 програм. =5 ср. балл = 5

Ильченко Е. Г. матем. = 3 физика = 3 програм. = 4 ср. балл = 3.33

Средний балл группы по математике = 4.2

Средний балл группы по физике = 4.2

Средний балл группы по программированию = 4.2

*******Отличники по программированию:

Адаменко Р.Л.

Долбня В.Е.

Наведемо другий варіант (див. Р8_1_2.СРР) розв’язання по­ставленої задачі з використанням покажчиків та структуру, яка містить поле оцінок з предметів у вигляді масиву.

//Р8_1_2. СРР — обработка ведомости успеваемости группы студентов

//---------------------------------------------- использование указателей

#include <iostream.h>

#include <string.h>

#include <conio.h>

void main()

{ const n = 5, k = 3;

/* n — количество студентов,k — количество оценок */  

int і, j;  

float sm, sf, sp;

/* sm, sf, sp — сумма оценок группы соответственно по математике, физике, программированию */

struct stud{ char fam[25];  

int ocen[3];          // ocen[3] — массив оценок  

float sb;

} ved[n], *pst =& ved[0]; //pst — указатель на массив

sm = sf = sp = 0;

//---- ввод и обработка информации о студентах и их успеваемости

for (і = 0; і < n; i++, pst++)

{ cout << "Введите информацию о " << (i+i) << " студенте\n";  

{ cout << "Введите фамилию и инициалы\n";    

gets ((* pst).fam);    

cout << "Оценки по матем., физике и программир. \n";

//-------------------- ввод оценок и подсчет среднего балла студента    

(*pst).sb=0;     

for (j = 0; j <3; j++)     

{ сіn >> (*pst).ocen[j];       

(*pst).sb += (*pst).ocen[j];    

}    

(*pst).sb=(*pst).sb/3;      //средний балл студента за сессию

//-------------- суммирование оценок группы студентов по предметам    

sm += (*pst).ocen[0];     

sf+= (*pst).ocen[1];    

sp += (*pst).ocen[2];   

}

}

//---------------------------------------- вывод результатов вычислений

pst -= n;  // pst=&ved[0];возвращение указателя на начало массива

cout.precision(3);

cout <<"****** Результаты сессии";

for (і = 0; і <n; і++, pst++)

cout <<і+1<<" "<<(*pst).fam<<" матем. = "<<(*pst).ocen[0]<<" физика = "<<(*pst).ocen[i]<<" програм. = "<<(*pst).ocen[2]<<" ср. балл = "<<(*pst).sb << "\n";

cout << "\n\nСредний балл группы по математике = "<< sm/n;

cout << "\nСредний балл группы по физике = "<< sf/n;

cout << "\nСредний балл группы по программированию = "<< sp/n;

cout << "\n\n*** Отличники по программированию: \n";

pst -= n;

for (і = 0; і < n; i++, pst++) 

 if ((*pst).ocen[2] == 5) cout << (*pst).fam <<"\n";

getch ();

}

Слід нагадати, що запис виразу з покажчиком, наприклад, (*pst).fam, рівнозначний запису pst -> fam.

В останніх версіях С++ до складу структури можна вклю­чати не тільки поля (тобто дані), але і функції обробки полів (методи) (див. розділ 9). Це вдало ілюструє третій варіант (див. Р8_1_3. СРР) розв’язання даного прикладу (середній бал групи з кожного предмета не обчислювався):

// Р8_1_3. СРР — обработка ведомости успеваемости группы студентов

//---------------------- структура содержит функции обработки полей

#include <iostream.h>

#include <string.h>

#include <conio.h>

void main()

{ const n=5;  

  int i;

//---------------------------------------------------- описание структуры

struct Tstud 

{ char fam[15];   

  int mat, fiz, prg;  

  float sb()          //------------ функция подсчета среднего балла  

  { float s = float(mat+fiz+prg)/3;      

    return s;}   

    void wod() //-------------------------- функция ввода данных   

    { cout<<" Введите фамилию\n";       

      cin>>fam; // фамилии вводятся без инициалов       

      cout<<"Enter ocenki\n";       

      сіn >> mat >> fiz >> prg;}   

    void vivod() //----------------------- функция вывода данных   

    { cout<<fam<<" mat="<<mat<<" fiz="<<fiz<" prg="<<prg; 

  } ved[n];

//--------------------------------------------------- ввод исходных данных 

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

  { cout << " Введите информацию о " << (i+1) << " студенте \n";     

    ved[i].vvod(); }

//--------------------------------- вывод результатов вычислений    

    cout << "******* Результаты сессии \n";     

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

    { ved[i].vivod();        

      cout.precision(3);       

      cout<<" sb="<<ved[i].sb() << endl; }    

    cout<<"\n*** Отличники по программированию: \n\n";    

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

      if (ved[i].prg == 5) cout << ved[i].fam << endl;    

  getch ();  
}

Наведемо приклад, в якому ініціювання масиву типу струк­тура здійснюється під час опису, а в процесі роботи з масивом ви­користовується поняття, що ім’я масиву є покажчиком на масив.

Приклад 8.2. Увести в комп’ютер таку інформацію про монітори: назву, термін гарантії, ціну. Відсортувати монітори за спаданням ціни і вивести відповідні повідомлення.

У програмі в рядках коментарів наведено інший спосіб за­пису звернення до полів структури.

/* Р8_2. СРР — обработка данных типа структура, работа с именем массива как указателем на массив */

/* сортировка информации о мониторах согласно уменьшению их цены (методом «пузырька») */

#include <iostream.h>

#include <conio.h>

const int n=6;

void main ()

{

  struct Monitor

  { char name[20]; //марка монитора    

    int garant; // срок гарантии

    float chena; // цена

  } mon[n]= {" Samsung 757 NF ", 36, 1100.1,

                   " Sony CPD-C520K", 36, 11753.4,

                   " Hansol H95S TFT ", 24, 4260.2,

                   " Samsung 757 DFX ", 12, 1054.5,

                   " LG T710H Flatron 36, 855.,

                   " Samsung 192V TFT ", 36, 4252.2 };

// инициализация массива mon[n] при описании

  int і, k;  cout << "\t ******* Spisok monitorov ******* \n";

//--------------------------- вывод информации о мониторах

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

  {

    cout<<(*(mon + i)).name; // cout<< (mоn + і) -> name;

   cout<<(*(mon + i)).garant; // cout<< (mоn + і) -> garant;

   cout<<(*(mon + i)).chena<< "\n";// cout<< (mоn + і)-> chena<< "\n";

  }

//------------------------ сортировка no убыванию стоимости

  Monitor rab; // rab — для перестановки элем. массива

  for (k = 1; k < n; k++)

  for (i = 0; і < n-k; i++)

    if ((*(mon + i)).chena < (*(mon + i+1)).chena)

//if ((mon + i) -> chena < (mon + i+1) -> chena)

  { rab = *(reon + i);

     * (mon+i) = *(mon+i+1);

     *(mon + i+1) = rab; }

cout << "\n\n\t ******* Sortirovka monitorov ******"\n ";

/*-------- вывод информации о мониторах отсортированной по убыванию стоимости */

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

   {

     cout<< " chena: ";

     cout<< (*(mon +i)).chena; // cout<< (топ + і) -> chena;

     cout<< " monitor ";

     cout<< (*(mon + i)).name; // cout<< (топ + і) -> name;

     cout<< " garantij: ";

     cout<< (*(mon + i)).garant <<"\n"; // cout<< (mоn + і) -> garant<< "\n";

   }

   getch();

}

Результати обчислень:

******* Spisok monitorov. *******

Samsung 757 NF 36 1100.1

Sony CPD-C520K 36 11753.4

Hansol H95S TFT 24 4260.2

Samsung 757 DFX 12 1054.5

LG T710H Flatron 24 855

Samsung 192V TFT 36 4252.2

******* Sortirovka monitorov *******

chena: 11753.4 monitor Sony CPD-C520K garantij: 36

chena: 4260.2 monitor Hansol H95S TFT garantij: 24

chena: 4252.2 monitor Samsung 192V TFT garantij: 36

chena: 1100.1 monitor Samsung 757 NF garantij: 36

chena: 1054.5 monitor Samsung 757 DFX garantij: 12

chena: 855 monitor LG T710H Flatron garantij: 24

 

Приклад 8.3. Обробити інформацію про групу студентів, що міс­тить прізвища, стать та середній бал успішності. Вивести повідомлен­ня, чий підсумковий середній бал вищий —юнаків чи дівчат.

Перший варіант програми розв’язання прикладу (див. Р8_3_1.СРР) передбачає, що стать студента при введенні інфор­мації позначається літерами «g» та «b» (від «girl» та«boy»).

// Р8 3 1.СРР — определение среднего балла девушек и юношей

/* программа без использования указателей и с использованием имени массива как указателя на массив */

#include<iostream.h>

#include<conio.h>

const int n=20;

void main()

{

   struct { char fam[20]; // фамилия

              char sex;        //пол

              float sb;         // средний балл

            } stud[n];         // массив студентов

  float sbg = 0, sbb = 0;

// sbg, sbb — средний балл соответственно девушек и юношей

  int і, kg = 0, kb = 0;

//kg, kb счетчики количества девушек и юношей в группе

//-------------------------------------- ввод исходной информации

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

  {

  cout << "Input fam"<< endl;

// ввод фамилии без инициалов

  сіn >> stud[i].fam;

// сіn >> (*(stud + i)).fam\ сіn >> (stud + i)-> fam;

cout <<"Input pol"<< endl;

сіn >> stud[i].sex;

//сіn>> (*(stud + i)).sex; сіn >> (stud + i) - > sex;

cout <<"Input sredniy ball"<< endl;

сіn >> stud[i].sb;

// сіn >> (*(stud + i)).sb; сіn >> (stud + і) -> sb;

}

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

if (stud[i].sex == 'g')

//if ((*(stud + i))sex == 'g'); if ((stud + i) ->sex == 'g');

/* определение количества девушек в группе и иx суммарного среднего балла */

{ kb++;

sbg += a[i].sb; //sbg +- ('(stud + i))sb; sbg += (stud + i) -> sb;

}

else

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

if (stud[i].sex == 'b')

// if (*(stud + і))sex == 'b');  if ((stud + i) -> sex == 'b');

{ km++;

     sbm += stud[i].sb;

//sbm += (*(stud + i)).sb;  sbm +- (stud + i) -> sb;

   }  cout.precision(2 );

cout << "Sredniy baO girls = " << sbg /kg << endl;

cout << "Sredniy ball boys = " << sbm/ kb << endl;

if (sbg / kg > sbb / kb)

cout << "sredn ball girls bolshe " << endl;

else

cout << "sredn ball mans bolshe" << endl;

  getch(); // задержка экрана

}

У рядках коментарів програми наведено інший спосіб запи­су звернення до полів структури для випадку, коли ім’я масиву використане як покажчик на масив.

Другий варіант розв’язання прикладу (див. Р8_3_2.СРР) містить змінну-покажчик та функцію порівняння рядків. При введенні інформації стать студента позначається рядками сим­волів відповідно  «girl» та «bоу».

/* Р8_3_2.СРР — определение среднего балла девушек и юношей с использованием указателей и функции сравнения строк strcmp() */

#include <iostream.h> 

#include <string.h> 

#include <conio.h>

const int n=20;

void main()

{ struct stud 

  { char fam[20];

    char sex[4];

    float sb; };

  stud a[n], *p(&a[0]);

  float sbg, sbb;

  int kg, kb, i;

//---------------------------------------- ввод информации

  for (і = 0; і < n; і++, р++)

  { cout << "Input fam"« endl;

    сіn >> (*p).fam;                         // сіn >> p->fam;

 cout << "Input pol"« endl;

    сіn >> (*p).sex;                         // сіn >> p->sex;

cout << "Input sredniy ball<< endl;

    сіn>> (*p).sb;                           //cin » p->sb; 

  }

  p-=n;       // возвращение указателя в начало массива

  kg = kb = sbg = sbb = 0; 

  for (і = 0; і < n; i++, p++) 

//функция strcmp() сравнивает две строки символов 

 if (strcmp((*p).sex, "girl") == 0)        //if (strcmp(p->sex, "girt') == 0) 

 { kg++;sbg += (*p).sb; }                   //sbg += p->sb;

 else

  if (strcmp((*p).sex, "boy") == 0)     //if (strcmp(p->sex, "man") == 0)

 { kb++;

    sbb += (*p).sb; }                   // sbb += p->sb;

  cout.precision(2);

  cout << "Sredniy ball girls = " << sbg /kg << endl; 

  cout << "Sredniy ball boys = " << sbm/ kb << endl;

  if (sbg/kg > sbb/kb) 

  cout << "sredn ball bolshe "<< endl; 

  else 

  cout << "sredn ball boys bolshe "<< endl; 

  getch (); 

}

У програмі для реалізації функції порівняння рядків strcmp( ) використана бібліотека string.h.

Залишити відповідь

Ваша e-mail адреса не оприлюднюватиметься. Обов’язкові поля позначені *