Вакал - Мова Сі (Типи данних та основні структури керування)
.pdfоператори, які відносяться до інших міток (поки не зустрінеться оператор
break).
Приклад:
#include <stdio.h> main( )
{
char c;
printf(²введіть символ:²); scanf(²%c²,&c);
switch(c) {
case ‘A’: printf(²вибір А\n²);break; case ‘B’: ;
case ‘C’: printf(²вибір В або С\n²);break;
default: printf(²Не вибрано ні A, ні В і ні С\n²);break;
}
}
Приклад:
#include <stdio.h> main( )
{
char sign;int x,y=4,z=2; printf(²введіть символ з клавіатури:²); sign=getch( );
switch(sign) {
case ‘-’: x=y-z; printf(²x=%d\n²,x);break; case ‘+’: x=y+z;
case ‘*’: x=y*z;
case ‘/’: x=y/z; printf(²x=%d\n²,x);break; default: printf(²Невідомий оператор\n²); }
}
3.11. Цикли
Раніше ми визначили дві структури управління у мові Ci: ланцюг або послідовне виконання команд, а також розгалуження. Розглянемо інший тип структури керування, що називається циклом або ітерацією і здійснює повторне виконання певної послідовності дій. Нагадаємо основні різновиди циклу: цикл з умовою продовження, цикл з умовою закінчення, цикл з виходом і цикл з лічильником. Ці інструкції на алгоритмічній мові мають такий вигляд
поки F повт |
|
повт |
|
P |
(Lw) |
P |
(Lu) |
кц; |
|
до F кц; |
|
цикл |
|
для k =1 до c крок h повт |
|
P |
|
P |
(Lс) |
|
|
31 |
|
якщо F то вихід; (Le) |
кц; |
Q |
|
кц; |
|
Розглянемо реалізацію цих циклів у мові Ci. Зауважимо перш за все, що у Ci, також як і у мові Паскаль, є три типи циклів: цикл з умовою повторення, цикл з умовою закінчення, цикл з лічильником.
3.11.1. Цикл з умовою повторення (продовження, з передумовою)
Цей цикл є найбільш загальним і може бути використаний замість двох інших типів циклічної інструкції.
Оператор циклу WHILE має формат: WHILE <вираз>
<оператор>;
де <вираз> – будь-який допустимий у Ci вираз, який приймає нульове або відмінне від 0 значення, а <оператор> може бути порожнім, простим або складеним. В останньому випадку він заключається в операторні (фігурні) дужки.
Схема виконання оператору WHILE:
1.Обчислюється значення <виразу>.
2.Якщо воно Хибне (дорівнює 0), то тіло оператору WHILE не виконується, цикл завершується і керування передається на наступний за циклом оператор програми.
3.Якщо воно Істинне (тобто не дорівнює 0), то виконується <оператор>, тобто тіло циклу.
4.Процес повторюється за цією ж схемою.
5.Цикл завершує роботу у випадках: <вираз> набув значення 0 (став Хибним); зустрівся оператор break, що передає керування на наступний за циклом оператор. Важливо запобігти можливість виконання циклу нескінченну кількість разів, що ілюструє наступний приклад:
#include <stdio.h> main( )
{
int index; index=1;
while (index≤5)
printf(²значення індексу=%d\n²,index);
}
Програма буде друкувати це повідомлення нескінченну кількість разів. Легко виправити цю помилку:
#include <stdio.h> main( )
{
int index; index=1;
while (index≤5)
32
{
printf(²значення індексу=%d\n²,index); index++;
}
}
Дії з перевірки виразу і прирощенню значення лічильника можуть бути поєднані в один вираз, використовуючи операцію збільшення на 1 ++.
#include <stdio.h> main( )
{
int index; index=1;
while (index++£5)
/*Значення index порівнюється з 5, а потім збільшується на 1 */ printf(²значення індексу=%d\n²,index);
}
Приклад:
/* Обчислення степеню */ #include <stdio.h>
main( )
{
double x,y=1; unsigned n,k; printf("Введіть x,n:"); scanf("%lf %u",&x,&n); k=n;
while (k >0)
{
if (k % 2 == 0)
{
k/=2; x*=x;
}
else
{
k--; y*=x;
}
};
printf("Степінь дорівнює %.2f \n",y);
}
3.11.2. Цикл з умовою закінчення (з післяумовою)
Формат циклу з післяумовою можна представити у вигляді
DO
<оператор>; WHILE (<вираз>);
Основна відмінність між циклами WHILE і DO ... WHILE полягає в тому, що <оператор> всередині DO ... WHILE завжди виконується хоча б один раз, оскільки перевірка умови виконання циклу здійснюється після виконання послідовності операторів, які складають тіло циклу.
33
Цикл DO ... WHILE аналогічний циклу repeat … until мови Паскаль, проте існують дві основні відмінності:
1.Цикл DO ... WHILE виконується поки <вираз> істинний (тобто не 0). Якщо вираз стає хибним (тобто рівним 0), то управління передається наступному оператору програми. У Паскалі цикл repeat ... until виконується поки булевий <вираз> хибний до тих пір, поки він не стане істинним.
2.Оператор repeat ... until не вимагає використання операторних дужок begin ... end для складеного оператора. У циклі DO ... WHILE використання фігурних дужок для групи операторів, що складають тіло циклу, є обов’язковим.
На додаток зауважимо, що у Ci (на відміну від мови Паскаль) операції порівняння (<, > та ін.) мають більш високий пріоритет, ніж логічні (&&, ||). Це дозволяє не заключати кожний вираз порівняння у круглі дужки, як це робиться у мові Паскаль.
Приклад:
do {
printf(²введіть a:²); scanf(²%d²,&a); }
/*виконується, поки не введем число більше low і менше high */ while (a<low||a>high);
Приклад: Знайти найлівішу цифру заданого натурального числа.
/* Обчислення першої цифри числа */ #include <stdio.h>
main( )
{
unsigned n,k; printf("Введіть n:"); scanf("%u",&n);
do
{
k=n % 10; n/=10;
}
while (n > 0);
printf("Перша цифра дорівнює %u \n",k);
}
3.11.3. Цикл з лічильником
Цикл з лічильником FOR( ; ; ) є одним з основних видів циклу, який характерний для всіх універсальних мов програмування, включаючи Ci. Однак версія циклу FOR, яка використовується у Ci, має більшу потужність і гнучкість.
Основна ідея циклу полягає в тому, що оператори, які знаходяться всередині тіла циклу, виконуються фіксоване число раз, у той час, як деяка змінна (параметр циклу) пробігає певний ряд значень.
Оператор циклу FOR має такий формат:
34
FOR(<вираз1>;<вираз2>;<вираз3>) <оператор>;
Так само, як і у попередніх циклах, <оператор> у тілі циклу FOR може бути або порожнім, або простим, або складеним. В останньому випадку він береться у фігурні дужки.
Параметри циклу FOR знаходяться у круглих дужках і відокремлюються один від одного крапкою з комою. Кожний параметр займає свою певну позицію і означає:
<вираз1> – як правило задає початкове значення параметру циклу – це вираз ініціалізації; <вираз2> – визначає умову продовження циклу;
<вираз3> – визначає зміну параметрів циклу після кожного виконання тіла циклу.
Схема виконання оператору циклу FOR:
1)обчислюється <вираз1>. Це здійснюється тільки один раз, коли цикл for починає виконуватися;
2)обчислюється <вираз2>. Це здійснюється перед кожним можливим виконанням тіла циклу;
3)якщо значення <виразу2> Істина (не дорівнює 0), то виконується тіло циклу, тобто <оператор>.
4)обчислюється <вираз3> (після кожного виконання тіла циклу);
5)обчислюється <вираз2>;
6)як тільки значення <виразу2> стає рівним 0 (Хибним), то управління передається на оператор, наступний за оператором FOR.
Істотно, що перевірка умови завжди виконується на початку циклу. Це означає, що цикл FOR може не виконатися жодного раз, якщо <вираз2> зразу буде хибним.
Приклад:
#include <stdio.h> main()
{
int i;
for (i=0; i<=10; i++)
printf("Квадрат числа %d=%d\n", i,i*i);
}
Цикл FOR часто використовується для реалізації у програмі затримки часу з метою узгодження швидкості реагування комп’ютера з можливостями сприйняття людиною FOR(n=1; n<=10000; n++); Тіло циклу – порожній оператор.
Оператор FOR – це цикл з передумовою: рішення, чи виконувати кожний раз тіло циклу, приймається до його проходження. Звідси випливає, що все, що можна здійснити за допомогою циклу WHILE, можна зробити і за допомогою циклу FOR. Для перетворення циклу WHILE у цикл FOR треба попередньо здійснити ініціалізацію деяких вибраних змінних і включити у тіло циклу
35
деякі оператори, що коректують їх значення. Тоді оператор FOR буде еквівалентний такій конструкції:
<вираз1>; while(<вираз2>)
{
<оператор>; <вираз3>;
}
Хоча цикл FOR на перший погляд дуже схожий з циклом FOR у мові Паскаль, він є набагато гнучкішим і потужним засобом, ніж у інших мовах. Ця гнучкість – наслідок способу використання <виразів1,2,3> у специфікації циклу FOR. До цього часу перший вираз використовувався для ініціалізації лічильника, другий – для завдання його граничного значення, третій – для збільшення його поточного значення. Існує багато можливостей його застосування.
1. Лічильник побудовано в порядку спадання:
for (n=10; n>0; n--)
printf("%d\n",n);
2. Значення лічильника збільшується або зменшується на2,3,5 і т.д.:
for (n=2; n<60; n=n+13) / або *n++=13 */
printf("%d\n",n);
3. Лічильник будується за допомогою символів:
for (ch=‘a’; ch<=‘z’; ch++)
printf("Величина кода ASCII для символа %c дорівнює %d\ \n", ch, ch);
При виконанні цього оператору будуть роздруковані усі букви від ‘a’ до ‘z’ разом з їх кодами у таблиці ASCII.
4.Замість перевірки умови, що накладається на число повторень – ітерацій, можна перевірити виконання деякої іншої довільної умови. Наприклад, у програмі видачі квадратів чисел замість умови i<=10; можна розглянути, наприклад, таку i*i<=625 for(i=0; i*i<=625; i++)
5.Значення величини, що визначає <вираз3>, зростає не в арифметичній, а в
геометричній прогресії:
for (x=1.0; x<150.0; x=x*1.1) /* або x*=1.1*/
printf("%.2fn",x);
6. <вираз3> є довільним правильно складеним виразом, значення якого буде змінюватися на кожній ітерації:
#include <stdio.h> main()
{
int x,y=55;
for (x=10; y<=75; y=5*x+++10) printf("xx=%d, y=%d\n",x,y);
}
Друкуються значення змінної x і виразу y. Проте не рекомендується змішувати процес зміни змінної циклу з алгебраїчними обчисленнями.
7. Можна опускати один або більше виразів у круглих дужках (але при цьому
36
∙ |
символи крапка з комою не можуть бути опущені): |
опустивши <вираз1> і <вираз3>, ми перетворимо цикл FOR у цикл WHILE: |
|
∙ |
специфікація for(;<вираз2>;) еквівалентна опису while(<вираз2>); |
опустивши <вираз2>, маємо значення порожнього умовного виразу завжди |
|
|
1 (Істина), цикл буде виконуватися нескінченну кількість раз, наприклад, |
for(;;)printf("Ну і цикл!\n");
8.<вираз1> необов’язково повинен ініціалізувати змінну, на цьому місті може стояти будь-який інший оператор. Слід тільки пам’ятати, що <вираз1>
виконується тільки один раз. Наприклад,
#include <stdio.h> main()
{
int i=1;
for (printf("Вводьте числа!\n"); i!=6;) scanf("%d",&i);
printf("Нарешті ввели те, що треба!\n");
}
Перше повідомлення друкується один раз, потім вводяться числа до тих пір, поки не введемо число 6.
9. Параметри, що входять до <виразів2,3>, можуть бути змінені при виконанні операцій у тілі циклу. Наприклад,
#include <stdio.h> main()
{
int n,delta; float sum=0.0;
for (n=1; n<10; n+=delta)
{
printf("Введіть delta=:"); scanf("%d",&delta); sum+=delta; printf("sum=%f\n",sum);
}
}
10. Гнучкість оператору FOR може бути збільшена за допомогою операції “,” – послідовне виконання команд, яка дозволяє вводити складені вирази у цикл FOR. Наприклад:
#include <stdio.h> main()
{
char *msg; int up,down; msg="Привіт";
for (up=1, down=9; up<10; up++, down--)
printf("%s:%2d растет,%2d зменшується! \n", msg,up,down);
}
<вирази2,3> складаються тут з двох виразів, що ініціалізують і модифікують змінні up, down. При використанні операції ‘кома’ гарантується,
37
що вирази, розділені, комою, будуть обчислені зліва направо. Приклад: обчислення степені y = x n :
/* Обчислення степеня */ #include <stdio.h> main()
{
double x,y; unsigned n,i; printf("Введіть x,n:");
scanf("%lf %u",&x,&n); /*Введення через пробіл. */
/* Для введення через кому – поставити кому між %lf і %u */ y=1;
for (i = 1; i <= n; i++) y*=x;
printf("Степінь дорівнює %.2f \n",y);
/* Формат виведення ціла_част.xx,де xx - дві цифри мантиси */
}
Приклад. Обчислити суму ряда, що складається з 15 доданків
1 + 1 + 1 + 1 + 1 + K 2 4 8 16
#include <stdio.h> main()
{
const int n=15; int i;
float sum,x;
for (sum=0.0,x=1.0, i=1; i<=n; i++,x*=2.0) {sum+=1.0/x;
printf("sum=%f, коли i=%d\n",sum,i);
}
}
Відповідь: sum=1.99999. Ця задача розв’язує так званий парадокс Зенона: випущена стріла ніколи не долетить до мети. Спочатку стріла пролітає половину відстані до мети (скажімо, за 1 сек), потім половину відстані, що залишилася (1/2), потім половину, що залишилося, тобто 1/4 і т.д. Повний час польоту 1+1/2+1/4+...
3.11.4. Цикл з виходом
У Ci цикла з виходом немає. Він кодується за допомогою нескінченного циклу WHILE за допомогою оператору break
while(1==1)
{
..............
if(...) break;
..............
}
Оператор завершення break припиняє виконання самого внутрішнього із операторів SWITCH, DO...WHILE, FOR і WHILE, які містять його. Після виконання оператору break управління передається оператору, наступному за
38
перерваним. Оператор завершення не можна використовувати у конструкціях if...else або в тілі головної функції main для виходу з неї.
Оператор завершення break часто використовується для виходу з циклу, коли задані дві умови припинення його роботи.
/* Обчислення суми додатних членів послідовності */ #include <stdio.h>
main()
{
int n,s=0; while (1 == 1)
{
printf("Введіть черговий елемент:"); scanf("%d",&n);
if (n < 0) break; s+=n;
}
printf("Сума додатних елементів дорівнює %d \n",s);
}
3.12. Оператор CONTINUE
Перериває виконання тіла циклів WHILE, DO ...WHILE або FOR, тобто пропускає залишок цикла, передаючи керування на початок наступної ітерації (тобто виходу з циклу подібно Break не відбувається). Для циклів WHILE, FOR після цього починається новий крок, а для циклу DO ... WHILE перевіряється умова виходу, і якщо вона Істинна, виконується наступна ітерація.
Приклад:
#include <stdio.h> main()
{
int n;
for (n=0;n<100;n++)
{
if(n%2) continue; printf(“ %d\n” ,n);
}
}
3.13. Оператор GOTO
Хоча для написання структурованих програм не рекомендується використовувати оператори переходу, у Ci є оператор переходу GOTO. Формат оператору:
goto <мітка>;
<мітка>: <оператор>;
де <мітка> – це просто ідентифікатор, який має бути унікальним. Оператор goto передає управління на <оператор>, що відмічено <міткою>. Відмічений
39
оператор повинен знаходитися в тій же функції, що і оператор goto. Оператор goto може використовуватися для виходу із вкладених операторів управління. Єдиний обґрунтований випадок його використання – це вихід із вкладених циклів при знаходженні якихось помилок (застосування оператору Break дає можливість здійснити вихід лише з внутрішнього циклу).
3.14. Вказівники
Змінні, що розглядаються в програмах, як правило, містять значення даних, тобто інформацію для роботи програм. Але іноді важливіше знати не саме значення змінної, а її місце розташування у пам’яті. З цією метою і використовуються вказівники.
Вказівник – це деяка змінна, яка містить адресу деяких даних, а не їх значення. За допомогою вказівника можна:
∙одержати доступ до різних комірок пам’яті з метою зміни їх значення, зчитування і т.д.;
∙створювати нові змінні в процесі виконання програми.;
∙одержати доступ до різних елементів структур даних (масиви, рядки, структури).
Оголошення вказівників здійснюється за допомогою символу “*”.
Приклад.
#include <stdio.h> main()
{
int i, *ipoint; ipoint=&i; i=421;
printf(“ зміст i :%d\n” ,i);
printf(“ значення за адресою :%d\n” ,*ipoint);
}
Тут i – ціла змінна, яка має значення типу int, ipoint – вказівник на цілу змінну, тобто ipoint містить адресу величини цілого типу.
У програмі вказівнику ipoint присвоюється адреса змінної i; ціле значення 421 присвоюється самій змінній i. Можна безпосередньо присвоїти *ipoint значення 421, тобто *ipoint=421; так як вирази *ipoint і i – суть одна й та сама комірка пам’яті. *ipoint – доступ до об’єкта, що здійснюється через покажчик з використанням операції * – розадресація.
Змінимо програму (динамічний розподіл).
#include <stdio.h> #include <alloc.h> main()
{
int *ipoint; ipoint=(int*)malloc(sizeof(int)); *ipoint=421;
printf(“ значення за адресою :%d\n” ,*ipoint);
}
Розглянемо роботу оператору ipoint=(int*)malloc(sizeof(int)):
40