
C _Учебник_МОНУ
.pdf
Типи опрацювання дати і часу |
359 |
if (!m) {ShowMessage("Немає такого маршруту"); return;} dt1 = Edit11->Text; // Ввести дату відправлення // Визначити дату і час прибуття до кінцевого пункту призначення
dt2 = dt1 + TTime(w[m].start) + TTime(w[m].dli); Edit12->Text = dt2.DateTimeString();
}
Інші приклади використання даних TDateTime буде наведено при вивченні файлів.
У C++ Builder для роботи з датою і часом існують спеціальні компоненти MonthCalendar та DateTimePicker, розміщені на сторінці Win32. Вони власним зовнішньою формою нагадують календар. Основна візуальна відмінність між ними полягає у тому, що компонент DateTimePicker у своєму звичному стані є згорнутий, а календар з‟являється лише при клацанні на відповідному трикутничкові, у той час як компонент MonthCalendar завжди є повністю видимий на формі.
Крім того, DateTimePicker дозволяє безпомилково з точки зору синтаксису вводити не лише дати, як для компонента MonthCalendar, а й час. Властивість Kind визначає режим роботи: dtkDate() – введення дати, dtkTime() – введення часу, а властивість DateFormat задає скорочений (dfShort) чи повний (dfLong) формат відбиття дати. Але головними властивостями компонента MonthCalendar є Date та Time, за допомогою якої користувач зчитує уведені значення дати чи дати і часу залежно від режиму роботи. Тип цих властивостей
– TDateTime.
Проте компонент MonthCalendar має свої додаткові можливості: можна дозволити множинний вибір дат у деякому діапазоні (властивість MultiSelect), можна виводити у календарі номера тижнів з початку року (властивість WeekNumbers), перебудовувати календар, задаючи перший день кожного тижня (властивість FirstDayOfWeek) тощо.
Крім наведених існує ще один компонент-календар CCalendar (закладка Samples), який має три окремі властивості цілого типу Year – рік, Month – місяць, Day – день, з якими іноді зручніше мати справу, аніж з типом TDateTime. Щоби цей компонент показував календар на потрібний місяць потрібного року, слід попередньо задати значення властивостей Year та Month.

360 Розділ 10
void __fastcall TForm1::MonthCalendar1Click(TObject *Sender) { Edit1->Text=MonthCalendar1->Date; } //------------------------------------------------------------
void __fastcall TForm1::DateTimePicker1CloseUp(TObject *Sender) { Edit1->Text=DateTimePicker1->DateTime; } //------------------------------------------------------------
void __fastcall TForm1::CCalendar1Change(TObject *Sender) { Edit1->Text=IntToStr(CCalendar1->Day)+"."+
IntToStr(CCalendar1->Month)+"."+ IntToStr(CCalendar1->Year);
}
Питання та завдання для самоконтролю
1) Які заголовні файли слід долучити до програмного проекту для використання функцій опрацювання дати і часу:
а) у консольному режимі; б) у додатку С++ Builder?
2)В який спосіб організовано тип TDateTime?
3)Наведіть функції та методи С++ Builder, які визначають поточну дату.
4)Наведіть функції та методи С++ Builder, які визначають поточний час.
5)Яка функція С++ Builder дозволяє визначити і дату і час?
6)В який спосіб можна за датою визначити номер тижня року?
7)Яка функція дозволяє за датою визначити день тижня?
8)Запишіть функцію, яка дозволяє визначити належність дати до високосного року.
9)В який спосіб можна збільшити (чи зменшити) дату:
а) на один день; б) на один тиждень; в) на один місяць?
10)Наведіть функцію, яка визначає належність зазначеного часу до другої половини дня.
11)Які функції дозволяють перетворити значення типу TDateTime на ря-
док?
12)Яка функція дозволяє перетворити рядок на значення типу TDateTime
зперевіркою можливості перетворення?
13)Запишіть функцію, яка формує значення типу TDateTime на початок місяця заданої дати.
14)Наведіть функцію, яка дозволяє змінити на нове значення день дати.
15)Яка функція дозволяє порівняти дві дати?
16)Запишіть функцію, яка дозволяє визначити годину заданого часу.
17)Яка функція із заданої дати виокремлює значення року, місяця і дня в окремі змінні?
18)Наведіть функції, які дозволяють перетворити рік, місяць і день ціло-
го типу на значення типу TDateTime.
19)В який спосіб можна перевірити правильність перетворення року, місяця і дня цілого типу на значення типу TDateTime?
20)Назвіть спеціальні компоненти С++ Builder для роботи з датою і часом.
Розділ 11
Типи користувача
11.1 Перейменовування типів (typedef)
С++ надає можливість моделювання нових типів даних на базі вже існуючих типів. Як приклад доцільності застосовування таких типів розглянемо оголошення
double A[5][7];
Тут оголошується змінна А як матриця (двовимірний масив) дійсних чисел розмірності 5 рядків і 7 стовпчиків. Якщо у програмі оголошується кілька аналогічних масивів або якщо програма містить функції, параметрами яких є матриця 57, зручніше є створити окремий тип “матриця” для оголошення змінної А, щоб уникнути помилок і покращити читабельність тексту програми. У мові С++ для створювання типів користувача на базі вже існуючих типів використовується службове слово typedef (від англійської “type definition” – означення типу). Отже оголошення описаного типу matrica може мати вигляд
typedef double matrica [5][7];
Цей тип є синонімом дійсного двовимірного масиву розмірності 5 7, і його можна використовувати як звичайний тип для оголошення змінної А:
matrica A;
Це оголошення означає, що змінна А має тип matrica, тобто є дійсним двовимірним масивом. Після перейменування типу можна використовувати новий тип matrica для оголошення всіх дійсних двовимірних масивів 5 7, наприклад:
matrica B, С; |
// В і С – матриці 5 7 |
double sumr(matrica С); |
/* Заголовок функції, параметром якої є |
|
матриця 5 7 */ |
matrica M[4]; |
// Масив з 4-х матриць |
Порівняймо останнє оголошення з еквівалентним до нього, але менш зручним, без перейменування типу:
double M[4][5][7];
Перейменування типу у загальному вигляді має синтаксис typedef <тип> <нове_і‟мя> [<розмірність>];
Тут квадратні дужки є елементом синтаксису. Розмірність може бути відсутньою. Наведемо приклади перейменовування типів:
typedef unsigned char byte; /* Перейменування типу
|
“беззнаковий char” на byte */ |
typedef char MS[50]; |
/* Перейменування типу “рядок |
|
з 50-ти символів” на MS */ |
Розглянемо ще один приклад оголошення без перейменовування:
char s[20]; |
// Слово s – рядок з 20-ти символів, |
362 |
Розділ 11 |
char mas[10][20]; |
// mas – масив з 10 слів |
Перейменування типу “рядок з 20-ти символів” на тип “слово”: |
|
typedef char slovo [20]; |
|
slovo s; |
// Оголошення змінної s типу slovo |
slovo mas[10]; |
// і масиву з 10-ти слів |
Взагалі typedef є просто засобом спрощення запису операторів оголошення змінних.
Доволі часто визначення типів даних використовується разом зі структурами, що надає можливість створювання складних типів даних, які поєднують різнотипні характеристики. Приклади такого використання буде наведено у наступному підрозділі.
11.2 Структури (struct)
У попередніх розділах для зберігання даних розглянуто прості типи даних: числа, символи, рядки тощо. Але часто на практиці перед програмістом постає завдання запрограмувати той чи інший об‟єкт, який характеризується водночас кількома параметрами, приміром, точка на площині задається парою дійсних чисел (x, y), а дані про людину можна задавати кількома параметрами: прізвище, ім‟я, по-батькові – рядки, рік народження – число тощо. Для поєднання кількох параметрів в одному об‟єктові в С++ існує спеціальний тип даних, який має назву структура. На відміну від масивів, які також є структурованим типом даних і містять елементи одного типу, структури дозволяють поєднувати дані різних типів.
Структура – це складений тип даних, змінні (поля) якого можуть містити різноманітну й різнотипну інформацію.
Опис структури має синтаксис
struct <ім‟я_типу_структури> { <тип1> <поле1>;
<тип2> <поле2>;
. . .
<типN> <полеN>; } <список_змінних>;
Ім‟я типу структури задається програмістом виходячи зі змісту даних, які зберігатимуться у структурі. Список змінних наприкінці оголошення структури може бути відсутнім, у цьому разі після фігурної дужки має бути крапка з комою.
Наведемо приклад оголошення структури з ім‟ям sportsman, яка поєднає в собі інформацію про змагання спортсменів: прізвище і вік спортсмена, країна, кількість його очок, утворюючи відповідні поля:
struct sportsman |
|
|
{ char prizv[15]; char kraina[10]; |
// Прізвище та країна, |
|
int vik; |
float ochki; |
// вік та кількість очок. |
}; |
|
|
Як наслідок цього оголошення створено новий тип sportsman. Тепер у програмі цей тип може використовуватись нарівні зі стандартними типами

Типи користувача |
363 |
для оголошення змінних. Оголошені змінні типу sportsman матимуть чотири поля: два рядки (prizv і kraina), ціле (vik) і дійсне (ochki) числа.
sportsman Sp;
sportsman Mas[15];
При оголошуванні змінної типу структури пам‟ять під усі поля структури виділяється послідовно для кожного поля. У наведеному прикладі структури sportsman під змінну Sp послідовно буде виділено 15, 10, 4, 4 байти – усього 33 байти. Поля змінної Sp у пам‟яті буде розміщено у такому порядку:
1 |
2 |
3 |
4 |
5 |
|
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
|
|
|
|
|
char prizv[15] |
|
|
|
char kraina[10] |
int vik |
float |
||||||||||||||||||||||
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
ochki |
Слід зазначити, що сумарну пам‟ять, яка виділяється під структуру, може бути збільшено компілятором на вимогу процесора для так званого вирівнювання адрес. Реальний обсяг пам‟яті, яку займає структура, можна визначити за допомогою sizeof().
При оголошуванні структур їхні поля можна ініціалізовувати початковими значеннями. Наприклад, оголошення змінної Sp типу sporsman й ініціалізація її даними про 20-річного спортсмена з України за прізвищем Бойко, який набрав 75.3 очок, матиме вигляд:
sporsman Sp={"Бойко", "Україна", 20, 75.3};
Оголошення типу “точка на площині” POINT з полями-координатами – x та y і змінної spot цього типу – точки з координатами (20, 40) може бути таким:
struct POINT { int x, у;
} spot = { 20, 40 }; // Точка має координати x = 20, у = 40
При ініціалізації масивів структури кожен елемент масиву слід записувати у фігурних дужках, наприклад при створенні двовимірного масиву compl розміром 2 3 типу структури complex з полями real та im ініціалізація початкових значень елементів може мати вигляд:
struct complex { float real, im;
} compl[2][3]={{{1.4}, {5}, {-3.1}},{{2}, {-5.2}, {0}}};
Доступ до полів структури здійснюється за допомогою операції “крапка”: <структурна змінна>.<ім‟я поля>
Наприклад, для розглянутої змінної Sp типу sporsman надання значень її полям може мати вигляд:
strcpy(Sp.prizv, "Бойко"); strcpy(Sp.kraina, "Україна"); Sp.vik = 20;
Sp.ochki = 75.3;

364 Розділ 11
Можна створювати тип структури за допомогою typedef, наприклад:
typedef struct
{ AnsiString priz, gruppa; int Bal_math, Bal_inform;
} student; |
// Оголошення типу “студент” |
student W; |
// і змінної W цього типу. |
Тут означено тип структури за ім‟ям student для зберігання даних про успішність студентів з полями: прізвище, група, екзаменаційні оцінки з математики та інформатики й оголошено змінну W цього типу.
Структури можуть розміщуватись у динамічній пам‟яті за допомогою оператора new і оголошуватися як покажчики. У такому разі доступ до полів структури здійснюється за допомогою операції вибору – “стрілка” (->, на клавіатурі послідовно натискаються клавіші “мінус” і “більше”):
sportsman *p=new sportsman; |
// Виділення пам‟яті, |
p->ochki=95.3; |
// присвоєння очок полю ochki |
(*p).vik=23; |
// і віку полю vik. |
Зверніть увагу на те, що якщо р – покажчик на структуру, то (*р) – сама структура і доступ до її полів здійснюється за допомогою крапки. Детальніше динамічні структури розглянуті в розд. 13.
Якщо змінні типу “структура” оголошуються у програмі лише один раз, то, зазвичай, їх записують наприкінці оголошення структури і при цьому ім‟я структури можна явно не зазначати, наприклад:
struct
{ char prizv[15]; char kraina[10];
int vik; float ochki;
}
Sp;
Тут оголошено змінну Sp як структуру з чотирма полями без створення типу “спортсмен”.
Поля структури можуть бути якого завгодно типу, у тому числі й масивом, покажчиком чи структурою, окрім типу тієї ж самої структури (але можуть бути покажчиком на неї). За приклад створимо дві структури для опрацювання інформації про співробітників у відділі кадрів: прізвище, ім‟я, по-батькові співробітника, домашня адреса, домашній телефон, дата народження, дата вступу на роботу, зарплатня.
struct date |
|
{ int day, month, year; |
// День, місяць та рік |
}; |
|
struct person |
|
{ char prizv[20], adresа[150]; |
// Прізвище, адреса, |
char phone[10]; date birth_date; // телефон, дата народження,
date work_date; |
// дата вступу на роботу |
float salary; |
// та зарплатня. |
}; |
|
Типи користувача |
365 |
У цьому прикладі оголошується новий тип – структура date для зберігання дати (день, місяць, рік). Окрім того, оголошується тип – структура person, яка використовує тип date для полів birth_date і work_date. Посилання на поля вкладеної структури формується з імені структурної змінної, імені структурного поля й імені поля вкладеної структури. Те, що поля структур самі можуть бути структурами, надає можливість цілісно описувати доволі складні об‟єкти реального світу. Оскільки структури, на відміну від масивів, зберігають дані різних типів, їх відносять до неоднорідних типів даних.
Оголосимо змінну створеного типу person: person P;
Присвоювання прізвища у змінну Р: strcpy(P.prizv, "Шкуропат");
Присвоювання дати народження 5 березня 1987 року у змінну P:
P.birth_date.day=5;
P.birth_date.month=3;
P.birth_date.year=1987;
Розмістимо змінну man типу person у динамічній пам‟яті: person *man = new person;
І присвоїмо ті ж самі значення:
strcpy(man->prizv, "Шкуропат"); (man->birth_date).day=5; (man->birth_date).month=3; (man->birth_date).year=1987;
Оголосимо масив з даними про 100 осіб: person P[100];
Попередні присвоювання для четвертої людини:
strcpy(P[3].prizv, "Шкуропат");
P[3].birth_date.day=5;
P[3].birth_date.month=3;
P[3].birth_date.year=1987;
Уведемо значення всіх полів для всіх співробітників до масиву: for(i=0; i<100; i++)
{cout<<"Введіть призвіще: " gets(P[i].prizv); cout<<"Введіть адресу: " gets(P[i].adresа); cout<<"Введіть телефон: " gets(P[i].phone);
cout<<"Введіть дату народження: "<<endl; cout<<"День: "; cin>>P[i].birth_date.day; cout<<"Місяць: "; cin>>P[i].birth_date.month; cout<<"Рік: " cin>>P[i].birth_date.year; cout<<"Введіть дату вступу до роботи: "endl; cout<<"День: "; cin>>P[i].work_date.day;

366 Розділ 11
cout<<"Місяць: "; cin>>P[i]. work_date.month; cout<<"Рік: " cin>>P[i]. work_date.year;
}
Структурні змінні одного типу можна переприсвоювати одну одній, при цьому виконується присвоювання усіх полів, наприклад:
person man=P[0];
Як вже було зазначено вище, поля структури можуть бути якого завгодно типу, у тому числі масивами. Наприклад, оголошення структури “погода за місяць” може мати вигляд
struct pogoda |
|
{ int month; |
// Номер місяця, |
int temp[31];// масив температур за 31 день. |
|
}; |
|
pogoda M; |
// Оголошення змінної M типу pogoda (погода за один місяць). |
pogoda G[12]; |
// і масиву G типу pogoda (погода за рік). |
Визначимо значення температури повітря за 14 липня:
M.month = 7; M.temp[13] = 27;
Присвоїмо випадкові значення температури за весь лютий:
M.month = 2;
for(int i=0; i<28; i++) M.temp[i] = random(5)-10;
Виведення температури повітря восени (за 3 місяці): for(int i=9; i<=11; i++)
{G[i-1].month = i; for(int j=0; j<31; j++)
if(j==30 && (i==9 || i==11))
continue; // Пропустити 31.09 та 31.11
else
cout << G[i-1].temp[j] << " "; cout << endl;
}
У С++ структура при описуванні, окрім звичних полів, може містити елементи-функції. Наприклад, можна дописати до структури pogoda функцію month_name, яка визначатиме рядок – назву місяця за номером.
struct pogoda
{int month, temp[31]; // Номер місяця і масив температур за 31 день. char *month_name()
{switch (month)
{case 1: return "Січень"; case 2: return "Лютий"; case 3: return "Березень"; case 4: return "Квітень"; case 5: return "Травень"; case 6: return "Червень"; case 7: return "Липень"; case 8: return "Серпень";
Типи користувача |
367 |
case 9: return "Вересень"; case 10: return "Жовтень"; case 11: return "Листопад"; case 12: return "Грудень";
default: return "Помилковий номер";
} }
};
У програмі цю функцію можна викликати в такий спосіб:
pogoda M;
. . .
cin >> M.month ;
cout << M.month_name();
Після виконання цих операторів на екран буде виведено рядок, утворений за допомогою функції month_name().
Наведемо ще один приклад елемента-функції. Зорганізуємо структуру student з функцією Show(), яка будуватиме рядок типу AnsiString, поєднуючи усі поля цієї структури. Надалі цей рядок можна буде використовувати для виведення записів на екран:
struct student
{AnsiString prizv, grupa; int Bal_math, Bal_inform; AnsiString Show()
{return prizv+" "+ grupa+" "+IntToStr(Bal_math)+" "+
IntToStr(Bal_inform);
}
};
У програмі цю функцію можна викликати в такий спосіб:
student z;
. . . .
Memo1->Lines->Add(z.Show());
Після виконання цих операторів до компонента Memo1 буде виведено рядок, утворений за допомогою функції Show(). Отже, звертання до елементівфункцій відбувається так само, як і до звичних полів. Наявність функцій у структурі може істотно спростити програму.
До функцій структури передаються як звичайні змінні.
Наведемо приклад консольної програми, в якій організовано дві функції, параметрами яких є структури.
Приклад 11.1 Створити програму для опрацювання інформації про результати сесії студентів: прізвище студента, курс, група і результати п‟яти екзаменів. У програмі передбачити можливість введення і виведення даних та переведення студентів, які склали сесію, на наступний курс, змінивши відповідно з цим номер групи.
Розв‟язок. У наведеному нижче програмному коді зорганізовано дві функції: print() та func(). Параметром функції print() є структура, поля якої
368 |
Розділ 11 |
почергово виводяться на екран консолі. Функція func() одержує посилання на структуру, що надає їй можливість редагувати дані структури. Ця функція перевіряє оцінки студента, і, якщо зустрічається оцінка нижче за 60 балів, функція перериває своє виконування. Якщо всі оцінки студента були не нижче за 60 балів, підвищується значення курсу. Зазвичай назва групи складається з абревіатури спеціальності, дефіса, курсу й номера групи. Для студента, якого буде переведено до наступного курсу, у назві групи слід змінити номер курса. Для цього слід віднайти позицію дефіса і замінити символ, який слідує за дефісом, на нове значення курсу. Позицію дефіса може бути обчислено як різницю покажчиків на віднайдений дефіс і початок рядка S.gr. Для перетворювання номера курсу на рядок, використовується функція atoi(), яка розглядалась у розділі 7.
Текст програми: const int N=2;
struct student |
// Оголошення типу структури з полями: |
{ char prizv[10]; |
// прізвище, |
int kurs; |
// курс, |
char gr[10]; |
// група, |
int ekz[5]; |
// і масив екзаменаційних оцінок. |
}; |
|
void func(student &S) |
// Функція переведення на наступний курс |
{int j,k; char* ss="";
for(j=0; j<5; j++) if(S.ekz[j]<60) return; S.kurs++;
k=strchr(S.gr,'-')-S.gr; itoa(S.kurs,ss,10);
S.gr[k+1]=ss[0];
}
void print(student S) // Функція виведення на екран даних структури
{cout<<"Прізвище "; puts(S.prizv); cout<<"Курс "<<S.kurs<<endl; cout<<"Група "; puts(S.gr); cout<<"Екзамени ";
for(int j=0; j<5; j++) cout << S.ekz[j] << " ";
cout<<endl;
}
}
int main(int argc, char* argv[])
{ student s[N]; // Оголошення масиву студентів int i,j;
cout<<"Введіть інформацію про студентів: "<<endl; for(i=0; i<N; i++) // Введення даних до масиву студентів
{cout<<"Прізвище: "; cin>>s[i].prizv; cout<<"Курс: "; cin>>s[i].kurs; cout<<"Група: "; cin>>s[i].gr; cout<<"Екзамени: "<<endl;
for (j=0; j<5; j++) cin>>s[i].ekz[j];
}