
C _Учебник_МОНУ
.pdfБагатофайлові програми |
319 |
Функції nsd(int, int) та nsk(int, int) далі слід визначити. Для цього переходимо до файла mat.cpp і створюємо текст реалізації оголошених у mat.h функцій:
#pragma hdrstop #include "mat.h" #include <math.h>
#pragma package(smart_init)
// Рекурсивна функція обчислення найменшого спільного кратного int nsd( int a, int b)
{int c;
if(abs(b) > abs(a))
c = nsd(b, a); else
if(b == 0) c = a;
else c = nsd(b, a% b); // Тут a%b – остача від ділення a на b return c;
}
// Нерекурсивна функція обчислення найменшого спільного кратного int nsk(int a, int b)
{ int x, y, i; if(abs(a)<abs(b))
{x=b; y=a; } else
{x=a; y=b; } for(i=1; i<=abs(y); i++)
if((i*x)%y==0) return x*i;
}
Функцію обчислення найменшого спільного кратного було розглянуто раніше в підрозд. 8.5 у прикладі 8.19.
Слід звернути увагу на директиви
#include "mat.h" #include <math.h>
Перша залучає до програми заголовний файл mat.h, який має міститися у теці з проектом, і стандартну бібліотеку математичних функції math.h, яку розміщено у спеціально відведеній для цього теці разом з Borland C++ Builder.
Слід звернути увагу на те, що створювана програма має опрацьовувати не тільки раціональні дроби, а й цілі числа. Головний файл проекту Unit1.cpp для опрацювання дробів використовуватиме функції, оголошені у файлі ratio.h, а для обчислення НСД та НСК двох цілих чисел – функції, оголошені у mat.h. Це означає, що обидва файли – ratio.h та mat.h – слід долучити до Unit1.cpp. Але заголовний файл mat.h вже буде долученим до ratio.h і тому долучення цього файла до Unit1.cpp викличе повторне долучення, чого слід уникнути. Для цього долучимо цей файл до ratio.h (а не до ratio.cpp) і при долученні ratio.h до Unit1.cpp всі функції, оголошені у mat.h, вже будуть доступні без додаткового долучення його до Unit1.cpp.
|
Багатофайлові програми |
321 |
Файл Unit1.cpp (головний файл проекту): |
|
|
#include <vcl.h> |
|
|
#pragma |
hdrstop |
|
#include "Unit1.h" |
|
|
#include "ratio.h" // Долучаємо заголовний файл ratio.h |
|
|
#pragma package(smart_init) |
|
|
#pragma resource "*.dfm" |
|
|
TForm1 *Form1; |
|
|
//------------------------------------------------------------- |
|
|
__fastcall TForm1::TForm1(TComponent* Owner) |
|
|
|
: TForm(Owner) |
|
{ |
|
|
} |
|
|
//------------ |
Перевірка на рівність дробів --------------------------- |
|
void __fastcall TForm1::Button1Click(TObject *Sender)
{int a, b, c, d;
a=StrToInt(Edit1->Text); b=StrToInt(Edit2->Text); sokr(a, b);
Edit3->Text=IntToStr(a); Edit4->Text=IntToStr(b); c=StrToInt(Edit5->Text); d=StrToInt(Edit6->Text); sokr(c, d);
if (rivn(a,b,c,d)) ShowMessage("a/b = c/d"); else ShowMessage("a/b != c/d");
}
//----------- Порівняння дробів -----------------------------------
void __fastcall TForm1::Button2Click(TObject *Sender)
{int a, b, c, d;
a=StrToInt(Edit1->Text); b=StrToInt(Edit2->Text); sokr(a, b);
Edit3->Text=IntToStr(a); Edit4->Text=IntToStr(b); c=StrToInt(Edit5->Text); d=StrToInt(Edit6->Text); sokr(c, d);
if (bilsh(a,b,c,d)) ShowMessage("a/b > c/d");
else ShowMessage("a/b <=c/d");
}
//-------------- Сума дробів -------------------------------------
void __fastcall TForm1::Button3Click(TObject *Sender)
{int a, b, c, d, s1, s2, x, y, z; a=StrToInt(Edit1->Text); b=StrToInt(Edit2->Text); sokr(a, b);
Edit3->Text=IntToStr(a); Edit4->Text=IntToStr(b); c=StrToInt(Edit5->Text); d=StrToInt(Edit6->Text); sokr(c, d);
z=znam(b, d); x=z/b; y=z/d; s2=z; s1=a*x+c*y;
sokr(s1, s2);
Edit7->Text=IntToStr(s1); Edit8->Text=IntToStr(s2);
}

Багатофайлові програми |
323 |
Приклад 9.5 Створити багатофайловий проект для опрацювання раціональних дробів і обчислення найбільшого спільного дільника і найменшого спільного кратного. Надати такі можливості: скорочення дробів, обчислення спільного знаменника двох дробів, порівняння на рівність/нерівність та більше/менше, додавання, множення і ділення дробів.
Розв‟язок. Створимо новий тип “drib”. Цей тип буде структурою з полями a (чисельник) і b (знаменник). Додамо до файла ratio.h заголовки функцій для обчислення суми, добутку і частки дробів, а до файла ratio.cpp – реалізацію цих функцій. Програма використовуватиме файл mat.h, який було створено у попередньому прикладі.
Файл ratio.h:
#ifndef ratioH #include "mat.h" #define ratioH
//-------------------------------------------------------------
struct drib { int a,b; };
void sokr(drib &d); |
// |
Прототипи функцій скорочення дробів, |
int znam(drib d1, drib d2); |
// |
обчислення спільного знаменника, |
bool rivn(drib d1, drib d2); // |
порівняння дробів на рівність, |
|
bool bilsh(drib d1, drib d2);// |
порівняння дробів на більше/менше, |
|
drib sum(drib d1, drib d2); |
// |
обчислення суми двох дробів, |
drib dob(drib d1, drib d2); |
// |
обчислення добутку двох дробів |
drib chast(drib d1, drib d2);// і обчислення частки двох дробів.
#endif
Файл ratio.cpp:
#pragma hdrstop #include "ratio.h"
//-------------------------------------------------------------
#pragma package(smart_init) void sokr(drib &d)
{ int z=nsd(d.a, d.b); d.a=d.a/z; d.b=d.b/z;
}
int znam(drib d1, drib d2) { int z=nsk(d1.b, d2.b);
return z;
}
bool rivn(drib d1, drib d2)
{int x, y, z=nsk(d1.b, d2.b); x=z/d1.b; y=z/d2.b;
if(d1.a*x == d2.a*y) return true; else return false;
}
bool bilsh(drib d1, drib d2) { int x, y, z=nsk(d1.b, d2.b);
|
Багатофайлові програми |
325 |
else ShowMessage("a/b != c/d"); |
|
|
} |
|
|
//-------------- |
Порівняння дробів 2 ------------------------------- |
|
void __fastcall TForm1::Button2Click(TObject *Sender) |
|
{drib d1, d2;
d1.a=StrToInt(Edit1->Text); d1.b=StrToInt(Edit2->Text); sokr(d1);
Edit3->Text=IntToStr(d1.a); Edit4->Text=IntToStr(d1.b); d2.a=StrToInt(Edit5->Text); d2.b=StrToInt(Edit6->Text); sokr(d2);
if (bilsh(d1,d2)) ShowMessage("a/b > c/d");
else ShowMessage("a/b <=c/d");
}
//---------------- Сума дробів ---------------------------------
void __fastcall TForm1::Button3Click(TObject *Sender)
{drib d1, d2, s;
d1.a=StrToInt(Edit1->Text); d1.b=StrToInt(Edit2->Text); sokr(d1);
Edit3->Text=IntToStr(d1.a); Edit4->Text=IntToStr(d1.b); d2.a=StrToInt(Edit5->Text); d2.b=StrToInt(Edit6->Text); sokr(d2);
s = sum(d1, d2);
Edit7->Text=IntToStr(s.a); Edit8->Text=IntToStr(s.b);
}
//----------------- Добуток дробів -----------------------------
void __fastcall TForm1::Button4Click(TObject *Sender)
{drib d1, d2, p;
d1.a=StrToInt(Edit1->Text); d1.b=StrToInt(Edit2->Text); sokr(d1);
Edit3->Text=IntToStr(d1.a); Edit4->Text=IntToStr(d1.b); d2.a=StrToInt(Edit5->Text); d2.b=StrToInt(Edit6->Text); sokr(d2);
p = dob(d1,d2);
Edit9->Text=IntToStr(p.a); Edit10->Text=IntToStr(p.b);
}
//----------------- Частка дробів ------------------------------
void __fastcall TForm1::Button5Click(TObject *Sender)
{drib d1, d2, r;
d1.a=StrToInt(Edit1->Text); d1.b=StrToInt(Edit2->Text); sokr(d1);
Edit3->Text=IntToStr(d1.a); Edit4->Text=IntToStr(d1.b); d2.a=StrToInt(Edit5->Text); d2.b=StrToInt(Edit6->Text); sokr(d2);
r = chast(d1, d2);
Edit11->Text=IntToStr(r.a); Edit12->Text=IntToStr(r.b);
}

Багатофайлові програми |
327 |
Директиви препроцесора є інструкціями, записаними в тексті С- програми, котрі виконуються до трансляції програми. Директиви препроцесора дозволяють змінювати текст програми, наприклад, замінювати певні лексеми в тексті, вставляти текст з іншого файла, забороняти трансляцію частини тексту тощо. Всі директиви препроцесора розпочинаються зі знаку #. Після директив препроцесора крапка з комою не ставиться.
У програмуванні термін “директива” (вказівка) за використанням є схожий до терміну “команда”, оскільки так само використовується для опису певних конструкцій мови програмування (тобто вказівок компіляторові чи асемблерові стосовно особливостей опрацювання при компіляції).
Узагальнений формат директиви препроцесора є
# <ім‟я_директиви> <лексеми_препроцесора>
Тут до лексем препроцесора належать символьні константи, імена файлів, які долучаються, ідентифікатори, знаки операцій, розділові знаки, рядкові константи (рядки) й будь-які символи, відмінні від пробілу.
Залежно від препроцесора перед символом # і після нього в директиві можуть бути дозволені пробіли. Пробіли також дозволені перед лексемами препроцесора, поміж ними і після них. За завершення препроцесорної директиви слугує кінець текстового рядка (за наявності символу '\', який позначає перенесення рядка, завершенням препроцесорної директиви буде ознака кінця наступного рядка тексту).
У препроцесорах мови С визначені такі препоцессорні директиви: #define – визначення макросу чи препроцесорного ідентифікатора; #include – долучення тексту з файла;
#undef – скасування визначення макросу чи препроцесорного ідентифіка-
тора;
#if – перевірка умови-виразу;
#ifdef – перевірка визначеності ідентифікатора; #ifndef – перевірка невизначеності ідентифікатора; #else – початок альтернативної гілки для #if; #endif – завершення умовної директиви #if;
#elif – складена директива #else/#if;
#line – змінення номера наступного нижче рядка;
#error – формування тексту повідомлення про помилку при трансляції; #pragma – дії, передбачені реалізацією;
# – порожня директива.
Окрім препроцесорних директив, є три препроцесорні операції, які використовуються разом з командою #define:
defined – перевірка істинності операнда; ## – конкатенація препроцесорних лексем;
# – перетворення операнда на рядок символів.
Директива #define має кілька модифікацій. Вони передбачають визначення макросів та препроцесорних ідентифікаторів, кожному з яких ставиться у відповідність певна послідовність символів. У подальшому тексті програми

328 |
Розділ 9 |
препроцесорні ідентифікатори замінюються на раніш визначені послідовності символів. Так відбувається, наприклад, при визначенні констант за допомогою директиви #define. Директива #define слугує для заміни констант, які часто використовуються, ключових слів, операторів чи виразів на певні ідентифікатори. Ідентифікатори, які замінюють текстові чи то числові константи, називають іменованими константами. Ідентифікатори, які замінюють фрагменти програм, називають макровизначеннями, причому макровизначення можуть мати аргументи. Директива #define має дві синтаксичні форми:
#define <ідентифікатор> <текст>
#define <ідентифікатор> <(список параметрів)> <текст>
Ця директива замінює всі подальші входження ідентифікатора на текст. Такий процес називається макропідстановкою. Текст може бути яким завгодно фрагментом програми на С, а також може бути відсутнім. В останньому разі всі екземпляри ідентифікатора вилучаються з програми.
Щоб задати константу n зі значенням 10 слід написати директиву
#define n 10
Ця директива є аналогом такої інструкції
const int n = 10;
Відмінності полягають у тому, що:
1)при використанні константи пам‟ять під константну змінну виділяється, а при використанні директиви – ні;
2)при використанні директиви не виконується перевірка типу значення, що може спричинитися до помилок у програмі;
3)тип значення n при використанні директиви визначається автоматично, тобто, якщо у програмі є суттєво, щоб n мала тип unsigned short, зробити це за допомогою директиви неможливо.
Отже, використання директиви #define у С++ без особливої на те потреби є небажаним.
Директива #include, яку було розглянуто вище, долучає до тексту програми вміст зазначеного файла. Ця директива широко використовується для долучення до програм так званих заголовних файлів. Існують три форми цієї директиви:
#include <ім‟я_заголовного_файла> #include "ім‟я_заголовного_файла" #include ідентифікатор_макроса
Відмінність між першими двома формами директиви полягає у методі пошуку препроцесором файла, що долучається. Якщо ім‟я файла записано у кутових дужках, то послідовність пошуку препроцесором заданого файла у каталогах визначається заданими каталогами включення (include directories). Якщо ім‟я файла записано у лапках, препроцесор шукає файл, переглядаючи каталоги у такій послідовності:
каталог того файла, який містить директиву #include;
каталоги файлів, які долучили цей файл директивою #include;