Лекція 3 (Оператори Сі)
.docxЛекція 3
ОПЕРАТОРИ
Оператори - це основні елементи, з яких "будуються" програми на будь-якій мові програмування. Більшість операторів складаються з виразів. Виходячи з цього, спочатку розглянемо вирази.
Вирази
Вираз представляє собою об'єднання операцій і операндів. Найпростіший вираз складається з одного операнду.
Приклади виразів:
5 -7 10+21 a*(b+d*1)-1 x=++a%3 a>3
Неважко помітити, що операнди можуть бути константами, змінними, їх об'єднаннями. Деякі вирази складаються з менших виразів. Дуже важливою особливістю мови Сі є те, що кожний вираз має значення. Наведемо приклади кількох виразів і їх значень :
-5+7 |
2 |
1<2 |
1 |
6+(a=1+2) |
9 |
a=1+2 |
3 |
Як вже було сказано, основу будь-якої програми складають оператори.
Оператором-виразом називається вираз, вслід за яким стоїть крапка з комою. Взагалі усі оператори можна згрупувати у наступні класи:
-
оператори присвоювання;
-
виклики функцій;
-
розгалуження;
-
цикли.
Проте, оператори найчастіше відносяться до більш ніж одного з чотирьох класів. Наприклад, оператор if (a=fn(b+c)>d) складається з представників наступних класів: присвоювання, виклик функції та розгалуження. У тому і є гнучкість Сі, що є можливість змішування в одному операторі операторів різних класів. Проте не слід цим зловживати - програма може вийти правильною, але заплутаною і нечитабельною.
Оператор розгалуження if
Оператор розгалуження призначений для виконання тих або інших дій в залежності від істинності або хибності деякої умови. Основний оператор цього блоку в Сі - if ... else не має ключового слова then, як у Паскалі, проте обов'язково вимагає, щоб умова, що перевіряється, розміщувалася б у круглих дужках. Оператор, що слідує за логічним виразом, є then- частиною оператору if...else.
if (<умова>)
<оператор1>;
[else <оператор2;>]
Умова хибна, якщо вона дорівнює нулю, в інших випадках вона істинна. Це означає, що навіть від'ємні значення розглядаються як істинні. До того ж, умова, що перевіряється, повинна бути скалярною, тобто зводитися до простого значення, яке можливо перевірити на рівність нулю. Взагалі не рекомендується використання змінних типу float або double в логічних виразах перевірки умов з причини недостатньої точності подібних виразів. Більш досвідчені програмісти скорочують оператори типу:
if (вираз!=0) оператор;
до наступного:
if (вираз) оператор;
Обидва логічні вирази функціонально еквівалентні, тому що будь-яке ненульове значення розцінюється як істина. Це можна довести наступними програмами:
Приклад 1.
/* програма виводить результат ділення двох дійсних чисел */ #include<stdio.h> #include<conio.h> void main() { float a,b,c; printf("Введiть число a :\n"); scanf("%f",&a); printf("Введiть число b :\n"); scanf("%f",&b); if (b==0)
printf("Дiлення да нуль !\n"); else { c=a/b; printf("a : b == %g",c); }; }
Приклад 2.
/* застосування умовного розгалужування */ #include <stdio.h> main() { int number; int ok; printf("Введіть число з інтервалу 1..100 : "); scanf("%d",&number); ok=(1<=number) && (number<=100); if (!ok) printf("Не коректно !!\n"); return ok; }
Змінній ok присвоюється значення результату виразу: ненульове значення, якщо істина, і в протилежному випадку - нуль. Умовний оператор if(!ok) перевіряє, якщо ok дорівнюватиме нулю, то !ok дасть позитивний результат й відтоді буде отримано повідомлення про некоректність, виходячи з контексту наведеного прикладу.
Оператор switch
Синтаксис:
switch(<вираз цілого типу>) { case <значення_1>: <послідовність_операторів_1>; break; case <значення_2>: <послідовність_операторів_2>; break; .................................. case <значення_n>: <послідовність_операторів_n>; break; [default: <послідовність_операторів_n+1>;] }
Оператор-перемикач switch призначений для вибору одного з декількох альтернативних шляхів виконання програми. Виконання оператора switch починається з обчислення значення виразу, що слідує за ключовим словом switch у круглих дужках. Далі управління передається одному з <операторів>. Оператор, що отримав управління - це оператор, значення константи якого співпадає зі значенням виразу перемикача.
Вітка default (може опускатися) означає, що якщо жодна з вищенаведених умов не задовольнятиметься (тобто вираз цілого типу не дорівнює жодному із значень, що позначені у саse-фрагментах), керування передається по замовчуванню в це місце програми. Треба також зазначити обов'язкове застосування оператора break у кожному з case-фрагментів (цей оператор застосовують для негайного припинення виконання операторів while, do, for, switch), що негайно передасть керування у точку програми, що слідує відразу за останнім оператором у switch-блоці.
Приклад 1:
switch(i) { case -1: n++; break; case 0: z++; break; case 1: p++; break; }
Приклад 2:
switch(c) { case 'A': capa++; case 'a': lettera++; default: total++; }
В останньому прикладі всі три оператори в тілі оператора switch будуть виконані, якщо значення с рівне 'A', далі оператори виконуються в порядку їх слідування в тілі, так як відсутні break.
Оператор циклу з передумовою while
Оператор while використовується для організації циклічного виконання оператора або серії операторів, поки виконується певна умова.
while (<логічний вираз>) оператор;
Цикл закінчується у наступних випадках:
-
умовний вираз у заголовку приймає нульове значення;
-
у тілі циклу досягнуто місця, де розташований оператор break;
-
у тілі циклу виконаний оператор return;
У перших двох випадках керування передається оператору, розташованому безпосередньо за циклом, у третьому випадку активна на той момент функція завершує свою роботу, повертаючи якесь значення.
Знову ж таки нерідкою помилкою програмістів є використання замість оператора порівняння (==) оператора присвоювання (=). Наприклад, наступний фрагмент створить нескінчений цикл:
/* некоректне використання оператору циклу */ int main(void) { int j=5; while(j=5) /* змінній j присвоїти значення 5 */ { printf("%d\n",j); j++; } }
Компілятор Сі попередить про некоректне присвоювання в даному випадку, виправити яке особливих труднощів не викличе. Втім, часто такий цикл використовується для перевірки відповіді користувача на питання з програми ("так чи ні ?"):
/* фрагмент використання while */ printf ("Підтверджуєте ? Так чи ні ?(y/n);"); scanf("%c",&ch); while (ch!='y' && ch!='n') { printf("\n Відповідайте так чи ні . . (y/n);"); scanf("%c",&ch); }
Тіло циклу почне виконуватися, якщо користувач введе будь-який символ, відмінний від у або n. Цикл виконується доти, доки користувач не введе або 'у' , або 'n'.
Цікаво розглянути й наступний приклад, що застосовує оператор while у функції підрахунку факторіалу:
long factorial(int number) { long total; total=number; while (--number) total*=number; return total; }
Оператор циклу з постумовою do … while
Оператор do…while використовується для організації циклічного виконання оператора або серії операторів, які називаються тілом циклу, до тих пір, поки умова не стане хибною.
do <оператор>; while (<логічний_вираз>);
Ситуації, що призводять до виходу з циклу, аналогічні наведеним для циклу while із передумовою. Характерним є те, що тіло циклу виконається хоча б один раз. Цикл do ... while припиняє виконання, коли умовний вираз обертається в 0 (хибно).
Приклад 1.
printf ("Підтверджуєте ? Так чи ні ?(y/n);"); do scanf("%c",&ch); while (ch!='y' && ch!='n')
Приклад 2.
#include<stdio.h> #include<conio.h> void main() { int n,i; float fact; printf("Програма обчислення n!.\n"); printf("Введiть число n :\n"); scanf("%d",&n); i = 1; fact = 1; do { fact *= i; i++; } while (i <= n); printf("n!==%g",fact); }
Оператор розриву break
Синтаксис :
break;
Оператор розриву break перериває виконання операторів do, for, while або switch. В операторі switch він використовується для завершення блоку case. В операторах циклу - для негайного завершення циклу, що не зв'язане з перевіркою звичайної умови завершення циклу. Коли оператор break зустрічається всередині оператора циклу, то здійснюється негайний вихід з циклу і перехід до виконання оператору, що слідує за оператором циклу.
Приклад:
main() { int i; for (i=0;i<1000;i++) { printf("%d - %d\n",i,i*i*i); if (i*i*i>=10000) break; } return 0; }
Оператор продовження continue
Синтаксис :
continue;
Оператор continue передає управління на наступну ітерацію в операторах циклу do, for, while. Він може розміщуватися тільки в тілі цих операторів. В операторах do і while наступна ітерація починається з обчислення виразу умови. Для оператора for наступна ітерація починається з обчислення виразу зміни значення лічильника.
Приклад
while (i-- > 0) { x=f(i); if (x == 1) continue; else y=x*x; }
В даному прикладі тіло циклу while виконується якщо i більше нуля. Спочатку значення f(i) присвоюється змінній x; потім, якщо x не рівний 1, то y присвоюється значення квадрата числа х, і управління передається на "заголовок" циклу, тобто на обчислення виразу (i-- > 0). Якщо ж х рівний 1, то виконується оператор продовження continue, і виконання продовжується з "заголовку" оператора циклу while, без обчислення квадрата x.
Оператор циклу for
Оператор for забезпечує циклічне повторення деякого оператора певне число разів. Оператор, який повторюється називається тілом циклу. Повторення циклу зазвичай здійснюється з використанням деякої змінної (лічильника), яка змінюється при кожному виконанні тіла циклу. Завершується, коли лічильник досягає заданого значення.
Синтаксис оператора:
for([ініціалізація];[перевірка_умови];[нове_значення]) оператор ;
Звернемо увагу на те, що кожен з трьох виразів може бути відсутнім. Перший вираз служить для ініціалізації лічильника, другий - для перевірки кінця циклу, а третій вираз - для зміни значення лічильника. Формально роботу циклу можна описати такими кроками:
-
якщо перший вираз (ініціалізація) присутній, то він обчислюється;
-
обчислюється вираз умови (якщо він присутній). Якщо умова виробляє значення 0, тобто вона невірна, цикл припиняється, у протилежному випадку він буде продовжений;
-
виконується тіло циклу;
-
якщо присутній вираз зміни лічильника, то він обчислюється;
-
надалі перехід до пункту під номером 2.
Поява у будь-якому місці циклу оператора continue призведе до негайного переходу до пункту 4.
Приклад використання циклу for
/* друк парних чисел у проміжку від 500 до 0 */ #include <stdio.h> void main(void) { long i; for(i=500;i>=0;i-=2) printf("\n%ld",i); printf("\n"); }
Для того, щоб продемонструвати гнучкість даного різновиду циклу, розглянемо інші варіанти цієї ж програми. У першому випадку представимо весь перелік обчислень лише в одному операторі for, за яким слідує порожній оператор:
#include <stdio.h> int main(void) { long i; for(i=500;i>=0;printf("\n%ld",i),i-=2) ; }
Другий варіант використовує оператор continue:
#include <stdio.h> int main(void) { long i; for(i=500;i>=0;i--) if (i%2 == 1) continue; else printf("\n %ld", i ); printf("\n"); }
Справа програміста, який з варіантів обрати - надати перевагу більш стислому викладанню або навіть взагалі скористатися іншим оператором. Цікаво, що різновид циклу for можна звести до циклу while наступним чином:
for(вираз1;вираз2;вираз3) оператор;
/* далі - аналогічний цикл while */
вираз1; while (вираз2) { оператор; вираз3; }
Інша справа - чи є в такій заміні необхідність? Не завжди гнучкість переважає стислість та навпаки. Справа за конкретною ситуацією. Зрештою, вибір циклу може бути й справою смаку конкретного програміста - саме йому вирішувати, які оператори застосувати для вірного запису того чи іншого алгоритму.
Оператор безумовного переходу goto
Синтаксис
goto <мітка>; /* ... */ <мітка> : <оператор>;
Оператор goto передає управління безпосередньо на <оператор>, перед яким розташована <мітка>. Область дії мітки обмежена функцією, в якій вона визначена. Тому, кожна мітка повинна бути відмінною від інших в одній і тій самій функції. Також, неможливо передати управління оператором goto в іншу функцію.
Оператор, перед яким розташована <мітка> виконується зразу після виконання оператора goto.
Якщо оператор з міткою відсутній, то компілятор видасть повідомлення про помилку.
Приклад використання goto
if (errorcode>0) goto exit; … exit : return errorcode;
В свою чергу при появі концепції структурного програмування оператор goto піддався критиці, і його використання стало розглядатися як ознака поганого стилю програмування. Дійсно, надмірно широке використання goto робить структуру програми надмірно заплутаною, тому без особливої необхідності намагайтесь обходитися без оператора goto.
"Порожній" оператор ;
Синтаксис: ; Порожній оператор - це оператор що складається лише з крапки з комою. Він може використовуватися в будь-якому місці програми, де за синтаксисом потрібний оператор.
for (i=0;i<10;printf("%d\n",i);) ;
"Складений" оператор
"Складений" оператор представляє собою два або більше операторів. Його також називають "блоком".
Синтаксис :
{ [<оператори>] }
Дія складеного оператора полягає в обов'язковому послідовному виконанні операторів, які містяться між { та }, за виключенням тих випадків, коли який-небудь оператор явно не передасть управління в інше місце програми.
if (i>0) { printf("i == %d\n",i); i--; }