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

Зубенко, Омельчук - Програмування. Поглиблений курс

.pdf
Скачиваний:
51
Добавлен:
07.03.2016
Размер:
4.72 Mб
Скачать

Розділ ІІІ. МОВИ ПРОГРАМУВАННЯ С ТА С++

{

if(keytab[n].keycount>0)

printf("%4d%s\n", keytab[n].keycount, keytab[n].keyword);

};

return 0;

}

/*бінарний пошук у таблиці*/ int binary (char *word)

{

int cond, low=0, high=(sizeof(keytab)/sizeof(struct key))-1; int mid;

while(low<=high)

{

mid=(low+high)/2;

struct key k=keytab[mid]; //cond=;

if((cond=strcmp(word, k.keyword))<0) high=mid-1;

else

if (cond>0) low=mid+1; else return (mid);

}

return (-1);

}

/*getword – читає з клавіатури в рядок w черговий ідентифікатор, довжина якого обмежена числом lim */

int getword(char *w, int lim)

{

int c, t; if(type(c=*w++=getch())!=LETTER){ *w='\0'; return(c);} while(--lim>0){ t=type(c=*w++=getch());

if(t!=LETTER && t!=DIGIT) {ungetch(c); break;}

}

*(w-1)='\0'; return(LETTER);

}

int type(int c)

{

if(isalpha(c)) return(LETTER); else

if(isdigit(c)) return(DIGIT); else return(c);

}

381

ПРОГРАМУВАННЯ

3.7.3. БІТОВІ ПОЛЯ

Часто трапляється, що один чи кілька компонентів структури не перевищують певного невеликого цілого значення, наприклад 1 (ви- падок бітових прапорців). З метою економії пам'яті було б доцільно пакувати такі об'єкти в одне машинне слово (типу int). В інших ситу- аціях виникає необхідність отримати прямий доступ не до всього слова, а до окремих його бітів. Такі можливості надають бітові поля:

<бітове-поле>::=[<декларація-цілого-поля>]:<константа-довжина-поля>

У загальному випадку тип структури з бітовим полем виглядає так:

struct { ..

unsіgned x: n1; /*х:беззнакове бітове поле довжиною n1*/ sіgned int y: n2; /*у: знакове бітове поле довжиною n2*/

int z: n3;

/*z: просте бітове

поле довжиною n3*/

: n4;

/*z: неіменоване бітове

поле довжиною n4*/

 

 

}

 

 

До неіменованих полів доступ відсутній. За їхньою допомогою ви- рівнюють адреси сусідніх компонент. Нульова довжина поля означає, що наступне бітове поле розпочнеться з нового слова.

Приклад 3.64. Бітові поля:

а) Визначення чотирьох прапорців і бітового поля довжиною 8:

struct flag {unsigned int less:1; unsigned int eq:1; unsigned int gr:1;

 

 

unsigned int fl:1;

 

:5;

 

 

 

 

 

:0;

 

 

 

 

unsigned int reg:8;

 

 

 

 

 

 

 

 

 

} cmp;

 

 

 

 

 

 

 

Структура flag:

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

less

eq

Gr

 

 

Fl

 

Reg

 

1

1

1

5

 

1

7

8

 

Структура flag потребує двох слів, затемнені поля довжиною 5, 7 та 8 не використані. Поле reg автоматично розміщується на початку другого слова.

382

Розділ ІІІ. МОВИ ПРОГРАМУВАННЯ С ТА С++

б) Пакування десяткових цифр:

struct pdigit {

 

 

 

 

unsigned int a:4;

 

 

unsigned int b:4;

 

 

unsigned int

c:4;

 

 

unsigned int d:4;

 

 

} pd, longdec[256];

 

 

Структура pdigit:

 

 

 

 

a

B

C

d

4

4

4

4

У структурі pd подаються чотири десяткові цифри, доступні за іме-

нами pd.a, pd.b, pd.c, pd.d, а в масиві longdec – 1024 цифри. Опе-

ратори присвоювання pd.a=5; pd.b=6; pd.c=7; pd.d=8; записують у змінну pd двійково-десяткове число 5678

Знакові бітові поля автоматично розміщуються на границях слів, при цьому деякі частини слів можуть бути невикористаними.

Використання бітових полів має певні обмеження:

не можна отримати адресу бітового поля;

немає масивів бітових даних;

програми з бітовими полями значною мірою є машинно-залежними.

3.7.4. ТИП ОБ'ЄДНАНЬ

Тип об'єднань є прямою сумою кількох типів. Його дані називають- ся об'єднаннями. Змінні типу об'єднання можуть набувати значень кількох різних типів. У мові С вони трактуються як структури, усі по- ля яких розміщуються не в окремих послідовних ділянках пам'яті, а в одній спільній ділянці, достатній для збереження найбільшого поля. Це означає, що всі вони мають одну спільну адресу. Звідси походить і друга назва даних цього типу суміші. Як наслідок, є можливість до- ступу до однієї й тієї самої ділянки пам'яті за допомогою змінних різ- них типів. Передбачивши, наприклад, в об'єднанні поле символьний масив, можна працювати з кожним окремим байтом ділянки, де збе- рігається значення об'єднання. Можна також отримати доступ до окремих бітів ділянки у С99 в об'єднаннях допускаються й бітові поля. За допомогою об'єднань можна економити пам'ять за умови, що обробка полів здійснюється в асі послідовно.

Синтаксис опису об'єднань, як і всі обмеження на нього, а також засоби доступу до полів ті самі, що й у випадку структур. Необхідно

383

ПРОГРАМУВАННЯ

тільки в описі об'єднань службове слово struct замінити на службове слово union.

Приклад 3.65. Об'єднання двобайтного масиву й цілої змінної:

union mix {unsigned char h[2]; int ivalue;

}

Масив h дозволяє розкласти поле ivalue на дві однобайтні частини

старшу h[0] і молодшу h[1]. Нехай у полі ivalue записано 255. Тоді

h[0]=0х0 і

h[1]=0хFF

 

00

 

FF

 

 

ivalue:

h[1]

 

h[0]

Застосуємо тип mix для друкування ASCII- та SCAN-кодів симво- лів, що вводяться з клавіатури:

#include <bios.h> #include <stdio.h>

int main()

{

union mix exc;

unsigned char scn, asc;

do /*цикл до Cntr+ Z*/

{

printf ("\n");

exc.ivalue=bioskey(0); /*bioskey(0): повертає черговий розшире-

ний код символу*/ asc=exc.h[0]; scn=exc.h[1];

printf ("%4d || %4d", scn, asc);

}

while (asc!=26 || scn !=44) /*26 та 44 є ASCIIта SCAN-кодами розширеного символу Cntr+ Z*/

return 0;

}

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

384

Розділ ІІІ. МОВИ ПРОГРАМУВАННЯ С ТА С++

момент часу інформувало про активне поточне поле всередині об'єд- нання. Для об'єднання data подібна структура й організація контролю за активним полем могли б виглядати так:

enum mix_tag{mix_h, mix_ivalue}; struct MIX {

enum mix_tag flag; /*flag: індикатор активного поля в об'єднан-

ні data*/

union mix {unsigned char h[2]; int ivalue;

}data;

}exc;

Нехай у полі data структури exc необхідно змінити масив h, напри- клад, записати в h[0] число 25. Тоді спочатку робимо активним ма- сив, а потім здійснюємо відповідне присвоювання:

exc.flag=mix_h; exc.data.h[0]=25;

Якщо там само необхідно змінити значення поля ivalue на 1024, то дії аналогічні:

exc.flag=mix_ivalue; exc.data.ivalue=1024;

*Література для CР: Структури, об'єднання – [55, 102, 132, 133].

Контрольні запитання та вправи

1.Що таке тип структур?

2.Що таке структура?

3.Як виглядає специфікатор типу структур?

4.Що таке тег структури?

5.Як ініціалізуються структури?

6.Навести приклади структур із неповним типом.

7.Визначити усі операції на структурах.

8.Який зв'язок між операціями стрілочка та крапка?

9.Що таке тип об'єднання?

10.Як виглядає специфікатор типу об'єднання?

11.Що спільного між структурами й об'єднаннями?

12.Чим відрізняються структури й об'єднання?

13.Як описуються бітові поля?

14.Для чого застосовують бітові поля?

15.Які існують обмеження на застосування бітових полів?

16.Що виведе такий фрагмент програми:

385

ПРОГРАМУВАННЯ

void main()

 

{struct

A {int i; char *s; struct A *p;};

static

struct

A b[]={{1,"XYZ",b+2},{2,"ABC",b},

{3,"PQR",b+1}, {4,"PRQ",b}} ; struct A *ptr=b;

printf ("%s", b[--(ptr->p ->i)+1].s);

}?

17.Описати структуру й написати функцію, що порівнює дві структури:

а) книга: містить інформацію про автора, назву книжки, рік видання, видавництво й кількість сторінок; б) студент: містить інформацію про ПІБ студента, рік всту- пу, рік народження, адресу й телефон;

в) товар: містить інформацію про назву товару, виробника, кількість одиниць товару в наявності; г) клієнт банку: містить інформацію про ПІБ клієнта, номер рахунку, кількість грошей на рахунку;

д) телефонний номер: містить інформацію про ПІБ абонен- та, номер телефону, адресу абонента.

18.Описати таблицю у вигляді масиву структур для даних, ого- лошених у вправі 17, і написати функції для введення й виведення таких таблиць.

19.Для описаних у вправі 18 таблиць знайти й вивести на ек- ран інформацію:

а) дані про книжку із заданою назвою; б) дані про всіх студентів із заданим роком вступу;

в) дані про всі товари заданого виробника; г) дані про всіх клієнтів банку, на рахунку яких кількість грошей менша ніж зазначена; д) дані про абонента за ПІБ.

20.Структура struct date{int day;int month;int year;int year_day; char mon_name[4];} подає інформацію про дату:

номер місяця, рік, порядковий номер дня в році й назву міся-

ця. Написати функції: a) void day_of_year(struct date *pd) для знаходження порядкового номера дня в даті *pd і занесення його в поле (*pd).year_day;

б) void month_day(struct date *pd) для пошуку місяця й числа в даті *pd за порядковим його номером і занесення їх

увідповідні поля структури *pd.

21.Раціональні числа можна подати у вигляді структур типу: typedef struct {long n/*=чисельник*/;

long d /*=знаменник*/; } racio;

Написати функції для основних арифметичних операцій над раціональними числами й відношень на них. Напри-

386

Розділ ІІІ. МОВИ ПРОГРАМУВАННЯ С ТА С++

клад, для операції додавання відповідна функція могла б мати такий вигляд:

racio add(racio a, racio b)

{

racio temp; long com; long gcd(long,long); temp.n=a.n*b.d+a.d*b.n; temp.d=a.d*b.d;

com=gcd(temp.n,temp.d); /*функція gcd обчислює НСД (x, y)*/

temp.n/=com;

temp.d/=com; return temp;

}

22.Знайти індекси двох однакових елементів матриці. Для зменшення часової складності програми матрицю подати як об'єднання двовимірного й одновимірного масивів.

23.Знайти найбільший елемент у тривимірному масиві. Для зменшення часової складності програми вхідний масив по- дати як об'єднання тривимірного й одновимірного масивів.

24.Реалізувати цілу довгу: а) двійкову; б) десяткову арифмети- ки. Для компактного подання довгих десяткових цілих ви- користати масиви структур із бітовими полями pdigit із

прикл. 3.64. Написати функції для взаємного перетворення чисел форматів а) та б).

25. Скористатися довгою арифметикою із вправи 24 для обчи- слення надвеликих чисел 2100 та 100!.

3.8. Файли

¾Потоки й файли

¾Консольні функції введення-виведення

¾Обробка файлів

¾Перенаправлення потоків

Ключові слова: файл, потік, потокове введення-виведення, введення- виведення нижнього рівня, введення-виведення для консолі й портів, потоки stdіn, stdout, stderr, текстовий потік, двійковий потік, покажчик файла, відкривання й закривання файла, перенаправлення потоків, стандартні функції: getchar, putchar, getch, getchе, gets, puts, scanf, printf, sscanf, sprintf, fopen,

387

ПРОГРАМУВАННЯ

fclose, fputc, fgetc, fgets, fputs, fseek, ftell, fprintf, fscanf, feof, ferror, fflush, rewind, remove, fread, fwrite, freopen.

Збереження й обробка інформації на зовнішніх пристроях здійс- нюються за допомогою файлових типів даних.

3.8.1. ПОТОКИ Й ФАЙЛИ

Файли це послідовності однотипних компонентів із певними за- собами доступу й обробки.У мові C усі файли розглядаються як послі- довності символів (байтів). Бібліотеки C підтримують три рівні оброб-

ки таких файлів: потокове введення-виведення, введення-виведення нижнього рівня та введення-виведення для консолі й портів.

На перших двох рівнях обмін даними між ОП і файлом здійснюється побайтно фіксованими частинами блоками, що формуються і зберіга- ються в спеціальних системних полях буферах. При читанні з файла дані спочатку блоками заносяться в буфер, а потім звідти за допомогою функцій введення-виведення передаються програмі. При записі у файл функції введення-виведення попередньо заносять дані (побайтно) у бу- фер, і при його заповненні весь блок разом передається у файл.

Незважаючи на специфіку фізичних файлів і операцій введення- виведення для різних пристроїв (екран, клавіатура, диск, принтер то- що), у потоковому введенні-виведенні завдяки буферизації підтриму- ється єдиний механізм доступу до даних. У програмах даний механізм реалізують спеціальні об'єкти потоки. Неформально можна сказати, що потік це поіменована сукупність стандартних універсальних засо- бів буферизації, пов'язана з певним фізичним файлом. Потоки бувають двох типів: текстові та двійкові. При роботі з ними можливі такі дії:

1)відкривання й закривання потоку (функції fopen, fclose);

2)введення-виведення: символів, рядків, форматованих даних, порцій даних довільної довжини (функції getc, putc, fgets, fputs,

fprintf, fscanf);

3)аналіз помилок операцій введення-виведення;

4)керування буферизацією потоку (розмір буфера тощо);

5)керування буферним покажчиком (індексом потоку), який задає поточну позицію в потоці.

У стандарті C текстовий потік організований у вигляді рядків, кожен з яких закінчується символом нового рядка '\n'; у кінці

останнього рядка цей символ не обов'язковий. У текстовому потоці на вимогу базового середовища можуть відбуватися певні перетворення символів. Наприклад, символ LF нового рядка може бути замінений

388

Розділ ІІІ. МОВИ ПРОГРАМУВАННЯ С ТА С++

парою символів CR і LF. Таким чином, може не бути однозначної ві- дповідності між символами, що пишуться (зчитуються), і тими, які зберігаються на зовнішньому пристрої.

Двійковий потік це послідовність байтів, що взаємно однозначно відповідає байтам на зовнішньому пристрої. Перетворення символів (як у попередньому випадку) не відбувається, проте наприкінці двій- кового потоку може додаватись обумовлена програмою кількість ну- льових байтів. Ці байти можуть використовуватися в блоці пам'яті для повного заповнення сектора на диску.

Коли C-програма розпочинає роботу автоматично, для неї відкрива- ються три стандартних потоки: stdin (для клавіатури), stdout (для екрана) і stderr (для виведення повідомлень про помилки на екран). Розглянемо спочатку консольні функції введення-виведення так називають функції, що виконують операції введення з клавіатури й виведення на екран.

3.8.2. КОНСОЛЬНІ ФУНКЦІЇ ВВЕДЕННЯ-ВИВЕДЕННЯ

Читання й запис символів. Найпростіший механізм введення ін- формації читання по одному символу з клавіатури за допомогою функції getchar:

int getchar(void)

Функція реалізована у вигляді макросу getc(stdin) і повертає зна- чення коду чергового символу (якщо він є) з потоку stdin чи EOF при досягненні кінця файла. Виведення символу в поточну позицію кур- сору на екрані можна здійснювати за допомогою функції putchar:

int putchar(int c);

Функція реалізована у вигляді макросу putc(c, stdout); і повертає c чи EOF у випадку помилки.

Приклад 3.66. Посимвольне копіювання на екран рядка, введено- го із клавіатури:

#include<stdio.h>

int main()

{

char c;

/*getchar читає символи з потоку stdin, який має буфер на один рядок. Повертає значення тільки після введення всього рядка, який закінчується символом'\n'(клавіша <Enter>)*/

389

ПРОГРАМУВАННЯ

while((c=getchar())!='\n')

putchar(c); return 0;

}

Як зазначалося вище, функція getchar має одну особливість вона повертає значення тільки після остаточного заповнення буфера кла- віатури, яке завершується введенням символу CR за допомогою кла- віші <Enter>. Таким чином, щоб ввести один або кілька символів, не- обхідно натиснути відповідні клавіші, а потім клавішу <Enter>. Фун- кція при кожному виклику вводить тільки один символ, тому збере- ження в буфері цілого рядка може призвести до того, що в черзі на введення залишаться один або кілька символів, а в інтерактивному середовищі це досить незручно. Іншою незручністю може стати ехо- відображення на екрані введених символів. Альтернативами функції getchar, які можуть застосовуватися як інтерактивні й не використо- вують буферизацію введеної інформації, є функції

int getch(void); int getchе(void);

Вони визначені в заголовному файлі <conio.h>. Перша повертає один введений із консолі символ без виведення його на екран, друга робить те саме, але символ на екран відображає.

Приклад 3.67. Із клавіатури вводиться послідовність символів. Пі- драхувати, скільки в ній слів, тобто послідовностей, розташованих

між порожніми символами (пробіл, \n, \t, EOF ) або на її початку до

першого порожнього символу.

Проілюструємо на цьому прикладі, як для аналізу й обробки послідов- ностей символів (програми-фільтри) використовуються скінченні авто- мати. Ідея застосування автомата полягає в тому, щоб за допомогою станів у процесі введення символів контролювати скінченну кількість фіксованих ситуацій і, залежно від стану, реагувати на введений символ (як того вимагає умова задачі). У нашому випадку таких ситуацій дві: 1) поточний символ перебуває в межах чергового слова, 2) за його ме- жами. Можливі дії вводити черговий символ (g) і збільшувати лічиль- ник слів (m++), якщо розпочалося введення нового слова. Наш автомат має два стани: ІN (=1) та OUT (=0). Перший означає, що процес перебігає в поточному слові, а другий поза ним. Нехай a та b позначають дові-

льні непорожній і порожній символи (пробіл,\n,\t ). У таблиці переходів

390

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