0% found this document useful (0 votes)
15 views41 pages

Тема7

Uploaded by

Chiter 24
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
15 views41 pages

Тема7

Uploaded by

Chiter 24
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 41

Лекції №27, 28, 29, 30.

Тема №7.
РОБОТА З ДИСКОВИМИ ФАЙЛАМИ
Автор курсу: старший викладач кафедри ЕОМ НУ «ЛП» Ногаль Марія Василівна
7.1. Загальні відомості про файли
Файлами є іменовані області пам’яті на зовнішньому носії, призначені для довготривалого зберігання інформації.
Файли мають імена й є організовані в ієрархічну деревовидну структуру з каталогів (тек) і простих файлів.
Для доступу до даних файла з програми в ній слід прописати функцію відкриття цього файла, тим самим встановити
зв’язок поміж ім’ям файла і певною файловою змінною у програмі.
Файли відрізняються від звичайних масивів тим, що
• вони можуть змінювати свій розмір;
• звертання до елементів файлів здійснюється не за допомогою операції індексації [], а за допомогою спеціальних
системних викликів та функцій;
• доступ до елементів файла відбувається з так званої позиції зчитування-записування, яка автоматично
просувається при операціях зчитування-записування, тобто файл є видимим послідовно. Існують, щоправда,
функції для довільного змінювання цієї позиції.
Часто використовується така метафора: якщо уявляти собі файли як книжки (лише зчитування) і блокноти (зчитування
й записування), які стоять на полиці, то відкриття файла – це вибір книжки чи блокнота за заголовком на його
обкладинці й відкриття обкладинки (на першій сторінці). Після відкриття можна читати, дописувати, викреслювати і
правити записи, перегортати книжку. Сторінки можна зіставити з блоками файла, а полицю з книжками – з каталогом.
С надає засоби опрацювання двох типів файлів: текстових та бінарних.

Національний університет «Львівська політехніка»


Текстові файли призначено для зберігання текстів, тобто сукупності символьних рядків змінної довжини.
Кожен рядок завершується керувальною послідовністю '\n', а розділювачами слів та чисел у рядку є пробіли й
символи табуляції.
Оскільки вся інформація текстового файла є символьною, програмне опрацювання такого файла полягає в читанні
рядків, виокремлюванні з рядка слів і, за потреби, перетворюванні цифрових символьних послідовностей на числа
відповідними функціями перетворювання.
Створювати, редагувати текстові файли можна не лише в програмі, а й у якому завгодно текстовому редакторі,
наприклад Блокноті чи Word.
Бінарні файли зберігають дані в тому самому форматі, в якому вони були оголошені, і їхній вигляд є такий самий, як
і в пам’яті комп’ютера. І тому відпадає потреба у використанні розділювачів: пробілів, керувальних послідовностей, а
отже, обсяг використовуваної пам’яті порівняно з текстовими файлами з аналогічною інформацією є значно меншим.
Окрім того, немає потреби у застосуванні функцій перетворення числових даних. Але кожне опрацювання даних
бінарних файлів можливе лише за наявності програми, якій має бути відомо, що саме і в якій послідовності
зберігається у цьому файлі.

Національний університет «Львівська політехніка»


7.2. Робота з текстовими файлами
У мові С файл розглядається як потік послідовності байтів. Інформація про файл заноситься до змінної типу FILE*. Цей
тип оголошує вказівник потоку, який використовується надалі у всіх операціях з цим файлом. Тип FILE визначено у
бібліотеці <stdio.h>. Тому, якщо в програмі передбачається робота з файлами, цей заголовний файл слід долучити:
#include <stdio.h>
Тепер можна оголосити файлову змінну – вказівник потоку, який в подальшому буде передаватися у функції введення-
виведення у якості параметра:
FILE* f;
Функція fopen() для відкривання файла має такий синтаксис:
FILE* fopen(const char* filename, const char* mode);
Перший параметр filename визначає ім’я файла, який відкривається. Другий параметр mode задає режим відкривання
файла.

Національний університет «Львівська політехніка»


Таблиця 7.1 – Специфікатори режиму відкриття файлів
Параметр Опис
r Відкрити файл для читання даних
r+ Відкрити файл для читання і запису даних
a Відкрити чи створити файл для запису даних в кінець файлу
a+ Відкрити чи створити файл для читання і запису даних в кінець файлу
w Створити файл для запису даних
w+ Створити файл для читання і запису даних

До зазначених специфікаторів наприкінці чи перед знаком "+" може дописуватись символ чи то "t" – для текстових
файлів, чи "b" – для бінарних (двійкових) файлів.

Національний університет «Львівська політехніка»


Функція fopen() повертає вказівник на об’єкт, який керує потоком. Наприклад, створити файл з ім’ям [Link] можна
так:
FILE* f;
f = fopen("[Link]", "wt");
Якщо файл відкрити не вдалося, fopen() повертає нульовий вказівник NULL. Для уникання помилок після відкриття
файла слід перевірити, чи насправді файл відкрився:
if(f == NULL)
{
printf("Error!\n");
return;
}
Безпечна версія цієї функції
errno_t fopen_s(FILE * *pFile, const char* filename, const char* mode);
повертає нуль у разі успіху або код помилки в разі невдачі.
Наприклад, створити файл з ім’ям [Link] можна так:
int err = fopen_s(&f, "[Link]", "wt");
if (err != 0) { printf("Error!\n"); return; }
Національний університет «Львівська політехніка»
Припинити роботу з файлом можна за допомогою функції
int fclose(FILE * stream);
int _fcloseall(void);
Функція fclose() закриває файл, на який посилається параметр функції.
Функція _fcloseall() закриває усі відкриті файли.
Перевірка кінця файла здійснюється функцією feof():
int feof(FILE * stream);
Наведемо приклад відкривання текстового файла для зчитування.
FILE* f;
// Перевіряємо, чи повертає ця функція нульовий вказівник
if ((f = fopen("[Link]", "rt")) == NULL)
{
printf("Error");
return;
}
// Читання даних з файла.
fclose(f); // Закриття файла.

Національний університет «Львівська політехніка»


Лістинг 7.1.: Файли
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
int main(void)
{
FILE* pf;
pf = fopen("MyFileName", "wb");
fclose(pf);
return 0;
}

Національний університет «Львівська політехніка»


З текстового файла можна читати цілі рядки й окремі символи.
Зчитування рядка з файла здійснюється функцією fgets():
char* fgets(char* str, int numChars, FILE * stream);
де str – перший параметр – рядок типу char*;
numChars – максимальна кількість читаних символів (байтів);
stream – вказівник на потік даних файла.
Записування даних до текстового файла можна здійснювати за допомогою функції fputs() :
int fputs(const char* str, FILE * stream);
де str – рядок типу char,
stream – файловий потік.
Для записування даних до файла слід відкрити файл у форматі записування даних у кінець файла, за потреби
перетворити рядок на тип char* і записати дані до файла.

Національний університет «Львівська політехніка»


Лістинг 7.2.: Файли, fputc()

#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;
}

Національний університет «Львівська політехніка»


Лістинг 7.3.: Файли, fgetc()
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
int main(void)
{
FILE* pf;
char a, b;
pf = fopen("MyFileName", "rb");
if (pf != 0)
{
a = fgetc(pf);
b = fgetc(pf);
fclose(pf);
}
printf("First is %c; Second is %c", a, b);
return 0;
}

Вмістиме файла MyFileName


AB
Результати роботи програми:
First is A; Second is B
Національний університет «Львівська політехніка»
Зчитування форматованих даних можна також здійснювати за допомогою функції fscanf():
int fscanf(FILE * stream, const char* format[, argument]);
Записування до текстового файла можна здійснити також за допомогою функції fprintf():
int fprintf(FILE * stream, const char* format[, argument]);
Текстові файли дозволяють переміщувати поточну позицію зчитування-запису. Для визначення поточної позиції
файла, яка автоматично зміщується на кількість опрацьованих байт, використовується функція ftell():
long int ftell(FILE * stream);
A змінити поточну позицію файла можна за допомогою функції fseek():
int fseek(FILE * stream, long offset, int whence);
Ця функція задає зсув на offset байтів щодо точки відліку, яка задається параметром whence. Параметр whence
може набувати таких значень: 0 – початок файлу, 1 - поточна позиція, 2 – кінець файлу.
Наприклад, для переміщення поточної позиції на початок файла можна скористатися функцією
fseek(f, 0, 0);
За допомогою функцій ftell() та fseek() можна визначити сумарний обсяг пам’яті у байтах, який займає файл. Для
цього достатньо переміститися на кінець файла:
fseek(f, 0, 2);
int d = ftell(f);

Національний університет «Львівська політехніка»


Отже, послідовність роботи з файлом при записуванні даних є така:
1) Відкрити чи то створити файл [Link] для записування (або для зчитування й записування) у кінець файла:

FILE* f;
f = fopen("[Link]", "at+");

2) Записати дані до файла f однією з команд:

// Записування С-рядка s
fputs(s, f);
//Форматоване записування С-рядка nazva, дійсного числа price і цілого числа kol
fprintf(f, "%s %6.2f %i\n", nazva, price, kol);

3) Закрити файл:

fclose(f);

Національний університет «Львівська політехніка»


Для зчитування даних з файла треба виконати таку послідовність дій:
1) Відкрити файл для зчитування

FILE* f;
f = fopen("[Link]", "rt+");

2) Здійснити зчитування даних з файла однією з команд:

// Зчитування С-рядка s довжиною у 80 символів


fgets(s, 80, f);
// Форматоване зчитування за адресами відповідних змінних
fscanf(f, "%s%f%i\n", &nazva, &price, &kol);

Для зчитування послідовно всіх даних з файла треба організувати цикл з умовою, яка перевіряє досягання кінця файла,
використовуючи чи то функцію 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;
}

Національний університет «Львівська політехніка»


7.3. Робота з бінарними файлами
У бінарному (двійковому) файлі число, на відміну від текстового, зберігається у внутрішньому його поданні.
У двійковому форматі можна зберігати не лише числа, а й рядки та цілі інформаційні структури. Причому останні
зберігати зручніше, завдяки тому що відсутня потреба явно зазначати кожен елемент структури, а зберігається вся
структура як цілковита одиниця даних. Хоча цю інформацію не можна прочитати як текст, вона зберігається більш
компактно і точно.
Тому, що саме і в якій послідовності розміщено в бінарному файлі, має бути відомо програмі, яка цей файл
опрацьовує.
Функція fread() читає інформацію у вигляді потоку байтів і в незмінному вигляді розміщує її в пам’яті.
Слід розрізнювати текстове подавання чисел і їхнє бінарне подавання.
size_t fread(
char* buffer, // Масив для зчитування даних,
size_t elemSize, // розмір одного елемента,
size_t numElems, // кількість елементів для зчитування
FILE* f); // і вказівник потоку
Тут size_t означений як беззнаковий цілий тип у системних заголовних файлах. Функція намагається прочитати
elemSize елементів з файла, який задається вказівником f, розмір кожного елементу становить elemSize. Функція
повертає реальну кількість прочитаних елементів, яка може бути менше за numElems, у разі завершення файла чи
помилки зчитування.

Національний університет «Львівська політехніка»


Функція бінарного записування до файла fwrite() є аналогічна до функції зчитування fread(). Вона має такий
прототип:
size_t fwrite(
char* buffer, // Масив записуваних даних,
size_t elemSize, // розмір одного елемента,
size_t numElems, // кількість записуваних елементів
FILE* f); // і вказівник потоку

Функція повертає кількість реально записаних елементів, яка може бути менше за numElems, якщо при записуванні
виникла помилка: приміром, не вистачило вільного простору на диску.
Так само, як і в текстових, у бінарних файлах можна визначати позицію зчитування-записування і переміщувати її у
довільне місце файла командами відповідно ftell() та fseek(). Можливість переміщувати позицію є корисна для
файлів, які складаються з однорідних записів однакового розміру. Приміром, якщо у файлі записано лише дійсні числа
типу double, то для того, щоб прочитати i-тe число, слід виконати оператори:
fseek(F, sizeof(double)* (i - l), 0);
fread(&а, sizeof(double), 1, F);

Національний університет «Львівська політехніка»


Лістинг 7.6.: Файли, fwrite(), fread() (1)
#define _CRT_SECURE_NO_WARNINGS Результати роботи програми:
#include <stdio.h> iRes is 5; dRes is 8.880000
int main(void)
{
FILE* pf;
int m = 5; double d = 8.88;
int iRes; double dRes;
/*------- file writing -------*/
pf = fopen("MyFileName", "wb");
if (pf != 0)
{
fwrite(&m, sizeof(int), 1, pf);
fwrite(&d, sizeof(double), 1, pf);
fclose(pf);
}
/*------- file reading -------*/
pf = fopen("MyFileName", "rb");
if (pf != 0)
{
fread(&iRes, sizeof(int), 1, pf);
fread(&dRes, sizeof(double), 1, pf);
fclose(pf);
}
printf("iRes is %d; dRes is %f", iRes, dRes);
return 0;
}
Національний університет «Львівська політехніка»
Лістинг 7.7.: Файли, fwrite(), fread() (2)
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h> Результати роботи програми:
int main(void) iArrRes[0] = 5; dArrRes[0] = 8.880000
{ iArrRes[1] = 6; dArrRes[1] = 9.900000
FILE* pf; int i; iArrRes[2] = 7; dArrRes[2] = 1.000000
int iArr[3] = { 5, 6, 7 };
double dArr[3] = { 8.88, 9.9, 1 };
int iArrRes[3]; double dArrRes[3];
/*------- file writing -------*/
pf = fopen("MyFileName", "wb");
if (pf != 0){
fwrite(&(iArr[0]), sizeof(int), 3, pf);
fwrite(&(dArr[0]), sizeof(double), 3, pf);
fclose(pf);
}
/*------- file reading -------*/
pf = fopen("MyFileName", "rb");
if (pf != 0){
fread(&(iArrRes[0]), sizeof(int), 3, pf);
fread(&(dArrRes[0]), sizeof(double), 3, pf);
fclose(pf);
}
for (i = 0; i < 3; i++)
printf("iArrRes[%d] = %d; dArrRes[%d] = %f\n", i, iArrRes[i], i, dArrRes[i]);
return 0;
}
Національний університет «Львівська політехніка»
Лістинг 7.8.: Файли, fwrite(), fread() (3)
#define _CRT_SECURE_NO_WARNINGS Результати роботи програми:
#include <stdio.h> iArrRes[0] = 5
int main(void)
iArrRes[1] = 6
{
FILE* pf; int i = 0, len; iArrRes[2] = 7
int iArr[3] = { 5, 6, 7 }; iArrRes[3] = 0
int iArrRes[8] = { 0 }; iArrRes[4] = 0
/*------- file writing -------*/ iArrRes[5] = 0
pf = fopen("MyFileName", "wb");
iArrRes[6] = 0
if (pf != 0) fwrite(&(iArr[0]), sizeof(int), 3, pf);
fclose(pf); iArrRes[7] = 0
/*------- file reading -------*/
pf = fopen("MyFileName", "rb");
if (pf != 0)
{
len = fread(&(iArrRes[i]), sizeof(int), 1, pf);
while (len != 0 && i < 7)
{
i++;
len = fread(&(iArrRes[i]), sizeof(int), 1, pf);
}
fclose(pf);
}
for (i = 0; i < 8; i++)
printf("iArrRes[%d] = %d\n", i, iArrRes[i]);
return 0;
} Національний університет «Львівська політехніка»
Лістинг 7.9.: Файли, найпростіша довідкова система
#define _CRT_SECURE_NO_WARNINGS Файл [Link]
#include <stdio.h> Kovalenko 1234567
#include <string.h> Honchar 1235467
int main(void) Kovalchuk 1236543
{ Tkachenko 1237322
FILE* pf; char* pch; Kovalskyj 2231234
Melnyk 2232341
char NameBuf[128] = ""; char ResultBuf[128] = "";
Honcharenko 2234301
printf("Enter a name, please: ");
Koval 4532345
gets_s(NameBuf, sizeof(NameBuf));
Kovaljuk 4533267
/* file reading and telephone number searching */ Tkach 4533385
pf = fopen("[Link]", "rt"); Melnychenko 4534358
if (pf != 0){
pch = fgets(ResultBuf, sizeof(ResultBuf), pf); Результати роботи програми:
while (pch != 0){ Enter a name, please: Tkach
pch = strstr(ResultBuf, NameBuf); -------------------------
if (pch != 0){ Tkachenko 1237322
printf("-------------------------\n"); -------------------------
printf("%s", ResultBuf); Tkach 4533385
} ==========================
pch = fgets(ResultBuf, sizeof(ResultBuf), pf);
The end.
}
fclose(pf);
printf("==========================\n");
printf("\nThe end.");
}
return 0;
} Національний університет «Львівська політехніка»
7.4. Структуровані типи даних
У попередніх розділах для зберігання даних розглянуто прості типи даних: числа, символи, рядки тощо. Але часто на
практиці перед програмістом постає завдання запрограмувати той чи інший об’єкт, який характеризується водночас
кількома параметрами, приміром, точка на площині задається парою дійсних чисел (x, y), а дані про людину можна
задавати кількома параметрами: прізвище, ім’я, по-батькові – рядки, рік народження – число тощо. Для поєднання
кількох параметрів в одному об’єктові в С існує спеціальний тип даних, який має назву структура. На відміну від
масивів, які також є структурованим типом даних і містять елементи одного типу, структури дозволяють поєднувати
дані різних типів.
Структура – це складений тип даних, змінні (поля) якого можуть містити різноманітну й різнотипну інформацію.
Опис структури має наступний синтаксис:
struct ім’я_типу_структури
{
тип1 поле1;
тип2 поле2;
. . .
типN полеN;
} список_змінних;

Ім’я типу структури задається програмістом виходячи зі змісту даних, які зберігатимуться у структурі. Список змінних
наприкінці оголошення структури може бути відсутнім, у цьому разі після фігурної дужки має бути крапка з комою.
Національний університет «Львівська політехніка»
Наведемо приклад оголошення структури з ім’ям 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}} };

Національний університет «Львівська політехніка»


Доступ до полів структури здійснюється за допомогою операції “крапка”:
cтруктурна_змінна.ім’я_поля
Наприклад, для розглянутої змінної Sp типу sporsman надання значень її полям може мати вигляд:

strcpy([Link], "Бойко");
strcpy([Link], "Україна");
[Link] = 20;
[Link] = 75.3;
Можна створювати тип структури за допомогою typedef, наприклад:

typedef struct
{
char name[32], group[6];
int Bal_math, Bal_inform;
} student; // Оголошення типу “студент”
student W; // і змінної W цього типу.
Тут означено тип структури за ім’ям student для зберігання даних про успішність студентів з полями: прізвище, група,
екзаменаційні оцінки з математики та інформатики й оголошено змінну W цього типу.

Національний університет «Львівська політехніка»


Структури можуть розміщуватись у динамічній пам’яті за допомогою оператора new і оголошуватися як вказівники.
У такому разі доступ до полів структури здійснюється за допомогою операції вибору – “стрілка” (->, на клавіатурі
послідовно натискаються клавіші “мінус” і “більше”):
sportsman* p = new sportsman; // Виділення пам’яті,
p->rating = 95.3; // присвоєння очок полю rating
(*p).age = 23; // і віку полю age.
Зверніть увагу на те, що якщо p – вказівник на структуру, то (*p) – сама структура і доступ до її полів здійснюється за
допомогою крапки. Якщо змінні типу “структура” оголошуються у програмі лише один раз, то, зазвичай, їх записують
наприкінці оголошення структури і при цьому ім’я структури можна явно не зазначати, наприклад:
struct
{
char name[32];
char country[10];
int age;
float rating;
}
Sp;
Тут оголошено змінну Sp як структуру з чотирма полями без створення типу “спортсмен”.

Національний університет «Львівська політехніка»


Поля структури можуть бути якого завгодно типу, у тому числі й масивом, покажчиком чи структурою, окрім типу тієї ж
самої структури (але можуть бути вказівником на неї).
За приклад створимо дві структури для опрацювання інформації про співробітників у відділі кадрів: прізвище, ім’я, по-
батькові співробітника, домашня адреса, домашній телефон, дата народження, дата вступу на роботу, зарплатня.
struct date
{
int day, month, year; // День, місяць та рік
};
struct person
{
char name[32], address[150]; // Прізвище, адреса,
char phone[10];
date birth_date;
// телефон, дата народження,
date work_date; // дата вступу на роботу
float salary; // та зарплатня.
};
У цьому прикладі оголошується новий тип – структура date для зберігання дати (день, місяць, рік). Окрім того,
оголошується тип – структура person, яка використовує тип date для полів birth_date і work_date. Посилання на
поля вкладеної структури формується з імені структурної змінної, імені структурного поля й імені поля вкладеної
структури. Те, що поля структур самі можуть бути структурами, надає можливість цілісно описувати доволі складні
об’єкти реального світу. Оскільки структури, на відміну від масивів, зберігають дані різних типів, їх відносять до
неоднорідних типів даних.
Національний університет «Львівська політехніка»
Оголосимо змінну створеного типу person :
person P;
Присвоювання прізвища у змінну P :
strcpy([Link], "Шкуропат");
Присвоювання дати народження 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->name, "Шкуропат");
(man->birth_date).day = 5;
(man->birth_date).month = 3;
(man->birth_date).year = 1987;
Оголосимо масив з даними про 100 осіб:
person P[100];
Попередні присвоювання для четвертої людини:
strcpy(P[3].name, "Шкуропат");
P[3].birth_date.day = 5;
P[3].birth_date.month = 3;
P[3].birth_date.year = 1987; Національний університет «Львівська політехніка»
Лістинг 7.10 : Сортування масиву календарних дат. Календарні дати представлені з
#include <stdio.h> допомогою структури date
#include <stdlib.h>
typedef
struct {
int d; //day
int m; //month
int y; //year
} date;
int date_cmp(const void* s1, const void* s2)
int date_cmp(const void* s1, const void* s2);
/* Compares dates. */
int main(int argc, char* argv[])
{
{
date* p1 = (date*)s1;
date dates[] = { { 12, 4, 1961 }, { 17, 7, 1969 },
date* p2 = (date*)s2;
{ 27, 8, 1856 }, { 1, 9, 1939 }, { 22, 6, 1942 },
int r;
{ 1, 12, 1991 }, { 1, 8, 1914 }, { 9, 3, 1814 } };
r = p1->y - p2->y;
int i;
if (!r)
int datescount = sizeof(dates) / sizeof(date);
{
printf("Before sorting:\n");
r = p1->m - p2->m;
for (i = 0; i < datescount; i++)
if (!r)
printf("%2d.%02d.%4d\n",
r = p1->d - p2->d;
dates[i].d, dates[i].m, dates[i].y);
}
qsort(dates, datescount, sizeof(date), date_cmp);
return r;
printf("\nAfter sorting:\n");
}
for (i = 0; i < datescount; i++)
printf("%2d.%02d.%4d\n",
dates[i].d, dates[i].m, dates[i].y);
return 0;
} Національний університет «Львівська політехніка»
#define _CRT_SECURE_NO_WARNINGS Лістинг 7.11.: Динамічні масиви структур
#include <stdio.h>
#include <malloc.h> Результати роботи програми:
struct TelPersonInfo Enter the number of TelPersonInfo elements: 2
{ Enter 1 name: Koval
char Name[128]; Enter 1 TelNumber: 12345
int TelNum;
Enter 2 name: Melnyk
};
Enter 2 TelNumber: 54321
int main(void) {
struct TelPersonInfo* pTPI; int N, i;
printf("Enter the number of TelPersonInfo elements: ");
Koval 12345
scanf("%d", &N); Melnyk 54321
/*---- get dynamic memory ----*/
pTPI = (TelPersonInfo*)malloc(N * sizeof(struct TelPersonInfo));
/*-- work with dynamic memory --*/
if (pTPI != 0){
for (i = 0; i < N; i++) {
printf("Enter %d name: ", i + 1);
scanf("%s", (pTPI + i)->Name);
printf("Enter %d TelNumber: ", i + 1);
scanf("%d", &(pTPI[i].TelNum));
} printf("\n\n");
for (i = 0; i < N; i++) {
printf("%s", (pTPI + i)->Name);
printf(" %d\n", (pTPI + i)->TelNum);
} free(pTPI);
}
return 0;
} Національний університет «Львівська політехніка»
В мові С є можливість працювати з окремими бітами певної змінної. Бітові поля – це особливий тип структури, який
визначає довжину кожного поля в бітах.
struct ім’я_структури
{
тип поле1 : довжина;
тип поле2 : довжина;
...
тип полеN : довжина;
}
Тип поля має бути цілого типу, unsigned або signed. При використанні бітових полів слід бути впевненим, що пам’яті
буде достатньо для усіх бітів.
struct date
{
unsigned int day : 5;
unsigned int month : 4;
unsigned int year : 23;
};
Така структура в пам’яті буде відображатись наступним чином:

22 21 20 … 1 0 3 2 1 0 4 3 2 1 0
year month day

Національний університет «Львівська політехніка»


Об’єднання – формат даних, який може містити різні типи даних, але лише один тип водночас.
Можна сказати, що об’єднання є окремим випадком структури, всі поля якої розташовуються за однією й тією самою
адресою.
Формат опису об’єднання є такий самий як і у структури, лише замість ключового слова struct використовується слово
union. Але, тоді як структура може містити, скажімо, елементи типу і int, і short, і double, об’єднання може містити чи
то int, чи short, чи double :
union prim
{
int x;
short y;
double z;
};
Обсяг пам’яті, яку займає об’єднання дорівнює найбільшому з розмірів його полів.
Об’єднання застосовують для економії пам’яті у тих випадках, коли відомо, що понад одного поля водночас не потрібно.
Зручно використовувати об’єднання, коли необхідно дійсне число типу float представити у вигляді масиву байт:
union types
{
float f;
unsigned char b[4];
};

Національний університет «Львівська політехніка»


При написанні програм часто виникає потреба у визначенні наперед відомої кількості іменованих констант, які мають
мати різні значення (при цьому конкретні значення можуть бути неважливими).
Для цього зручно користуватися типом перерахування enum, всі можливі значення якого задаються списком
цілочисельних констант:
enum ім’я_типу { список_констант };
Ім’я типу задається в тому разі, якщо у програмі потрібно визначати змінні цього типу.
Змінним перераховного типу можна присвоювати кожне значення зі списку констант, зазначених при оголошуванні типу.
Імена перераховних констант мають бути унікальними.
Перераховні константи подаються й опрацьовуються як цілі числа і можуть ініціалізуватися у звичайний спосіб.
Окрім того вони можуть мати однакові значення.
За відсутності ініціалізації перша константа обнулюється, а кожній наступній присвоюється значення на одиницю більше,
аніж попередній.
enum week { sat = 0, sun = 0, mon, tue, wed, thu, fri } rоb_den;
Тут описано перерахування week з відповідною множиною значень і оголошено змінну rоb_den типу week. Перераховні
константи sat та sun мають значення 0, mon – значення 1, tue – 2, wed – 3, thu – 4, fri – 5.
enum { first, second = 100, third };
У цьому разі first за замовчуванням дорівнює 0, а наступні неініціалізовані константи перерахування перевищують
своїх попередників на 1. Приміром, third матиме значення 101.
Національний університет «Львівська політехніка»
До перераховних змінних можна застосовувати арифметичні операції й операції відношення, наприклад:
enum days { sun, mon, tue, wed, thu, fri, sat };
days day1 = mon, day2 = thu;
int diff = day2 - day1;
Арифметичні операції над змінними перераховних типів зводяться до операцій над цілими числами.
Проте, не зважаючи на те, що компіляторові відомо про цілочисельну форму подання перераховних значень, варто
використовувати цей факт надто обережно.
Рекомендовано без нагальної потреби не використовувати цілочисельну інтерпретацію перераховних значень.
Істотним недоліком типів перерахування є те, що вони не розпізнаються засобами введення-виведення С.
При виведенні такої змінної виводиться не її формальне значення, а її внутрішнє подання, тобто ціле число.

Національний університет «Львівська політехніка»


7.5. Внутрішнє представлення даних
Цілі типи даних.
У пам'яті ціле число представлено у вигляді послідовності бітів фіксованого (кратного 8) розміру. Ця послідовність нулів
і одиниць - не що інше, як двійковий запис числа, оскільки зазвичай для подання використовується позиційний
двійковий код. Діапазон цілих чисел, як правило, визначається кількістю байтів в пам'яті комп'ютера, що відводяться під
одну змінну.
Беззнакові цілі представляють тільки невід'ємні числа, при цьому всі розряди коду використовуються для подання
значення числа і максимальне число відповідає коду у якого у всіх розрядах одиниці: 111 … 111. Змінна цілого типу без
знаку приймає значення від 0 до +2n−1, де n – кількість розрядів числа.
У C і C++ для позначення беззнакових типів використовується модифікатор unsigned.
Числа з знаком представляються у так званому доповняльному коді.
Старший розряд числа позначає знак числа – якщо він рівний 0 – то число додатне, якщо 1 – то число від’ємне.
Змінна цілого типу зі знаком приймає значення від −2n-1 до +2n-1−1, де n –кількість розрядів числа.
Для додатного числа доповняльний код рівний прямому, а для від’ємного оберненому плюс одиниця. Математично
доповняльний код Xдоп = 2n+1 — X, де X— число, яке треба представити у доповняльному коді, n — к-сть розрядів
числа.

Національний університет «Львівська політехніка»


Дійсні типи даних.
Для кодування дійсних даних (типи float і double) використовують стандарт IEEE 754, який визначає формати і
методи для арифметики з плаваючою комою в комп'ютерних системах – стандартні та розширені функції для чисел
одинарної, подвійної, розширеної і розширюваної точності - і рекомендує формати для обміну даними.
Дані з плаваючою комою представляються за допомогою трьох полів:
• 1-бітовий знак S;
• w-бітовий зміщений порядок E = e + bias;
• t-бітовий хвіст мантиси T.

молодший розряд старший розряд

знак S зміщений порядок E xвіст мантиси T

1 біт w бітів t бітів

Національний університет «Львівська політехніка»


Для представлення у цьому форматі двійкове число спочатку нормалізується, тобто приводиться до такого вигляду,
коли мантиса потрапляє в діапазон 1 ≤ m < 2. Таким чином ціла частина нормалізованої мантиси завжди дорівнює 1.
Знак додатних чисел кодується нулем, від’ємних одиницею.
В поле порядку пишеться зміщений порядок – до порядку нормалізованого числа додається константа, таким чином всі
порядки представляються додатними числами.
В поле мантиси записується, т. зв. "хвіст" мантиси – всі дробові цифри мантиси (крім першої одиниці, яка є цілою
частиною).
Ціла частина мантиси в нормалізованому вигляді дорівнює 1 і не входить до коду числа. Це так звана прихована
одиниця.
Приклад. 0,312510 = 0,01012 = 1,01 × 2-2.
Для 32-х розрядного формату порядок зміщується на 127. E = –2 + 127 = 125 = 11111012.
Мантиса записується без першої одиниці, тобто T = 01 (1,01 –1 = 0,01).
Компоненти коду: знак – 0 (1 розряд), зміщений порядок – 01111101 (8 розрядів), хвіст мантиси
01000000000000000000000 (23 розряди).
Повний 32-бітний код 00111110101000000000000000000000.

Національний університет «Львівська політехніка»


Лістинг 7.12.: Внутрішнє представлення даних
#define _CRT_SECURE_NO_WARNINGS double_type d1, d2;
#include <stdio.h> d1.f = 275.5555555; d2.f = -275.5555;
printf("\ndouble:");
int main(void) printf("\n%26g\t %016llx ", d1.f, d1.i);
{ printf("\n%26g\t %016llx ", d2.f, d2.i);
union float_type printf("\n%26lld\t %016llx ", d1.i, d1.i);
{ printf("\n%26lld\t %016llx ", d2.i, d2.i);
float f; d1.i = 2321777620394769639; d2.i = -2289908398520801427;
int i; printf("\n%26lld\t %016llx ", d1.i, d1.i);
}; printf("\n%26lld\t %016llx ", d2.i, d2.i);
float_type f1, f2; printf("\n%26g\t %016llx ", d1.f, d1.i);
f1.f = 275.5; f2.f = -275.5555; printf("\n%26g\t %016llx ", d2.f, d2.i);
printf("float:"); return 0;
printf("\n%13g\t %08x ", f1.f, f1.i); }
printf("\n%13g\t %08x ", f2.f, f2.i);
printf("\n%13d\t %08x ", f1.i, f1.i);
printf("\n%13d\t %08x ", f2.i, f2.i);
f1.i = 566551438; f2.i = -507190386;
printf("\n%13d\t %08x ", f1.i, f1.i);
printf("\n%13d\t %08x ", f2.i, f2.i);
printf("\n%13g\t %08x ", f1.f, f1.i);
printf("\n%13g\t %08x ", f2.f, f2.i);
union double_type
{
double f;
long long i;
}; Національний університет «Львівська політехніка»
Лістинг 7.12.: Внутрішнє представлення даних
Результати роботи програми:
float:
275.5 4389c000
-275.556 c389c71b
1133101056 4389c000
-1014380773 c389c71b
566551438 21c4e38e
-507190386 e1c4e38e
1.33417e-18 21c4e38e
-4.53995e+20 e1c4e38e
double:
275.556 407138e38e29f9cf
-275.555 c07138e353f7ced9
4643555240789539279 407138e38e29f9cf
-4579816797041602855 c07138e353f7ced9
2321777620394769639 20389c71c714fce7
-2289908398520801427 e0389c71a9fbe76d
1.83558e-153 20389c71c714fce7
-3.29981e+155 e0389c71a9fbe76d

Національний університет «Львівська політехніка»


Контактна інформація:

79013, м. Львів, вул. С. Бандери, 12,


(032) 258-26-80, coffice@[Link]
[Link]

You might also like