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

osn_progr_final

.pdf
Скачиваний:
37
Добавлен:
12.02.2016
Размер:
3.27 Mб
Скачать

В цьому випадку в якості параметра функції (а він має бути одним згідно з оголошенням) виступає результат операції послідовного виконання: для випадку (a+=b,6) – це константа 6, для випадку

(i++,b=5,c=9,a=b+4,k=7)– це константа 7.

4.4.6 Операції інкремента(++) та декремента(--)

Існує дві форми - інфіксна та постфіксна. В інфіксній операція стоїть перед операндом, постфікснфй - після. В інфіксній формі спочатку відбувається зміна операнда, а потім він використовується у відповідному виразі. В постфіксній формі спочатку операнд використовується у виразі, а потім його значення змінюється.

Якщо операнд є значенням цілого типу, то ++ - збільшення на одиницю,-- - зменшення на одиницю. Якщо операнд є вказівником, то значення його змінюється на розмір типу, на який він вказує .

Використання операції інкремента дозволяє не тільки скорочувати текст програми шляхом використання цікавих конструкції виду i+++j, але й має особливий зміст в технології стверення програмних компонент. Як приклад розглянемо наступну задачу-жарт. Яка мова програмування краща: С чи С++?

#include <stdio.h> void main()

{

int C = 0;

puts("Що краще: С чи С++?"); if(C > C++)

puts("Очевидно, С краще."); else if (C == C++)

puts("Визначити не вдалося."); else /* C < C++ */

puts("Безсумнівно,краще С++.");

}

Яким буде результат? Давайте детальніше розглянемо цю програму. У рядку

if(C > C++) використана постфіксна форма операції інкремента. Отже, спочатку операнд, в якості якого виступає змінна С, використовується в умовному виразі. Тобто, фактично перевіряється істинність умови C > C, яка є хибною. Але зразу ж після цього спрацьовує операція інкремента, і змінна С стає рівною 1.Тоді виконується оператор else if (C == C++), де маємо аналогічну ситуацію. Тобто, фактично перевіряється істинність умови C == C, яка завжди ви-

61

конується. Тому і друкується повідомлення : Визначити не вда-

лося.

Як бачимо з цього простого прикладу, операції інкремента не просто скорочують запис, але мають особливе змістове навантаження. Щоб зрозуміти його, досить написати аналогічну програму без використання операції інкременту.

4.4.7 Операція присвоювання

Синтаксис: <операнд1>=<операнд2> На відміну від ряду інших мов програмування, в мові Сі присво-

єння виступає не як оператор, а як операція. Як і будь-яка інша операція, операція присвоєння повертає значення. Значенням операції присвоєння є значення правого операнда . Воно повинне мати тип лівого операнда ( якщо типи не співпадають, відбуваються перетворення типів по замовчуванню). Остання обставина дозволяє записувати конструкції виду:

a=(i=5)+4;

a=b=c=d=1;

В першому рядку відбувається одночасно присвоєння відповідного значення (числа 9) змінній а та ініціалізація змінної і. У другому рядку прикладу бачимо ілюстрацію серії ініціалізацій.

Існує складена операція присвоювання. Синтаксично вона складається з двох символів–символу деякої бінарної операції та символу присвоювання:

+=, *=, /=, %=, <<=, >>=, ^=, !=, -=, &=

Специфіка складеної операції присвоювання полягає в тому, що спочатку виконується операція, значок якої стоїть першим, а потім результат присвоюється першому операнду. Приклад:

int i=1,j=2; i+=j; /*i==3*/; i+=1; /*i==2*/;

4.4.8 Умовна операція

<умовний вираз>?<вираз1>:<вираз2> Спочатку обчислюється умовний вираз. Якщо його значення істинне,

то обчислюється вираз1, інакше-вираз2. Типи виразів повинні співпадати.

Конструкція

if (i>5) j=3 ; else j=4;

еквівалентна наступній:

62

(i>5)? j=3 : j=4;

4.4.9 Операція sizeof sizeof <вираз>.

Повертає розмір об`єкта в байтах. int rozmir; rozmir=sizeof (int);

rozmir=sizeof (a+b-4+3.5e-4);

Стосовно імені масиву операція sizeof дає весь розмір масиву в байтах. Наприклад, якщо оголосити масив char s[23]; , то sizeof(s) поверне значення 23.

На практиці може статися ситуація, коли сума розмірів компонент структури не дорівнює її розміру, тоді операція sizeof дасть реальний розмір об`єкта.

4.4.10 Унарний “+”

Існує специфічна унарна операція + , яка забороняє компілятору реорганізовувати вирази. Компілятор з високим рівнем оптимізації намагається реорганізовувати вирази , враховуючи пріоритет операцій. Наявність унарного плюса забезпечить виконання операцій саме в такій послідовності, як вказано в дужках.

Приклад: int x,y=5,z=4;

x=12000*+(y*0.001);

4.4.11 Пріоритет операцій

Пріоритет операцій записаний у наступній табличці. Зростає він зверху вниз. Порядок слід враховувати при обчисленні виразів з рівним пріоритетом.

Операція

Порядок

== !=

зліва направо

 

 

() [] -> .

зліва направо

&

зліва направо

^

зліва направо

! ~

справа наліво

|

зліва направо

++ -- -

справа наліво

&&

зліва направо

(type) * & sizeof справа наліво

||

зліва направо

* / %

зліва направо

?:

справа наліво

+ -

зліва направо

= += -=

і т.д. справа наліво

<< >>

зліва направо

,

зліва направо

< <= > >=

зліва направо

 

 

 

 

 

 

63

4.5 ОПЕРАТОРИ МОВИ С

4.5.1 Порожній оператор

Синтаксис: ;

Використовується у випадку по синтаксису, вимагається наявність оператора .

Приклад: for(;;);

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

4.5.2 Складений оператор

Синтаксис: { }

Складений оператор відіграє величезну роль при написанні програм. Він дозволяє синтаксично об”єднувати кілька операторів в одне ціле, що вважатиметься одним оператором. Наприклад, можемо об’єднати в одному складеному операторі декілька простих операторів присвоювання:

{

a=4;

b=5;

c=6;

}

Складений оператор може містити оголошення та визначення. Як буде детальніше розглянуто далі, визначені таким чином змінні будуть мати локальну область дії, тобто доступними будуть лише в межах даного складеного оператора. Складений оператор , очевидно, може містити інші складені оператори:

{ int i=5; j++;

{ float i=4; i+=5;}

}

4.5.3 Умовний оператор

Умовний оператор відіграє ключову роль при описі алгоритмів, що мають структуру типу розгалуджень.

Синтаксис: if (<умовний вираз>) <оператор1>[else<оператор2>]

64

Дія цього оператора є наступною: якщо <умовний вираз> виявиться істинним значенням (а таким значенням в мові С вважається будьяке число, відмінне від нуля), то виконується <оператор1>. В противному випадку, при наявності необов”язкової конструкції [else<оператор2>], виконується <оператор2>.

Приклад:

if (a>5) i++; else j++;

Допускається послідовне використання оператора: if (i>j) i++;

else

if (i==j) j++;

else printf(“the end”);

При такий інтерпретаціі знаходиться оператор else і пов’язується з найближчим оператором if у якого відсутня конструкція else. Тобто останній фрагмент програми еквівалентний наступному:

if (i>j) i++; else

{ if (i==j) j++;

else printf(“the end”);}

Використання складеного оператора в такому випадку робить програму більш читабельною і, звичайно, має переваги.

Відмітимо, що на відміну від інших мов програмування, в Сі немає ключового слова then. Ті, хто вивчає Сі , вже знаючи мову Pascal, дуже часто роблять помилки, як у наступному фрагменті програми:

if( x > 2

)

then

x

= 2;

Зауважимо також, що умови в операторах if (а також while) повинні братись в круглі дужки. Тому в наступному рядку буде помилка:

if x < 1 x = 1;

4.5.4 Оператор циклу for

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

Синтаксис: for([<початковий вираз>];[<умовний вираз>];[<виразприріст>])<оператор>

Дія цього оператора наступна. Спочатку обчислюється <почат-

65

ковий вираз> , тоді перевіряється <умовний вираз>, у випадку його істинності виконується <оператор> і після цього виконується <виразприріст>. Як бачимо, <початковий вираз>, <умовний вираз>,<виразприріст> можуть бути відсутніми. Обов’язковою є лише наявність <оператора> (при цьому зауважимо, що синтаксично повинні бути присутніми відповідні крапки з комою). Якщо умовного виразу немає, то значення його вважається істинним. Тому в певному розумінні мінімальний оператор for, як вже наводилось вище, має вигляд: for(;;);.

Як правило, <початковий вираз> на практиці використовується для початкової ініціалізації змінних, <умовний вираз> визначає умову виконання оператора, що задає тіло циклу, а <вираз-приріст> задає умову зміни змінних, що використовуються в циклі, наприклад, як лічильники. Нехай, наприклад, нам необхідно декілька разів надрукувати повідомлення “Hello, world”. Для цього використаємо оператор for:

for (i=j=0;i<10;i++); printf (“Hello, world \n”);

Відповідне повідомлення надрукується 10 разів. При цьому, як бачимо, у <початковому виразі> ініціалізуються зразу дві змінних-i та j.

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

Розглянемо ще один приклад. Нехай необхідно надрукувати рядок довжини WIDTH, в якому спочатку міститься x0 символів '-', потім w символів '*', і до кінця рядка - знову символи '-':

int x;

 

 

 

for(x=0; x < x0;

++x) putchar('-');

for(

; x <

x0 + w; x++) putchar('*');

for(

; x <

WIDTH ; ++x) putchar('-');

putchar('\n');

або

for(x=0; x < WIDTH; x++)

putchar( x < x0 ? '-' : x < x0 + w ? '*' :

'-' );

putchar('\n');

Допоміжні змінні, які не мають змісту (наприклад, лічильник повторення циклу, який не використовується в самому тілі циклу) прийня-

66

то по традиції позначати однією літерою, наприклад, i, j. Більш того, можна навіть написати:

main(){ int _ ;

for( _ = 0; _ < 10; _++) printf("%d\n", _ );

}

(підкреслювання в ідентифікаторах – рівноправна літера)

4.5.5 Оператори dowhile

Поряд з оператором for, для запису циклічних алгоритмів використовуються також оператори do-while та while.

Синтаксис: while(<вираз>) <оператор>

Дія наступна: поки істинний <вираз> виконується <оператор>.

Синтаксис: do <оператор> while(<вираз>)

Дія наступна: виконується <оператор> поки істинний <вираз>.

Для того, щоб зрозуміти відмінність у цих операторах, розглянемо два простих фрагменти програм:

1)

int i=10; while(i>0) {i--; printf(“%d”,i);

}

2)

int i=10; do

{i--; printf(“%d”,i);

}

while(i>0);

В обох прикладах друкується значення змінної і, починаючи з 10. Однак в першому прикладі останнє надруковане значення “0”, а в другому – “-1”. Дійсно, в другому прикладі спочатку виконується тіло оператора, а вже потім перевіряється умовний вираз. Якщо він виявиться хибним, то оператор більше не виконується.

Відмітимо, що при використанні циклів не потрібно забувати ініціалізувати зміні, що виступають в якості лічильників. Розглянемо фрагмент програми, у якій деяке повідомлення повинно друкуватись 10 разів:

main(){ /* друк фрази 10 раз */ int i;

while(i < 10){

67

printf("%d-ий раз\n", i+1); i++;

}

}

Однак тут автоматична змінна i не була проініціалізована і містить не 0, а якесь довільне значення. Цикл може виконуватись не 10, а довільне число раз (в тому числі і 0 ). Тому у відповідному рядку необхідно написати: int i = 0;

В даному прикладі було б ще краще використовувати цикл for, в якому всі операції над індексом циклу зібрані в одному місці - в заголовку циклу:

for(i=0; i < 10; i++) printf(...);

4.5.6 Оператор continue

Синтаксис: continue;

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

i=10;

while(i>0)

{

i--;

if (i%2==0) continue; printf(“%d”,i);

}

В цьому прикладі будуть надруковані непарні значення змінної і від 9 до 1. Якщо значення змінної i виявиться парним, то виконується оператор continue, який перериває виконання циклу та виконується наступна ітерація.

4.5.7 Операторперемикач switch

Синтаксис: switch(<вираз>)

{

[оголошення чи визначення ] [case<константний вираз>:<оператор>]

[case<константний вираз>:<оператор>]

[default:<оператор>]

}

68

Обчислюється <вираз> і наступним виконується той оператор, який слідує за умовою case, для якої значення константного виразу співпадає з значенням початкового <виразу>.

Приклад: switch(c=getch()) { int i,j,k=0;

case’a’:i++; printf(“%d\n”,i);break; case’b’:j++; break;

default:k++; }

В залежності від значення c відбувається виконання тих операторів , що визначаються умовою case. Тобто,

якщо с==’a’, виконується

i++; printf(“%d\n”,i);break;

якщо с==’b’, виконується j++;break;

інакше виконується

k++;

Відмітимо, що відсутність оператора break в нашому прикладі призведе до того, що після вибору якогось оператора по case-умові будуть виконуватись усі оператори , що стоять після нього, в тому числі і помічені іншими умовами case.

4.5.8 Оператор break

Синтаксис: break;

Перериває виконання операторів циклу та switch. Як вже вказувалось вище, для того, щоб в попередньому прикладі забезпечити виконання лише одного оператора case або default, необхідно в кінці кожної групи операторів case ставити break.

Приклад:

for (i=0;i<10;i++) for (j=0;j<20;j++)

{ if ( j%5==4) break;

}

В даному прикладі при виконанні умови j%5==4 відбувається переривання виконання внутрішнього циклу. При цьому зовнішній цикл по i продовжує виконуватись.

4.5.9 Оператор goto

Синтаксис: goto<мітка>

69

<мітка>:<оператор> Передає управління на мітку. Міткою може бути будь-який ідентифі-

катор. Наприклад, у випадку коли потрібно вийти з кількох циклів при великому рівні вкладеності (більше ніж 2) єдина можливість - це оператор goto. Можна увійти за допомогою goto в блок, тіло циклу, оператор switch.

Приклад: for(i=0;i<100;i++) for(j=0;j<100;j++) for(k=0;k<100;k++)

if (error(i,j,k)) goto exit; exit: ;

В даному прикладі у випадку, коли значення деякої функції error виявиться ненульовим, переривається виконання усіх циклів та управління передається на мітку exit, за якою стоїть порожній оператор.

4.5.10 Оператор return

Синтаксис: return <константний вираз>

Повертає управління у викликаючу функцію. Розглянемо наступні функції main():

main( )

void main( )

{

{

return 0;}

}

В першому випадку функція main() за замовчуванням повинна повертати значення типу int. Тому в тілі цієї функції міститься оператор return. В другому випадку тип результату–void, тому оператор return відсутній. Взагалі, будь-яка функція, що має тип результату, відмінний від void, повинна містити оператор return .

Відмітимо, що значення, яке повертається оператором return, на практиці часто використовується для видачі певної інформації . Наприклад, якщо функція відпрацювала нормально, можна повернути нуль.

4.6 ДИРЕКТИВИ ПРЕПРОЦЕСОРУ ТА ВКАЗІВКИ КОМПІЛЯТОРУ

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

70

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