Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:

C _Учебник_МОНУ

.pdf
Скачиваний:
206
Добавлен:
12.05.2015
Размер:
11.12 Mб
Скачать
// у – менше, х – більше
// За b>a параметри обмінюються місцями

Багатофайлові програми

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.

320

Розділ 9

Створимо ще один заголовний файл ratio.h, в якому оголосимо чотири функції для опрацювання раціональних дробів:

#ifndef ratioH #define ratioH

#include "mat.h"

// Долучення заголовного файла mat.h

void sokr(int &a, int &b);

//Скорочення

int znam(int a1, int b1, int a2, int b2); //Спільний знаменник bool rivn(int a1, int b1, int a2, int b2); //Порівняння на рівність bool bilsh(int a1,int b1,int a2,int b2);//Порівняння на більше/менше

#endif

Функція sokr() отримує два параметри – чисельник і знаменник раціонального дробу. Вона має можливість змінювати їхні значення, оскільки параметри передаються за посиланням.

Функція znam() обчислює найменший спільний знаменник двох дробів (їхні чисельники і знаменники передаються як параметри).

Функція rivn() перевіряє, чи є два дроби (чисельники і знаменники яких передаються як параметри) рівними. Результат – логічне значення типу bool.

Функція bilsh() перевіряє, чи є один дріб більше другого. Результат – логічне значення типу bool.

Файл реалізації ratio.cpp:

#pragma hdrstop #include "ratio.h"

#pragma package(smart_init) void sokr(int &a, int &b)

{ int z=nsd(a,b);

// Обчислюємо найбільший спільний дільник

a=a/z; b=b/z;

// Ділимо чисельник і знаменник на НСД

}

 

int znam(int a, int b)

{ int z=nsk(a,b);

/* Спільний знаменник є найменшим

}

спільним кратним чисельника і знаменника */

bool rivn(int a1, int b1, int a2, int b2)

{int z=nsk(b1, b2); // Спільний знаменник. int x,y;

x=z/b1; y=z/b2;

/* Обчислюємо коефіцієнти, на які слід помножити

чисельники і знаменники, щоб привести до спільного дільника */

if(a1*x == a2*y)

// Порівнюємо чисельники здобутих дробів

return true;

 

else return false;

 

}

bool bilsh(int a1, int b1, int a2, int b2) { int z=nsk(b1, b2), x, y;

x=z/b1; y=z/b2; // Приводимо до спільного дільника if(a1*x > a2*y) return true; // Порівнюємо чисельники

else return false;

}

 

Багатофайлові програми

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);

}

322

Розділ 9

//------------- Добуток дробів ----------------------------------

void __fastcall TForm1::Button4Click(TObject *Sender)

{int a, b, c, d, s1, s2, 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);

s1=a*c; s2=b*d; sokr(s1,s2);

Edit9->Text=IntToStr(s1); Edit10->Text=IntToStr(s2);

}

//---------------- Частка дробів -------------------------------

void __fastcall TForm1::Button5Click(TObject *Sender)

{int a, b, c, d, s1, s2, 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);

s1=a*d; s2=b*c; sokr(s1,s2);

Edit11->Text=IntToStr(s1); Edit12->Text=IntToStr(s2);

}

//------------- НСД і НСК -------------------------------------

void __fastcall TForm1::Button6Click(TObject *Sender)

{int a, b; a=StrToInt(Edit13->Text); b=StrToInt(Edit14->Text); Edit15->Text=IntToStr(nsd(a,b));

Edit16->Text=IntToStr(nsk(a,b));

}

Багатофайлові програми

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);

324

Розділ 9

x=z/d1.b; y=z/d2.b;

if(d1.a*x > d2.a*y) return true; else return false;

}

drib sum(drib d1, drib d2)

{int x,y,z; drib s; sokr(d1); sokr(d2); z=znam(d1,d2); x=z/d1.b; y=z/d2.b;

s.b=z; s.a=d1.a*x+d2.a*y; sokr(s);

return s;}

drib dob(drib d1, drib d2)

{drib p;

sokr(d1); sokr(d2); p.a=d1.a*d2.a; p.b=d1.b*d2.b; sokr(p);

return p;

}

drib chast(drib d1, drib d2)

{drib r;

sokr(d1); sokr(d2); r.a=d1.a*d2.b; r.b=d1.b*d2.a; sokr(r);

return r;

}

Файл Unit1.cpp:

#include <vcl.h> #pragma hdrstop #include "Unit1.h" #include "ratio.h"

#pragma package(smart_init) #pragma resource "*.dfm" TForm1 *Form1;

//------------------------------------------------------------

__fastcall TForm1::TForm1(TComponent* Owner)

 

: TForm(Owner)

{

 

}

 

//---------------

Порівняння дробів 1 -----------------------------

void __fastcall TForm1::Button1Click(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(rivn(d1,d2)) ShowMessage("a/b = c/d");

 

Багатофайлові програми

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);

}

326

Розділ 9

9.3 Бібліотеки функцій

Функції, прототипи яких містяться у стандартних заголовних файлах, зазвичай визначено у бібліотеках функцій. Бібліотеки функцій С++ – це набір функцій, але не у формі коду, а у вже скомпільованому вигляді. Файли бібліотек С++ мають розширення lib. При використанні бібліотек неможливо обійтися без заголовних файлів, оскільки це є єдиний спосіб надати програмі інформацію про функції, які зберігаються у бібліотеках.

Файли бібліотек у Borland C++ Builder можна поділити на три категорії:

файли стандартної бібліотеки С++ (які містять функції та інші елементи, спільні для всіх реалізацій С++);

файли бібліотек Windows;

файли бібліотек Borland С++ Builder, які реалізують специфічні функ-

ції С++ Builder.

Для того, щоб використовувати бібліотеку у програмі, яка створюється у Borland С++ Builder, слід долучити до програми цю бібліотеку. Це можна зробити двома способами:

1) скористатися командою інтегрованого середовища Project / Add To Project. Для цього слід налагодити діалогове вікно, яке відкриється в такий

спосіб, щоб у ньому відображалися файли з розширенням lib, та обрати потрібний файл бібліотеки;

2) долучити бібліотеку директивою #pragma link. У загальному вигляді використання цієї директиви виглядає як

#pragma link "<ім‟я файла бібліотеки>"

9.4 Директиви препроцесора

Препроцесор – це комп‟ютерна програма, яка приймає дані на вході і видає дані, призначені для входження іншої програми, приміром, такої як компілятор.

У мови програмування С та C++ вбудовано підтримку препроцесора. Рядки в початковому коді, які мають бути опрацьовані препроцесором у вигляді #define та #include, називаються препроцесорними директивами.

Багатофайлові програми

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;

Соседние файлы в предмете [НЕСОРТИРОВАННОЕ]