Professional Documents
Culture Documents
Тема1 Merged Compressed
Тема1 Merged Compressed
(Основи алгоритмізації
і програмування)
Автор курсу: старший викладач кафедри ЕОМ НУ «ЛП» Ногаль Марія Василівна
Лекція №1. Тема №1.
ОСНОВИ АЛГОРИТМІЗАЦІЇ ЗАДАЧ
Автор курсу: старший викладач кафедри ЕОМ НУ «ЛП» Ногаль Марія Василівна
Структура навчальної дисципліни
№ Найменування показників Всього годин
1. Кількість кредитів/год. 7/210
2. Усього годин аудиторної роботи, у тому числі: 105
3. лекційні заняття, год. 60
4. практичні заняття, год. 15
5. лабораторні заняття, год. 30
6. Усього годин самостійної роботи 105
7. Екзамен +
30 10 40 50 10 100
Мови програмування — це алгоритмічні мови, призначені для опису алгоритмів, що орієнтовані для
виконання на комп’ютері, або система позначень для точного опису алгоритму, який треба виконати за
допомогою комп’ютера.
Виконавець алгоритму: людина, комп’ютер, комп’ютер, мови програмування. Залежність алгоритму від
виконавця – від множини операцій, які знає (вміє виконувати) виконавець.
Алгоритм Евкліда (~450 р. – ~380 р. до н.е., грек з Александрії, Основи – основи геометрії та теорії чисел)
знаходження найбільшого спільного дільника (НСД) двох натуральних чисел.
Нехай задані А і В – два натуральних числа. Необхідно знайти НСД цих чисел.
10 INPUT A, B
20 M=A : N=B
30 IF M=N THEN 80
40 IF M>N THEN 60
50 R=M : M=N : N=R
60 R=M-N : M=N : N=R
70 GOTO 30
80 PRINT “НСД=”; M
90 END
У даному стандарті встановлюють вимоги, які регламентують розробку, супровід, виготовлення та експлуатацію програм, що
забезпечує можливість:
• уніфікації програмних виробів для взаємного обміну програмами та застосування раніше розроблених програм у нових
розробках;
• зниження трудомісткості і підвищення ефективності розробки, супроводу, виготовлення і експлуатації програмних засобів;
• автоматизації виготовлення і зберігання програмної документації.
Згідно даного стандарту блок-схема програми відображає послідовність операцій в програмі і складається з:
1. символів процесу, що вказують фактичні операції обробки даних (включаючи символи, визначають шлях, якого слід
дотримуватися з урахуванням логічних умов);
2. лінійних символів, що вказують потік управління;
3. спеціальних символів, які використовуються для полегшення написання і читання схеми.
умова
умова умова
так ні так
ні
Якщо присутні обидва процеси, то говорять про повну альтернативу. Якщо замість одного процесу стоїть
вказівка «перейти до пункту №», то така форма запису називається неповною альтернативою.
Умова - це логічний вираз, яке може приймати два значення - «ТАК» (істина) або «НІ» (неправда). Якщо
умова вірна, дія виконується, у протилежному випадку - дія не виконується.
При виконанні алгоритму після обчислення умов, записаних усередині символу Рішення, один з виходів буде
активізований. Відповідні результати обчислення можуть бути записані поряд з лініями, що відображають ці
шляхи.
так ні умова
продовження
циклу
тіло циклу
ні
Умова
продовження
циклу
ні
Кінець циклу
Розглянемо класифікацію циклів для кращого розуміння їх різновидів та основних відмінностей один від одного.
За місцем розташування умов перевірки повторення або закінчення циклу можна виділити цикли з
передумовою та післяумовою.
• У циклі з передумовою (з попередньою умовою) перевірка виходу стоїть перед тілом циклу. Умова
записується у вигляді логічного виразу. Оператори циклу (тіло циклу) виконуються, поки умова істинна. Якщо
при вході в цикл умова "неправда" (не виконується), то буде вихід з циклу. У цьому випадку цикл не
виконається жодного разу.
• У циклі з післяумовою перевірка виходу стоїть після тіла циклу. Оператори циклу будуть виконуватися до
тих пір, поки не стане можливою умова виходу з циклу. Цикл виконається хоча б один раз.
• Кількість повторень циклу невідома (цикл з невідомим числом ітерацій). Вихід з циклу виконується при
перевірці додаткової умови.
• Тип арифметичної прогресії (цикл з відомим числом ітерацій), в якому кількість повторення циклу відома
при входженні в цикл – задана явно (як в попередньому прикладі) чи може бути просто вирахувана
(наприклад, в задачі табуляції функції із заданим кроком на заданому проміжку).У цих циклах параметр
(змінна циклу) змінюється від заданого початкового до заданого кінцевого значення, змінюючись при
кожному виконанні циклу на постійну величину, яка називається кроком параметра циклу. Інша назва цього
типу - цикли з параметром.
Початкове, кінцеве значення змінної циклу і крок повинні мати один і той же тип.
Всередині циклу не рекомендується змінювати початкове значення параметру циклу і його кінцеве
значення, тому ці значення встановлюються на самому початку роботи циклу.
Цикл закінчується, коли параметр циклу приймає кінцеве значення. При організації циклу слід особливу
увагу приділити правильному оформленню зміни параметра циклу, тому що помилка на цьому етапі може
призвести до «зациклення» обчислювального процесу.
програми.
функція b() локальні змінні;
Всі змінні повинні бути описані до їх використання. {} оператори
Визначення описують об’єкти, необхідні для представлення в програмі даних, що обробляються (іменовані
константи, змінні різних типів).
Описи повідомляють компілятору про властивості і імена об’єктів і функцій, які описані в інших частинах
програми.
int main(void)
{
double res, xl, x2, x3;
xl = 4.5;
x2 = 5.6;
x3 = 7.8;
res = (xl + x2 + x3) / 3;
return 0;
}
#include <stdio.h>
int main(void)
{
double res, xl, x2, x3;
xl = 4.5;
x2 = 5.6;
x3 = 7.8;
res = (xl + x2 + x3) / 3;
printf("Result is %f\n", res);
return 0;
}
#include <stdio.h>
#include <math.h>
int main(void)
{
double res, xl, x2;
xl = 4.5;
x2 = 5.6;
res = sqrt(xl * xl + x2 * x2);
printf("Square root is %f\n", res);
return 0;
}
Синтаксичні помилки бувають у тому випадку, коли порушені правила мови С. Ці синтаксичні помилки будуть
знайдені компілятором.
Але компілятор може видати повідомлення про уявні помилки. Наявна синтаксична помилка може призвести
до того, що компілятор буде знаходити неіснуючі помилки в інших місцях. Наприклад, якщо змінні оголошенні
не вірно, то використання цих змінних в інших місцях буде рахуватися помилкою.
Семантичні помилки виникають тоді, коли неправильно використана семантика тої чи іншої конструкції,
наприклад, передача параметрів функції, які не відповідають її аргументам.
Логічні помилки призводять до невірного результату. Компілятор не знаходить таких помилок, оскільки вони не
суперечать правилам мови С. Такі помилки можна знайти виконуючи програму крок за кроком, переглядаючи
значення кожної змінної.
– це програма, яка перетворює програму написану мовою високого рівня в набір команд машинної мови, який
використовується комп’ютером.
Цей процес є двоетапним. Зокрема, при компіляції програми на мові С спочатку виконується препроцесінг, а
після цього — компіляція в об’єктний код.
Перед основною компіляцією виконується попередня обробка (препроцесінг) програмного коду. При цьому всі
директиви препроцесора, що починаються з символу #, обробляються однаково, а саме замість самої
директиви в програмний код вставляється контент відповідного заголовного файлу. Заголовні файли зазвичай
містять необхідні описи бібліотечних функцій, змінних та структур даних. Код, отриманий після виконання
препроцесінгу, називається одиницею трансляції.
Компілятор створює файли форматy COFF (об'єктні файли .obj).
(лінкер) – програма, яка генерує виконувчий файл шляхом зв'язування об'єктних файлів проекту.
Програми С зазвичай використовують бібліотечні функції, для яких вже існує об’єктний код, наприклад, sin(),
cos(), sqrt(), тощо. Під час компоновки об’єктний код програми об’єднується з об’єктним кодом бібліотечних
функцій, які використовуються в програмі, та стандартним кодом початкового завантаження. (Код початкового
завантаження забезпечує можливість виклику функції main() із середовища операційної системи) В результаті
створюється виконавчий файл.
Компоновщик створює виконавчі файли (.exe) або бібліотеки динамічного компонування (DLL).
Об єктний код
програми
Виконавчий
Компоновщик
файл
Стандартні
бібліотеки
Visual Studio
– це широко відоме повнофункціональне середовище розробки від компанії Microsoft, яка дозволяє працювати
з такими платформами, як Windows, Інтернет, хмарні сервіси і Android. Можливості IDE Visual Studio
дозволяють правильно і ефективно писати код, реорганізовувати, аналізувати і виправляти проблеми з кодом.
Офіційний сайт - https://visualstudio.microsoft.com.
Змінна
– іменована ділянка оперативної пам’яті, яка застосовується для зберігання даних під час
роботи програми. При оголошенні змінної для неї резервується певна ділянка пам’яті, розмір
якої залежить від конкретного типу змінної. Значення змінної – фактичне значення, яке
міститься у цій ділянці пам’яті.
При написанні імені ідентифікатора враховується регістр.(MAX, Max, max- три різні ідентифікатори).
/* Коментар до програми
може займати кілька рядків */
Окрім символів /* та */ для невеликих коментарів в один рядок в С використовують знак //.
Також, окрім пояснень, доволі часто коментарі використовують при налагодженні для тимчасового
“вимкнення” певного фрагмента програми.
Усі типи змінних мови С розподіляють на дві групи: основні типи та структуровані.
До основних (базових) типів можна віднести char, int, float та double, а також їхні варіанти з
специфікаторами short (короткий), long (довгий), signed (зі знаком) та unsigned (без
знаку).
Структуровані (похідні) типи базуються на основних, до них належать масиви будь-яких типів, вказівники,
структури, об’єднання, тощо.
В С для відокремлення цілої частини числа від дійсної застосовується десяткова крапка.
Окрім звичної форми, дійсні константи можна записувати у експоненціальній формі.
Наприклад: 2.38е3 (яке дорівнює 2.38 * 103 = 2380), 3.61Е–4 (яке дорівнює 3.61 * 10–4 = 0.000361).
Число перед символом “е” називається мантисою, а після символу “е” – порядком, тобто замість основи 10
використовується літера “e”, після якої ставиться показник степеня, наприклад: 1е3 (1 * 103 = 1000), –2.7е–
4 (–2.7 * 10–4 = 0.00027).
Наприклад:
char ch;
unsigned int n;
int c; // Інтерпретується як signed int c
short а; // Інтерпретується як signed short int а
unsigned d; // Інтерпретується як unsigned int d
signed f; // Інтерпретується як signed int f
float u, v;
double z;
Для визначення розміру пам’яті, займаної змінною, існує операція sizeof(), яка повертає значення довжини
зазначеної змінної чи типу, наприклад:
a = sizeof(int); // a = 4
b = sizeof(long double); // b = 10
У якості значення константи можна подавати константний вираз, який містить раніше оголошені константи та
змінні. Наприклад:
Константам при створенні слід неодмінно надавати значення, які в подальшому не можна буде змінювати.
У мові С розрізнюють чотири типи констант: цілі, дійсні, символьні константи та рядкові константи.
Окрім десяткових цілих констант, в С можна використовувати вісімкові (перед ними обов’язково ставиться 0) і
шістнадцяткові (перед ними слід ставити 0х) цілі константи, наприклад:
Рядкова константа (літерал) – послідовність символів, включаючи великі та малі літери кирилиці й латиниці, а
також цифри і пробіли, розташовані поміж подвійних лапок “ ”, наприклад:
const char S[10]="Львів";
const char *SS="НУ Львівська політехніка";
Для того, щоб програма могла використовувати згадані функції, на початку програми слід вказати директиву
#include <stdio.h>.
Тут формат задає пояснювальний текст та вигляд значень змінних, імена яких задає параметр список_змінних.
Параметр список_змінних не є обов’язковим і являє собою послідовність розділених комами змінних, значення
яких виводяться.
Специфікатор формату задає вигляд виведеного результату.
Специфікатор формату для printf():
%[прапорці] [ширина] [.точність] [{ h | l | L }]тип
де: формат − рядок специфікаторів формату у подвійних лапках, змінна − це ім’я змінної, значення якої
вводиться. Знак & означає операцію отримання адреси змінної у пам’яті.
При виконанні функції scanf() відбувається таке: програма призупиняє роботу і очікує, доки користувач
набере на клавіатурі рядок символів та натисне клавішу <Enter>. Після натиснення клавіші <Enter> функція
scanf() перетворює введений рядок відповідно до специфікаторів формату на дані й записує їх до змінних,
адреси яких зазначено.
Функція scanf() корпорацією Microsoft визнана небезпечною, а тому, щоб використовувати її в нових версіях
Visual Studio необхідно на початку програми помістити таке оголошення:
#define _CRT_SECURE_NO_WARNINGS
Або користуватись безпечною версією цієї функції scanf_s().
Специфікатор формату для scanf:
%[*] [ширина] [{h | l | L}]тип
Операнд
- це константа, літерал, ідентифікатор, виклик функції, індексний вираз, вираз вибору елемента або більш
складний вираз, сформований комбінацією операндів, знаків операцій і круглих дужок.
Будь-який операнд, який має константні значення, називається константним виразом.
Кожен операнд має тип.
Якщо в якості операнда використовується константа, то йому відповідає значення і тип константи, що його
представляє.
За кількістю операндів, що беруть участь в операції, операції поділяються на унарні, бінарні і тернарні.
Операція операнд;
Операції збільшення та зменшення збільшують або зменшують значення операнда на одиницю і можуть бути
записані як праворуч так і ліворуч від операнда. Якщо знак операції записаний перед операндом (префіксна
форма), то зміна операнда відбувається до його використання у виразі. Якщо знак операції записаний після
операнда (постфіксна форма), то операнд спочатку використовується у виразі, а потім відбувається його зміна.
Постфіксна форма:
int a=1, b=2, c;
c=a*b++; // c=2
Префіксна форма:
int a=1, b=2, c;
c=a*++b; // c=3
== Рівно
!= Не рівно
Національний університет «Львівська політехніка»
Бінарні операції в мові С
Знак операції Операція Група операцій
& Порозрядне І
| Порозрядне АБО Порозрядні (побітові) операції
^ Порозрядне виключне АБО
&& Логічне І
Логічні операції
|| Логічне АБО
, Послідовне обчислення Послідовного обчислення
= Присвоєння
*= Множення з присвоєнням
/= Ділення з присвоєнням
%= Залишок від ділення з присвоєнням
-= Віднімання з присвоєнням
+= Додавання з присвоєнням Операції присвоєння
<<= Зсув вліво з присвоєнням
>>= Зсув вправо з присвоєнням
&= Порозрядне І з присвоєнням
|= Порозрядне АБО з присвоєнням
^= Порозрядне виключне АБО з присвоєнням
Національний університет «Львівська політехніка»
Порозрядні операції
(виконуються над окремими розрядами)
При записі виразів слід пам'ятати, що символи (*), (&), (!), (+) можуть позначати унарні або бінарні операції.
Якщо операнд1 має ненульове значення (правдиве, true), то обчислюється операнд2 і його значення є
результатом операції. Якщо операнд1 дорівнює 0, то обчислюється операнд3 і його значення є результатом
операції.
А = (D <= B)? B: D;
Змінній А присвоюється максимальне значення змінних D і B.
Типи даних від менш точного до більш точного (беззнакові вважаються точнішими за знакові):
char
short
int
long
float
double
long double
При виконанні цього присвоювання правила зведення використовуватимуться у такий спосіб. Операнд ch
зводиться до unsigned int. Після чого він зводиться до типу unsigned long. За цим самим правилом i зводиться до
unsigned long, і результат операцій у круглих дужках матиме тип unsigned long. Тоді він зводиться до типу double, і
результат всього виразу матиме тип double.
(тип) арифметичний_вираз;
тобто перед ім’ям змінної у дужках зазначається тип, до якого її слід перетворити.
Наприклад, вищерозглянутий приклад буде обчислювати правильний результат, якщо записати його у такий спосіб:
int m = 2, n = 3;
double a = (double) m/n; // У результаті a=0.66(6)
Явне зведення типу є джерелом можливих помилок, оскільки вся відповідальність за його результат покладається на
програміста. Операцію явного зведення типів слід використовувати обережно, лише якщо іншого способу здобуття
коректного результату немає. Наприклад, у виразі z/(x+25), де z, x – цілі змінні, дробова частина втрачається.
Запобігти цьому можна, якщо перетворити чисельник чи знаменник до дійсного типу. Для цього слід використати один
з трьох способів:
1) приписати десяткову крапку до числа 25. Вираз набуде вигляду z/(x+25.);
2) домножити чисельник чи знаменник ліворуч на 1.0. Вираз набуде вигляду 1.0*z/(x+25);
3) явно зазначити тип результату (double)z/(x+25).
Директива #undef
використовується для відміни чинності поточного визначення директиви #define для зазначеного
ідентифікатора. Не є помилкою використання директиви #undef для ідентифікатора, який не було визначено
директивою #define. Синтаксис цієї директиви є таким:
#undef ідентифікатор
Національний університет «Львівська політехніка»
Директива #include
долучає до тексту програми вміст зазначеного файла. Ця директива широко використовується для долучення
до програм так званих заголовних файлів. Існують три форми цієї директиви:
#include <ім’я_заголовного_файла>
#include "ім’я_заголовного_файла"
#include ідентифікатор_макроса
Відмінність між першими двома формами директиви полягає у методі пошуку препроцесором файла, що
долучається. Якщо ім’я файлу записано у кутових дужках, то послідовність пошуку препроцесором заданого
файлу у каталогах визначається заданими каталогами включення (include directories). Якщо ім’я файлу
зазначено зі шляхом, то препроцесор ніде більше цей файл не буде шукати.
Третя форма директиви #include припускає наявність макросу, який визначає файл, що долучається.
Ідентифікатор макроса не повинен починатися з символів “<” та “"”.
Оператор-вираз – будь-який вираз, що завершується крапкою з комою. Дія такого оператора полягає в
обчисленні виразу.
x++;
a*=b;
a=cos(b * 5);
Складений оператор – група операторів, обмежених фігурними дужками. Примітка – кожен оператор
завершується символом ; після закриваючої фігурної дужки крапку з комою не ставлять.
{ [оголошення]
:
оператор; [оператор];
:
}
Національний університет «Львівська політехніка»
3.2. Алгоритми з розгалуженням
і умовний оператор if
Умовний оператор мови С if має дві форми: скорочену та повну.
Скорочена форма має вигляд: умова
так
if (умова) оператор;
оператор;
Якщо умова є правдива (істинна, ненульове значення, значення true),
то виконується оператор чи група операторів в операторних дужках {}, ні
інакше відбудеться перехід на наступний оператор.
if (умова) оператор1;
else оператор2; умова
так ні
початок
Щоб отримати
результат і для
ввід х
від’ємних
значень, будемо
обчислювати
корінь так x >= 0 ні
квадратний, для
чисел менших
за нуль, з числа res x res x
з протилежним
знаком (щоб
отримати
додатнє вивід
значення). res
кінець
Національний університет «Львівська політехніка» Рисунок 3.4 – Блок-схема обчислення квадратного кореня заданого числа
Лістинг 3.2: Оператор розгалуження if-else. Обчислення квадратного кореня
заданого числа
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <math.h>
int main(void)
{
double res, x;
printf("Enter x:");
scanf("%lf", &x);
if (x >= 0)
{
res = sqrt(x);
}
else
{
res = sqrt(-x);
}
printf("Square root is %f\n", res);
return 0;
}
Національний університет «Львівська політехніка»
Завдання: дано два числа А і В. Знайти більше з них.
початок
Позначимо найбільше як max, отже,
потрібно скласти схему алгоритму
обчислення X = max (A, B). Ввід
У даному прикладі можливі два АіВ
варіанти відповіді: A або B, тобто
реалізується повна альтернатива.
Вхідні дані можуть мати будь-які ні А >= В так
значення.
Вибір варіанту буде проведено за
Х=В Х=А
результатами перевірки умови: A>B?
Для однозначності рішення
вважаємо, що при A = B, X = A.
У загальному випадку розташування Вивід Х
знаку рівності визначається
постановкою самої задачі.
кінець
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
int main()
{
int A, B, X;
printf("Enter A and B:");
scanf("%d%d", &A, &B);
if (A >= B) X = A;
else X = B;
printf("X=%d", X);
}
Позначимо найбільше як
max, отже, потрібно ні А >= В так
ні А >= В так
скласти схему алгоритму
обчислення X = max (A,
Y=В Y=А
B, С). Спочатку можна С >= В ні ні А >= С
так так
знайти максимальне з
двох значень А і В
Х=В Х=С Х=А
(Y = max (A, B)), а потім ні Y >= C так
знайти більше з Y і С:
X = max (A, B). Отже, X=C X=Y
Вивід Х
X = max (max (A, B), С).
У даному прикладі
можливі три варіанти Вивід Х
кінець
відповіді: A або B, або С.
кінець
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
int main()
{
int A, B, C, Y, X;
printf("Enter A, B, C:");
scanf("%d%d%d", &A, &B, &C);
if (A >= B) Y = A;
else Y = B;
if (Y >= C) X = Y;
else X = C;
printf("X=%d", X);
}
if (A >= B)
{
if (A >= C) X = A;
else X = C;
}
else {
if (C >= B) X = C;
else X = B;
}
У випадку вкладеного оператора if доцільно використовувати фігурні дужки, щоб згрупувати оператори у
складений оператор і, таким чином, явно вказати ваші наміри. Якщо дужки відсутні, компілятор співставляє
найближчий if, що відповідає else.
складену умову.
ні B>0
ні так Вивід Вивід
До цього часу усі «не усі додатні» «усі додатні»
умови, які ми ні C>0
так
розглядали були кінець
простими. Вивід Вивід
«не усі додатні» «усі додатні»
Складена умова – дві
або більше простих
кінець
умови, з’єднаних
знаком логічної
операції (АБО, І, НЕ). Рисунок 3.7 – Блок-схеми алгоритму перевірки, чи додатні три числа
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
int main()
{
int A, B, C;
printf("Enter A, B, C:");
scanf("%d%d%d", &A, &B, &C);
if (A >= 0)
if (B >= 0)
if (C >= 0) printf("all positive\n");
else printf("not all positive\n");
else printf("not all positive\n");
else printf("not all positive\n");
}
A >=B
ні так
A = X+Y A = X*Y
B = X*Y B = X+Y
Вивід
А, В
кінець
B D B D
x1,2 x2
2* A 2* A
Якщо дискримінант від’ємний, або А=0, вивід
вивід
«коренів
то говорять, що рівняння коренів не має. x 1 і x2 немає»
кінець
switch (вираз)
{ [оголошення]
case значення_мітка_1 : {послідовність_операторів_1;}
............
case значення_мітка_n : {послідовність_операторів_n}
[ default: послідовність операторів; ]
}
мітка_n мітка_2
послідовність мітка_2 послідовність
операторів операторів 1 послідовність послідовність послідовність
послідовність
операторів n; операторів 2; операторів 1;
операторів;
break; break; break;
мітка_n
послідовність
операторів 2
...
послідовність
операторів n
оператор break, який перериває виконання оператора, в якому розміщено цей break, і передає керування на
наступний оператор. Зауважимо, що всередині вкладених операторів do-while, for, while чи switch оператор
break завершує лише самий внутрішній з цих операторів, тому break неможна використовувати для виходу з
декількох вкладених циклів.
Для переходу до наступної ітерації циклу можна застосовувати оператор continue. Цей оператор, подібно до
оператора break, використовується лише всередині операторів циклу for, while, do-while, але, на відміну від
останнього, оператор continue не припиняє подальше виконання циклу, а переходить до чергової ітерації того
циклу, у тілі якого він виявився. Оператор continue, так само як і оператор break, перериває найглибший з циклів.
спеціальною міткою. Мітка - це ідентифікатор. Мітку записують перед оператором, на який слід передати
керування, і відокремлюють від цього оператора символом двокрапки (:).
вивід s
кінець
while (умова)
{ тіло циклу }; умова
так ні
Послідовність операторів (тіло циклу) виконується, поки
умова є правдива (істинна, true, має ненульове значення),
тіло циклу
а вихід з циклу здійснюється, коли умова стане хибною
(false, матиме нульове значення).
Якщо умова є хибною при входженні до циклу, то
послідовність операторів не виконуватиметься жодного
разу, а керування передаватиметься до наступного Рисунок 3.13 – Блок-схема циклу while
оператора програми.
Лістинг 3.9. : Обчислення суми чисел від 1 до 100
за допомогою оператора while
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
int main()
{
int i, s;
i = 1;
s = 0;
while (i <= 100)
{
s = s + i;
i = i + 1;
}
printf("s=%d", s);
}
do
{ тіло циклу
тіло циклу;
так
}
while (умова); умова
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
int main()
{
int i, s;
i = 1;
s = 0;
do
{
s = s + i;
i = i + 1;
} while (i <= 100);
printf("s=%d", s);
}
тіло циклу; ні
так
тіло циклу
Конструктивно цей оператор складається з трьох
основних блоків, розміщених у круглих дужках і
відокремлених один від одного крапкою з комою (;), та ні
модифікації
операторів (тіла циклу), які мають багаторазово
виконуватись у цьому циклі.
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
int main()
{
int i, s;
for (s = 0, i = 1; i <= 100; i++) s += i;
printf("s=%d", s);
}
Цей оператор можна прочитати так: “виконати команду s += i 100 разів (для значень і від 1 до 100 включно, де і на
кожній ітерації збільшується на 1)”.
Порядок виконання цього циклу є таким:
1) присвоюються початкові значення (s=0, i=1);
2) перевіряється умова (i<=100);
3) якщо умова є правдива (true), виконується тіло циклу: до суми, обчисленої на попередній ітерації, додається нове
число;
4) параметр циклу збільшується на 1. Далі повертаємось до пункту 2. Якщо умова у пункті 2 стане хибною (false), то
відбудеться вихід із циклу.
Національний університет «Львівська політехніка»
Оператори while, do-while і for можуть завчасно завершитись при виконуванні операторів break,
goto, return (вихід з поточної функції) усередині тіла циклу.
З допомогою оператора continue можна достроково завершувати чергову ітерацію, оминаючи решта
операторів циклу.
Якщо в тілі циклу for присутній оператор continue, то його виконання приводить до повторного обчислення
модифікацій і нової ітерації циклу.
2 3 4 початок
x x x
Завдання: необхідно обчислити: y x , 0 x 1, x
2 3 4
ввід x
i
x
Обчислення припинити при r , де r ,а рівне 0,0001.
i=2
i q=x
y=x
Дана задача це приклад циклу з невідомим числом ітерацій,
адже вихід з циклу виконується за додатковою умовою, q = q*x
r = q/i
яка залежить від значення вхідної змінної x. y = y+r
так
i = i+1
r>=0.0001
ні
вивід y
кінець
i = i+2
|r|>=0.0001
ні
вивід y
кінець
циклу з
y = y+1/(i*i)
параметром, i = i+1 y = y+1/(i*i)
тут значення ні
параметра
i <= 50
(змінна і) кінець
змінюється циклу за і
ні
від одиниці вивід y
вивід y
до
п’ятдесяти. вивід y
кінець
кінець
кінець
y = f(x) y = f(x)
A A
a b x a=x0 x1 x2 b=x3 x
найбільший, то з цього
моменту найбільшим
a > max
вважаємо вже його. вивід
так max
max = a ні
кінець
Національний університет «Львівська політехніка» Рисунок 3.21 – Блок-схема алгоритму знаходження найбільшого числа послідовності
Лістинг 3.16.: Пошук найбільшого числа в послідовності
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
int main(void)
{
int n, a, i, max;
printf("Enter n:");
scanf("%d", &n);
printf("\nEnter a:");
scanf("%d", &a);
max = a;
for (i = 2; i <= n; i++)
{
printf("\nEnter a:");
scanf("%d", &a);
if (a > max)
max = a;
}
printf("\nmax=%d", max);
return 0;
}
Наприклад оголошення функції обчислення обчислення інтеграла sin(x) може мати вигляд:
MyTrapezSquare(a, b, N);
чи
MyTrapezSquare(1.1, 4.2, 10);
Програма спочатку виділяє місце для розміщення параметрів, а після цього копіює фактичні значення параметрів у
відповідні виділені комірки пам'яті.
Тоді програма починає виконувати оператори функції, починаючи з першого оператора.
Коли програма дійде до виконання оператора return чи досягне закриваючої дужки } блоку функції, виконання
функції завершується і управління повертається у точку виклику функції.
Коли виконання функції завершується не через оператор return, а через досягнення закриваючої дужки } блоку
функції, то у цьому випадку значення функції для викликаючого оператора є невизначеним.
Тому функція, яке не є типу void, мусить мати оператор return.
#include <stdio.h>
int MySum(int M, int N) Лістинг 3.17.: Опис функції, звертання
{ до функції, фактичні параметри
int i, Sum;
i = M; Sum = 0;
while (i <= N)
{
Sum = Sum + i;
i++;
}
return Sum;
}
int main(void)
{
int i, N, Sum;
i = 137; N = 895;
Sum = MySum(i, N);
Sum = MySum(5, 555);
Sum = MySum(Sum, Sum + 1);
Sum = MySum(N, N + 3) + 4;
MySum(11, 123);
printf("Sum = %d", Sum);
return 0;
}
Національний університет «Львівська політехніка»
Лістинг 3.18.: Функція типу void (без return)
Область видимості – це частина тексту програми, в якій може бути використаний даний об'єкт.
Об'єкт вважається видимим в блоці або в вихідному файлі, якщо в цьому блоці або файлі відомі ім'я і тип об'єкту.
Об'єкт може бути видимим в межах блоку, вихідного файлу або у всіх вихідних файлах, що утворюють програму.
Це залежить від того, на якому рівні оголошений об'єкт: на локальному, тобто всередині деякого блоку, або на
глобальному, тобто поза усіма блоками.
Якщо змінна оголошена всередині блоку (локальна змінна), то вона видима в цьому блоці, і у всіх внутрішніх
блоках. Якщо змінна оголошена поза усіма блоками (глобальна змінна), то вона видима від точки оголошення до
кінця даного вихідного файлу.
auto,
register,
static
або extern,
а якщо він не вказаний, то мається на увазі клас пам'яті auto.
Специфікатор класу пам'яті register вказує компілятору виділити пам'ять для змінної в регістрі процесора,
якщо це можливо.
Використання регістрової пам'яті зазвичай призводить до скорочення часу доступу до змінної.
Змінна, оголошена з класом пам'яті register, має ту ж область видимості, що і змінна auto.
Число регістрів, які можна використовувати для значень змінних, обмежена можливостями комп'ютера, і в тому
випадку, якщо компілятор не має в розпорядженні вільних регістрів, то змінній виділяється пам'ять як для класу
auto.
Клас пам'яті register може бути зазначений тільки для змінних з типом int або вказівників з розміром, рівним
розміру int.
Локальна змінна (оголошена всередині блоку) з класом пам'яті extern, є посиланням на глобальну змінну з
тим же самим ім'ям, визначену в одному з вихідних файлів програми. Мета такого оголошення полягає в тому,
щоб зробити визначення глобальної змінної видимим всередині блоку.
В оголошеннях з класом пам'яті extern не допускається ініціалізація, так як ці оголошення посилаються на вже
існуючі та визначені раніше змінні. Змінна, на яку робиться посилання за допомогою специфікатора extern,
може бути визначена тільки один раз в одному з вихідних файлів програми.
Імена формальних параметрів, оголошені в списку параметрів функції, видимі тільки від точки оголошення
параметра до кінця тіла функції.
В мові С допускається (але не рекомендується) оголошення локальної змінної з тим же ім’ям, що і глобальна
змінна. В такому випадку в блоці, де оголошена така змінна, використовується локальна змінна, а не
глобальна.
Національний університет «Львівська політехніка»
При оголошенні глобальних змінних (поза усіма блоками) може бути використаний специфікатор
класу пам'яті static чи extern, а так само можна оголошувати змінні без вказівки класу пам'яті.
Класи пам'яті auto і register для глобальних змінних неприпустимі.
Оголошення глобальних змінних - це або визначення змінних, або посилання на визначення, зроблені в іншому
місці програми. Якщо при оголошенні глобальної змінної клас пам’яті не вказаний, і явної ініціалізації немає, то
змінна набуде нульового значення.
Глобальна змінна видима в межах залишку вихідного файлу, в якому вона визначена. Вище свого опису і в
інших вихідних файлах ця змінна невидима (якщо тільки вона не оголошена з класом extern).
Глобальна змінна може бути визначена тільки один раз у межах своєї області видимості. В іншому вихідному
файлі може бути оголошена інша глобальна змінна з таким же ім'ям, конфлікту при цьому не виникає, оскільки
кожна з цих змінних буде видимою тільки у своєму вихідному файлі.
Специфікатор класу пам'яті extern для глобальних змінних використовується, як і для локального оголошення,
в якості посилання на змінну, оголошену в іншому місці програми, тобто для розширення області видимості
змінної. При такому оголошенні область видимості змінної розширюється до кінця вихідного файлу, в якому
зроблено оголошення.
Специфікатор класу пам'яті static для глобальних змінних вказує, що з інших файлів програми не можна
отримати доступ до цієї змінної, навіть з використанням специфікатора extern. Область видимості такої змінної
завжди обмежена файлом.
1. Функція, оголошена як static, видима в межах того файлу, в якому вона визначена.
Кожна функція може викликати іншу функцію з класом пам'яті static з свого вихідного файлу, але не може
викликати функцію визначену з класом static в іншому вихідному файлі.
Різні функції з класом пам'яті static можуть мати однакові імена , якщо вони визначені в різних вихідних файлах,
і це не веде до конфлікту.
2. Функція, оголошена з класом пам'яті extern, видима в межах всіх вихідних файлів програми. Будь-яка
функція може викликати функції з класом пам'яті extern.
3. Якщо в оголошенні функції відсутній специфікатор класу пам'яті, то за замовчуванням приймається клас
extern.
x=5y=1
MySwap();
Значення розмір_масиву при оголошенні масиву може бути не вказано в таких випадках:
• при оголошенні масив ініціалізується;
• масив оголошено як формальний параметр функції (докладніше див. далі);
• масив оголошено як посилання на масив, явно визначений в іншому модулі.
Національний університет «Львівська політехніка»
Використовуючи ім’я масиву та індекс, можна звертатися до елементів масиву:
ім’я_масиву [значення_індексу]
Значення індексів перебувають в діапазоні від нуля до величини, на одиницю меншу за розмір масиву, визначений
при його оголошенні, оскільки в С нумерація індексів розпочинається з нуля.
Наприклад,
int A[10];
оголошує масив з ім’ям A, який містить 10 цілих чисел; при цьому виділяє і закріплює за цим масивом пам’ять для
усіх 10-ти елементів відповідного типу (int – 4 байти), тобто 40 байтів.
Отже, при оголошенні масиву виділяється пам’ять, потрібна для розташування усіх його елементів.
Елементи масиву з першого до останнього запам’ятовуються у послідовно зростаючих адресах пам’яті.
Поміж елементами масиву в пам’яті проміжків немає.
Елементи масиву запам’ятовуються один за одним поелементно.
Зауважте на звертання у програмі до елементів масиву: А[0] – перший елемент, А[1] – другий, А[9] – останній.
Так, для розміщення елементів одновимірного масиву int В[5] виділяється по 4 байти під кожен з 5-ти елементів
масиву – тобто усього 20 байт:
Вводити чи виводити значення одновимірних масивів можна лише поелементно, для чого слід організовувати цикли
зі зміненням значень індексу зазначеного масиву.
Для оголошування масивів можна використовувати типізовані констант-масиви, які дозволяють водночас і оголосити
масив, і задати його значення як константи:
сonst тип_даних ім’я_масиву [розмір_масиву] = {значення_елементів_масиву};
Слід пам’ятати, що змінювати значення елементів констант-масивів не припустимо.
Приклади створення констант-масивів:
const int arr[5]={9,3,–7,0,123}; //масив з 5-ти цілих чисел
const float f[5]={1.5,6,8,7.4,–1.125}; //масив з 5-ти дійсних чисел
const С[5]={15,–6,546}; //масив з 5-ти цілих чисел типу int, // де С[0]=15, С[1]= –6, С[2]=546, С[3]=0 і С[4]=0
int main(void)
{
int iSum = 0, ar[4] = { 7, 4, 2, 1 };
float dSum = 0, set[3] = { 2.3, 1, 4.5 };
// Calculation with array elements:
iSum = ar[0] + ar[1] + ar[2] + ar[3];
dSum = set[0] + set[1] + set[2];
return 0;
}
#include <stdio.h>
int main(void)
{
int size, val, ar[3] = { 1, 2 };
size = sizeof(ar) / sizeof(int);
val = ar[2];
printf("size = %d val = %d\n", size, val);
return 0;
}
ввід n
ввід ai
i від 1 до n-1
ні
з кроком 1
так
ai > max
вивід
max
так
max = ai ні
кінець
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
int main(void)
{
const int n = 10;
int a[n], i, max;
for (i = 0; i < n; i++)
{
printf("Enter a[%d]:", i);
scanf("%d", &a[i]);
}
max = a[0];
for (i = 1; i < n; i++)
{
if (a[i] > max)
max = a[i];
}
printf("\nmax=%d", max);
return 0;
}
max = x0
int SearchMaxArray(int x[], int size)
{
int i, max; i від
1 до size-1
max = x[0]; з кроком 1
for (i = 1; i < size; i++)
так
{
if (x[i] > max) xi > max
max = x[i];
} так
return max;
} max = xi ні ні
кінець
підпрограми
пошуку найбільшого
Національний університет «Львівська політехніка» Рисунок 4.3 – Блок-схема алгоритму пошуку найбільшого числа в масиві
початок
Тепер, маючи
ввід n
окремі
підпрограми
для вводу і
пошуку підпрограма вводу
масиву а
найбільшого InputArray(a, n);
елемента
масиву, можна
перерисувати
підпрограма пошуку
блок-схему найбільшого
алгоритму max = SearchMaxArray(a, n)
знаходження
найбільшого.
вивід
max
кінець
Кінець
i = i +1
сортування
Національний університет «Львівська політехніка» Рисунок 4.5 – Блок-схема алгоритму сортування методом «бульбашки»
Лістинг 4.17.: Підпрограма сортування за зростанням методом “бульбашки”
void BubbleSort(int x[], int size)
{
int i, j, temp;
for (i = 1; i < size; i++)
for (j = 0; j < (size - i); j++)
if (x[j] > x[j + 1])
{
temp = x[j];
x[j] = x[j + 1];
x[j + 1] = temp;
}
}
xi > xj
так
temp = xi
xi = xj ні
ні
xj = temp
j=j+1
Кінець
i = i +1
сортування
Національний університет «Львівська політехніка» Рисунок 4.6 – Блок-схема алгоритму сортування методом вставки
Лістинг 4.18.: Підпрограма сортування за зростанням методом вставки
void InsertSort(int x[], int size)
{
int i, j, temp;
for (i = 0; i < size - 1; i++)
for (j = i; j < size; j++)
if (x[i] > x[j])
{
temp = x[i];
x[i] = x[j];
x[j] = temp;
}
}
temp = xmax
пошуку найбільшого значення у так xmax = xn-i
послідовності, трішки max = i ні
xn-i = temp
i<m i<m
i = від 0 до m -1 i = від 0 до m -1
з кроком 1 з кроком 1
так так
так так
j=0 j=0
j = від 1 до n - 1 j = від 1 до n - 1
ні ні з кроком 1 з кроком 1
j<n j<n
так так
так так
ввід вивід
ввід вивід xij ні xij ні
xij xij
ні ні
j = j +1 j = j +1 ні ні
Рисунок 4.8 – Блок-схеми алгоритмів вводу і виводу елементів Рисунок 4.9 – Блок-схеми алгоритмів вводу і виводу
матриці елементів матриці з використанням символу підготовка
Для генерування елементів масиву можна скористатись функцією випадкового генерування чисел.
так
ні
j = j +1
кінець підпрограми
i=i+1
сумування матриць
𝑏𝑖𝑗 = 𝑘 ∗ 𝑎𝑖𝑗
j<n
так
bij = k*aij
ні
j = j +1
кінець підпрограми
i=i+1 множення матриці
на число
Дві матриці можна перемножити між собою тоді і тільки тоді, коли кількість стовпців першої матриці дорівнює
кількості рядків другої матриці.
𝑏1
𝑎1 𝑎2 … 𝑎𝑛 ∗ 𝑏⋯2 = 𝑐
𝑏𝑛
𝑐 = 𝑎𝑖 ∗ 𝑏𝑖 + 𝑎𝑖 ∗ 𝑏𝑖 + ⋯ + 𝑎𝑛 ∗ 𝑏𝑛
Національний університет «Львівська політехніка»
початок підпрограми початок підпрограми
множення матриць
множення матриць
i=0
i = від 0 до m-1
з кроком 1
i<m
так
так
j = j +1
кінець підпрограми
множення матриць
i=i+1 кінець підпрограми
множення матриць
Національний університет «Львівська політехніка»
Лістинг 4.29.: Функція множення матриці на матрицю
ділянки.
10004
Існують спеціальні змінні – вказівники, в яких можна зберігати
адреси комірок пам’яті, тобто адреси змінних. 10005
тип *ім’я;
Тут тип – це базовий тип вказівника, котрий може бути яким завгодно типом. Ім’я є ідентифікатором змінної-вказівника.
Слід звернути увагу на те, що тип – це не тип вказівника, а тип даних, адресу яких буде записано у вказівнику.
Наприклад, вказівник на пам’ять, в якій зберігатиметься ціле число: int* А;
Вказівник на дійсне число: float* В;
При цьому змінна А – це адреса ділянки пам’яті, в якій розташоване ціле число, В – адреса ділянки пам’яті, в якій
розташоване дійсне число. Обидві змінні – А та В – є вказівниками, тобто їхніми значеннями є адреси. Окрім того,
адреси змінних типу int та float займають у пам’яті однакову кількість байтів. Але насправді змінні А та В мають
різний тип: А – вказівник на ціле, В – вказівник на дійсне.
Вказівникові можна присвоїти лише адресу змінної відповідного типу, оскільки мова С не виконує автоматичного
перетворення типів вказівників.
Тому такі команди є помилковими:
A = B; // Не можна переприсвоювати один одному вказівники на різні типи;
А = 10; // не можна присвоювати числа вказівникам.
#include <stdio.h>
void MySwap(int* pM, int* pN);
int main(void)
{
int M = 1, N = 5;
printf("Initial values:\n M = %d N = %d\n\n", M, N);
MySwap(&M, &N);
printf("After MySwap() call:\n M = %d N = %d\n\n", M, N);
return 0;
}
void MySwap(int* pM, int* pN)
{
int temp;
temp = *pM;
*pM = *pN;
*pN = temp;
}
10 -2 0 40 3
10 -2 0 40 3
#include <stdio.h>
int main(void)
{
int ar[5] = { 1, 2, 3, 4, 5 };
int Sum, i;
/*---- Indexing ----*/
Sum = 0;
for (i = 0; i < 5; i++)
Sum = Sum + ar[i];
printf("Indexing: Sum = %d\n", Sum);
/*-- Pointer syntax --*/
Sum = 0;
for (i = 0; i < 5; i++)
Sum = Sum + *(ar + i);
printf("Pointer syntax: Sum = %d\n", Sum);
return 0;
}
arr
#include <stdio.h>
#include <stdlib.h>
int data_cmp(const void* p1, const void* p2);
int main(int argc, char* argv[])
{
int data[] = { 12, 4, 1961, 17, 7, 1969, 27, 8, 1856, 1, 9, 1939, 22, 6, 1942 };
int i; int datacount = sizeof(data) / sizeof(int);
printf("Before sorting:\n");
for (i = 0; i < datacount; i++)
printf("%6d\n", data[i]);
qsort(data, datacount, sizeof(int), data_cmp);
printf("\nAfter sorting:\n");
for (i = 0; i < datacount; i++)
printf("%6d\n", data[i]);
return 0;
}
int data_cmp(const void* p1, const void* p2)
{
int x = *(int*)p1; int y = *(int*)p2;
return (x < y) ? -1 : ((x == y) ? 0 : 1);
}
Національний університет «Львівська політехніка»
5.6. Динамічна пам’ять
Окрім звичайної пам’яті (стека), в якій автоматично розміщуються змінні за їхнього оголошення, існує ще й динамічна
пам’ять (heap − купа), в якій змінні можуть розміщуватися динамічно.
Це означає, що пам’ять виділяється під час виконання програми, й лише тоді, коли у програмі зустрінеться спеціальна
інструкція.
Основна потреба у динамічному виділенні пам’яті виникає, коли розмір або кількість даних заздалегідь є невідомі, а
визначаються в процесі виконання програми.
Існують функції, які дозволяють керувати динамічним розподілом пам’яті. Щоб їх використовувати треба підключити
бібліотеку:
#include <malloc.h>
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14
0-й рядок 1-й рядок 2-й рядок
У такому масиві перші п’ять елементів належать до першого рядка, наступні п’ять – до другого і останні п’ять – до
третього.
0 0 1 2 3 4 0-й рядок
1 0 1 2 3 4 1-й рядок
2 0 1 2 3 4 2-й рядок
При цьому буде виділено пам’ять під кожний рядок матриці окремо, тобто буде утворено три різні одновимірні
масиви.
Адреси нульових елементів цих масивів зберігатимуться в допоміжному масиві a (пам’ять під нього слід виділити
заздалегідь).
Елементами цього масиву будуть адреси дійсних чисел, тому вони матимуть тип “вказівник на дійсне число”, тобто
float*.
Нагадаємо, що загальний вигляд оголошення вказівника на динамічний масив є такий:
тип елемента* ім’я масиву;
Після цього можна працювати з матрицею як зі звичайним двовимірним масивом, звертаючись до кожного елемента
за його індексом, наведеним у квадратних дужках: a[i][j], – що є більш природним і зручним, аніж попередній
спосіб.
Звільнення пам’яті необхідно виконувати в зворотному порядку – спочатку звільнити пам’ять під рядки в циклі, а потім
звільнити пам’ять, що виділялась під масив вказівників.
for (int i = 0; i < 3; i++)
free(a[i]);
free(a);
1 7 4
0 9 4
Matrix B:
8 8 2 4
5 5 1 7
1 1 5 2
Matrix C:
47 47 29 61
49 49 29 71
str w o r l d \0 \0 \0 \0 \0
При цьому виділиться пам’ять під масив з 9-ти елементів та 10-й – нуль-символ (всього 10 байт) і перші 5 символів
рядка записуються в перші 5 байт цієї пам’яті (str[0] = 'w', str[1] = 'o', str[2] = 'r', str[3] = 'l',
str[4] = 'd'), а в шостий елемент str[5] записується нуль-символ. Якщо рядок при оголошенні ініціалізується,
його розмірність можна опускати (компілятор сам виділить потрібну кількість байтів):
char str[] = "world"; // Виділено й заповнено 6 байтів
str w o r l d \0
При роботі даної програми працюємо з двома змінними – рядком str1 і масивом символів str2. При спробі
вивести змінну str2 в якості рядка на екрані отримаємо після символів hj ще якісь значення, оскільки функція
виводу буде вважати рядком все, поки не зустрінеться нуль-символ.
str1 H e l l o , \32 \n \0
str2 h j
При роботі даної програми працюємо з двома змінними – рядком str1 і str2 :
str1
Z h o v t i \32 V o d y \32 L v i v \0 ... \0
Before concatenation:
str1 --> Zhovti Vody len = 11
After concatenation:
str1 --> Zhovti Vody L'viv len = 17
При роботі даної програми працюємо з двома змінними – масивом рядків StrArr і рядком Buf:
strArr H e l l o \0 \0 \0 ... \0
w o r l d \0 \0 \0 ... \0
I \32 a m \32 C \0 \0 ... \0
buf ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ... ?
Hello
world
I am C
Hello, world - I am C
str1 \0 \0 \0 \0 \0 \0 \0 \0 \0 \0 \0 \0 ... \0
str2 L v i v \0
str1 L v \0 \0 \0 \0 \0 \0 \0 \0 \0 ... \0
str1 \0 \0 \0 \0 \0 \0 \0 \0 \0 \0 \0 \0 ... \0
str2 L v i v \0
pAux pAux++
Після виконання функції strncpy() в рядку str1 буде наступне:
str1 v i \0 \0 \0 \0 \0 \0 \0 \0 \0 ... \0
До зазначених специфікаторів наприкінці чи перед знаком "+" може дописуватись символ чи то "t" – для текстових
файлів, чи "b" – для бінарних (двійкових) файлів.
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
int main(void)
{
FILE* pf;
pf = fopen("MyFileName", "wb");
if (pf != 0)
{
fputc('A', pf);
fputc('B', pf);
fclose(pf);
}
return 0;
}
FILE* f;
f = fopen("fname.txt", "at+");
// Записування С-рядка s
fputs(s, f);
//Форматоване записування С-рядка nazva, дійсного числа price і цілого числа kol
fprintf(f, "%s %6.2f %i\n", nazva, price, kol);
3) Закрити файл:
fclose(f);
FILE* f;
f = fopen("fname.txt", "rt+");
Для зчитування послідовно всіх даних з файла треба організувати цикл з умовою, яка перевіряє досягання кінця файла,
використовуючи чи то функцію feof(f), яка є ідентична до true при досяганні кінця файла, чи функцію зчитування
даних, яка дає нульовий результат за досягання кінця файла. Можна використовувати інформацію про сумарний розмір
файла і контролювати досягання кінця файла функцією ftell(f).
3) Закрити файл:
fclose(f); Національний університет «Львівська політехніка»
Лістинг 7.4.: Файли, fputs(), fgets()
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
int main(void)
{
FILE* pf;
char str1[] = "Hello,\n"; char str2[] = " world!\n";
char Str1[128] = ""; char Str2[128] = "";
/*------- file writing -------*/
pf = fopen("MyFileName", "wt");
if (pf != 0)
{
fputs(str1, pf);
fputs(str2, pf);
fclose(pf);
}
/*------- file reading -------*/
pf = fopen("MyFileName", "rt");
if (pf != 0)
{
fgets(Str1, sizeof(Str1), pf);
fgets(Str2, sizeof(Str2), pf);
fclose(pf);
}
printf("Str1 = %sStr2 = %s\n", Str1, Str2);
return 0;
}
Національний університет «Львівська політехніка»
Лістинг 7.5.: Файли, fgets() (2)
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
int main(void)
{
FILE* pf;
char Buf[128] = "";
char* pch;
/*------- file reading -------*/
pf = fopen("MyFileName", "rt");
if (pf != 0)
{
pch = fgets(Buf, sizeof(Buf), pf);
while (pch != 0)
{
printf("Str1 = %s", Buf);
pch = fgets(Buf, sizeof(Buf), pf);
}
fclose(pf);
}
return 0;
}
Функція повертає кількість реально записаних елементів, яка може бути менше за numElems, якщо при записуванні
виникла помилка: приміром, не вистачило вільного простору на диску.
Так само, як і в текстових, у бінарних файлах можна визначати позицію зчитування-записування і переміщувати її у
довільне місце файла командами відповідно ftell() та fseek(). Можливість переміщувати позицію є корисна для
файлів, які складаються з однорідних записів однакового розміру. Приміром, якщо у файлі записано лише дійсні числа
типу double, то для того, щоб прочитати i-тe число, слід виконати оператори:
fseek(F, sizeof(double)* (i - l), 0);
fread(&а, sizeof(double), 1, F);
Ім’я типу структури задається програмістом виходячи зі змісту даних, які зберігатимуться у структурі. Список змінних
наприкінці оголошення структури може бути відсутнім, у цьому разі після фігурної дужки має бути крапка з комою.
Національний університет «Львівська політехніка»
Наведемо приклад оголошення структури з ім’ям sportsman, яка поєднає в собі інформацію про змагання
спортсменів: прізвище і вік спортсмена, країна, кількість його очок, утворюючи відповідні поля:
struct sportsman
{
char name[32];
char country[32]; // Прізвище та країна,
int age;
float rating; // вік та кількість очок.
};
Як наслідок цього оголошення створено новий тип sportsman. Тепер у програмі цей тип може використовуватись
нарівні зі стандартними типами для оголошення змінних. Оголошені змінні типу sportsman матимуть чотири поля: два
рядки (name і country), ціле (age) і дійсне (rating) числа.
sportsman Sp; /* Змінна Sp типу sportsman може зберігати інформацію про одного спортсмена. */
sportsman Mas[15]; /* Масив Mas типу sportsman може містити інформацію про 15 спортсменів. *
При оголошуванні змінної типу структури пам’ять під усі поля структури виділяється послідовно для кожного поля. У
наведеному прикладі структури sportsman під змінну Sp послідовно буде виділено 32, 32, 4, 4 байти – усього 72
байти. Поля змінної Sp у пам’яті буде розміщено у такому порядку:
0 1 2 3 4 5 6 … 31 0 1 2 3 4 5 6 … 31 0 1 2 3 0 1 2 3
char name[32] char country[32] int age float rating
Слід зазначити, що сумарну пам’ять, яка виділяється під структуру, може бути збільшено компілятором на вимогу
процесора для так званого вирівнювання адрес. Реальний обсяг пам’яті, яку займає структура, можна визначити за
допомогою sizeof().
Національний університет «Львівська політехніка»
При оголошуванні структур їхні поля можна ініціалізовувати початковими значеннями.
Наприклад, оголошення змінної Sp типу sportsman й ініціалізація її даними про 20-річного спортсмена з України за
прізвищем Бойко, який набрав 75.3 очок, матиме вигляд:
sportsman Sp = { "Бойко", "Україна", 20, 75.3 };
Оголошення типу “точка на площині” POINT з полями-координатами – x та у і змінної spot цього типу – точки з
координатами (20, 40) може бути таким:
struct POINT
{
int x, у;
}
spot = { 20, 40 }; // Точка має координати x = 20, у = 40
При ініціалізації масивів структури кожен елемент масиву слід записувати у фігурних дужках, наприклад при
створенні двовимірного масиву x розміром 2х3 типу структури complex з полями real та im ініціалізація початкових
значень елементів може мати вигляд:
struct complex
{
float real, im;
} x[2][3] = { {{1.4, 7}, {5, 3}, {-3.1, -1.1}},{{2, -6.7}, {-5.2}, {0, 6}} };
strcpy(Sp.name, "Бойко");
strcpy(Sp.country, "Україна");
Sp.age = 20;
Sp.rating = 75.3;
Можна створювати тип структури за допомогою typedef, наприклад:
typedef struct
{
char name[32], group[6];
int Bal_math, Bal_inform;
} student; // Оголошення типу “студент”
student W; // і змінної W цього типу.
Тут означено тип структури за ім’ям student для зберігання даних про успішність студентів з полями: прізвище, група,
екзаменаційні оцінки з математики та інформатики й оголошено змінну W цього типу.
22 21 20 … 1 0 3 2 1 0 4 3 2 1 0
year month day