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

Пособие

.pdf
Скачиваний:
67
Добавлен:
22.03.2015
Размер:
1.35 Mб
Скачать

4. Отримати квадратну матрицю розмірності N.

 

n

n 1

0

 

 

n

n 1

0

 

 

 

 

0

 

 

 

 

0

 

 

 

 

0

 

 

n

n 1

0

24.1. В одновимірному масиві, що складається з N цілих елементів, обчислити: – кількість однакових елементів; – номера елементів масиву, що діляться на 3, але не діляться на 7.

2. Перетворити масив цілих чисел розмірності N, видаливши із

масиву всі серії, довжина яких дорівнює k.

3. Дано матрицю розмірності 5х9. Після останнього стовпця, що містить лише нульові елементи, додати стовпець, що містить лише нулі.

4. Отримати квадратну матрицю розмірності N. 0 n

. .

. .

. .

. .

0 n

25.1. В одновимірному масиві, що складається з N цілих елементів, обчислити: – середнє арифметичне елементів; – добуток парних, додатних елементів.

2. Перетворити масив цілих чисел розмірності N, видаливши із

масиву всі серії, довжина яких менша k.

3. Дано матрицю розмірності 5х9. Після першого стовпця, що містить лише від’ємні елементи, додати стовпець, що містить лише нулі.

4. Отримати квадратну матрицю розмірності N.

 

n 1

 

 

2

1

0

 

 

 

 

 

 

2

1

0

1

 

 

 

2

1

0

1

2

 

 

2

1

0

1

2

 

 

 

1

0

1

2

 

 

 

 

0

1

2

 

 

n 1

91

12 ФУНКЦІЇ

Функції можуть приймати параметри і повертати значення. Будьяка програма на мові С складається з функцій, причому одна з яких обов’язково повинна мати ім’я main().

Синтаксис опису функції має наступний вигляд :

тип_поверт_значення ім’я_функції ([список_аргументів])

{

оператори тіла функції

}

Рис. 12.1. Синтаксис опису функції

Опис функції задає її ім’я, тип значення, що повертається та список параметрів.

Ім’я функції або main – для основної (головної) функції програми, або ім’я, що довільно обирається програмістом та не співпадає із ключовими словами та з іменами інших об’єктів (або функцій) програми.

Список параметрів – або пустий, або містить список формальних параметрів, кожних з яких має вигляд:

визначення_типу ім’я_параметра

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

Оголошенню функції можуть передувати специфікатори класу пам’яті extern або static.

extern – глобальна видимість у всіх модулях (по замовчуванню);

static – видимість тільки в межах модуля, в якому визначена функція.

Тип значення, що повертається функцією, може бути будь-яким, за виключенням масиву та функції (але може бути покажчиком на

92

масив чи функцію). Якщо функція не повертає значення, то вказується тип void.

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

Для звертання до функції використовується вираз з операцією "круглі дужки":

ім’я_функції (список_фактичних_параметрів);

Наприклад, нехай визначена функція з прототипом: int g(int long);

Далі в програмі використано виклик: g(3,0+m, 6.4+2);

12.1 Функції, що не повертають значення

Функції типу void не повертають значення. Вони можуть розглядатися як деякий різновид команд, реалізований особливими програмними операторами. Оператор func(); виконує функцію void func(), тобто передасть керування функції, доки не виконаються усі її оператори. Коли функція поверне керування в основну програму, тобто завершить свою роботу, програма продовжить своє виконання з того місця, де розташовується наступний оператор за опера-

тором func().

Приклад.

#include<stdio.h> void func1(void); void func2(void); main()

{

func1();

func2(); return 0;

}

void func1(void)

{

93

/*тіло*/

}

void func2(void)

{

/*тіло*/

}

Звернемо увагу на те, що текст програми починається з оголошення прототипів функцій – схематичних записів, що повідомляють компілятору ім’я та форму кожної функції у програмі. Для чого використовуються прототипи? У великих програмах це правило примушує Вас планувати проекти функцій та реалізовувати їх таким чином, як вони були сплановані. Будь-яка невідповідність між прототипом (оголошенням) функції та її визначенням (заголовком) призведе до помилки компіляції. Кожна з оголошених функцій має бути визначена у програмі, тобто заповнена операторами, що її виконують. Спочатку йтиме заголовок функції, що повністю збігається з оголошеним раніше прототипом функції, але без заключної крапки з комою. Фігурні дужки обмежують тіло функції. В середині функцій можливий виклик будь-яких інших функцій, але неможливо оголосити функцію в середині тіла іншої функції [18].

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

Приклад.

#include <stdio.h> #include <stdlib.h> #include <conio.h> #include <math.h> float A,B,C;

/*функція прийому даних*/ void GetData()

{

printf("Input A,B,C:"); scanf("%f%f%f",&A,&B,&C);

}

/*функція запуску основних обчислень*/ void Run()

{

float D; float X1, X2;

94

if ((A==0) && (B!=0))

{

X1 = (-C)/B; printf("\nRoot: %f",X1); exit(0);

}

D = B*B - 4*A*C;

if (D<0) printf("\nNo roots..."); if (D==0)

{

X1=(-B)/(2*A); printf("\nTwo equal roots: X1=X2=%f",X1);

}

if (D>0)

{

X1 = (-B+sqrt(D))/(2*A);

X2 = (-B-sqrt(D))/(2*A); printf("\nRoot X1: %f\nRoot X2: %f",X1,X2);

}

}

/*головна функція програми*/ int main()

{

GetData();

Run(); _getch(); return 0;

}

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

95

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

12.2 Передача параметрів

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

формальними параметрами, або просто параметрами, а операто-

ри, що записані в операторі виклику функції, – фактичними пара-

метрами.

Існує два способи передачі параметрів до функції: за значенням

та за адресою.

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

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

#include <stdio.h> #include <conio.h>

void f(int i, int *j, int &k); int main()

{

int i=1, j=2, k=3;

printf("i=%d j=%d k=%d\n",i,j,k); f(i,&j,k);

printf("i=%d j=%d k=%d\n",i,j,k); _getch();

return 0;

}

void f(int i, int *j, int &k)

{

i++;

(*j)++;

k++;

}

96

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

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

Якщо необхідно заборонити зміну параметра у функції, використовується модифікатор const:

int f(const char *);

char *t(char*a? const int *b);

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

12.3 Рекурсивні функції Рекурсія – це спосіб організації обчислювального процесу, при

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

(непрямий виклик).

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

void fn(int i)

{

/* ... */ fn(i);

/* ... */

}

97

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

Рис. 12.2. Непряма рекурсія

void fnA(int i); void fnB(int i); void fnC(int i); void fnA(int i)

{

/* ... */ fnB(i);

/* ... */

}

void fnB(int i)

{

/* ... */ fnC(i);

/* ... */

}

void fnC(int i)

{

/* ... */ fnA(i);

/* ... */

}

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

В якості прикладу розглянемо функцію, що рекурсивно обчислює факторіал. Як відомо, значення факторіала обчислюється за формулою: n!= n (n 1) (n 2) K 1, причому 1!=1 та 0!=1 . Факторі-

98

ал також можна обчислити за допомогою простого рекурентного співвідношення n!= n (n 1)!. Для ілюстрації рекурсії скористаємося

саме цим співвідношенням.

#include<stdio.h>

#include<conio.h> double fact(int n)

{

if (n<=1) return 1; return (fact(n-1)*n);

}

int main()

{

int n;

double value; printf("N="); scanf("%d",&n); value=fact(n);

printf("%d! = %.50g",n,value); _getch();

return 0;

}

Роботу рекурсивної функції fact() розглянемо на прикладі n = 6 . За рекурентним співвідношенням: 6!= 6 5!. Таким чином, щоб обчислити 6! ми спочатку повинні обчислити 5!. Використовуючи співвідношення, маємо, що 5!= 5 4!, тобто необхідно визначити 4!. Продовжуючи процес, отримаємо :

1). 6!= 6 5!

2). 5!= 5 4!

3). 4!= 4 3!

4). 3!= 3 2!

5). 2!= 2 1!

6). 2!=1

Укроках 1-5 завершення обчислення кожний раз відкладається,

ашостий крок є ключовим. Отримане значення, що визначається безпосередньо, а не як факторіал іншого числа. Відповідно, ми можемо повернутися від 6-ого кроку до 1-ого, послідовно використовуючи значення:

99

6).1!=1

5).2!=2

4).3!=6

3). 4!=24

2). 5!=120

1). 6!=720

Важливим для розуміння ідеї рекурсії є те, що в рекурсивних функціях можна виділити дві серії кроків.

Перша серія – це кроки рекурсивного занурення функції в са-

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

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

12.4 Додаткові можливості функції main()

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

тип main(int argc, char* argv[], char *env[]) { /* … */ }

Імена параметрів можуть мати будь-які назви, але прийнято використовувати argc, argv та env. Перший параметр argc містить ціле число аргументів командного рядка, що посилається функції main(); argv – це масив покажчиків на рядки-аргументи. argv[0] – містить повний шлях до файлу програми, що в даний момент виконується, argv[1] та argv[2] відповідно вказує на перший та другий після імені програми аргументи командного рядка, argv[argc-1] вказує на останній аргумент, env – це масив покажчиків на рядки, причому кожний елемент env[] містить рядок типу ENVVAR=значення. ENVVAR – це ім’я змінної середовища оточення програми.

#include <stdio.h> #include <stdlib.h> #include <conio.h>

int main(int argc, char *argv[], char *env[])

100