МІНІСТЕРСТВО ОСВІТИ І НАУКИ УКРАЇНИ
Національний університет “Львівська політехніка”
ПІДПРОГРАМИ В МОВІ ПРОГРАМУВАННЯ С
Інструкція
до Лабораторної роботи № 5
з курсу “Проблемно-орієнтовані мови програмування”
для студентів базового напрямку 6.08.04
"Комп’ютерні науки"
ЗАТВЕРДЖЕНО
на засіданні кафедри
системи автоматизованого проектування
Протокол № від р.
ЛЬВІВ 2007
Підпрограми в мові програмування С. Інструкція до лабораторної роботи № 5 з курсу “Проблемно-орієнтовані мови програмування” для студентів базового напрямку 6.08.04 "Комп’ютерні науки“/Укл. С. І. Бобало, О. Р. Корбецький, В. І. Каркульовський -Львів: ДУ “ЛП”, 2007 р.- 10 с.
Укладачі С. І. Бобало, асист.,
О. Р. Корбецький, асп.,
В. І. Каркульовський, канд.техн.наук
Відповідальний за випуск С. П. Ткаченко, канд.техн.наук, доц.
Рецензенти М. В. Лобур, канд.техн.наук,
І. І. Чура, канд.техн.наук
1. МЕТА РОБОТИ
Мета роботи - ознайомитися із особливостями застосування підпрограм у алгоритмічній мові С.
2. ТЕОРЕТИЧНІ ВІДОМОСТІ
Підпрограми
Дуже часто виникає потреба у виконанні тої ж самої послідовності дій на різних етапах обробки інформації. В алгоритмах такого роду у різних місцях зустрічаються однакові за діями фрагменти, які відрізняються лише значеннями вхідних даних. Для підвищення ефективності програмування введено поняття підпрограми, або функції.
Загальний вигляд функції
Загальний вигляд функції такий:
тип ім’я_функції( список параметрів )
{
тіло функції
}
Тип визначає тип значення, яке поверне функція використовуючи оператор return. Якщо тип не визначений, то функція поверне значення типу integer по замовчуванню. Список параметрів являє собою список, розділених попарно комами, типів та імен змінних, значення яких отримує в якості аргументів функція при виклику. У випадку, коли функція не має аргументів, список параметрів є порожнім, але дужки не опускаються.
Для прикладу наведено правильний опис функції f:
f( int x, int y, float z )
та опис, що є невірним, через int x,y:
f( int x, y, float z )
Отже, кожна змінна у списку параметрів разом із своїм типом має бути відокремлена комою від інших.
Вихід з функції
Існують два шляхи завершення виконання функції і повернення у програму, яка здійснила виклик. Перший шлях - послідовне проходження всього тіла функції, наприклад, функція, що друкує значення х:
void simple( int x )
{
printf( “%d”, x );
}
Другий шлях - завершення виконання функції з використанням оператора return. Наступна функція завершує виконання, якщо значення змінної х рівне нулю або досягнуто кінець функції. Оператор return викликає завершення функції, хоча вона не виконалася до кінця (якщо х = 0).
void divide( float x, float y )
{
float z;
if (x == 0) return; /* не можна ділити на нуль*/
z = x / y;
printf( “Результат : %f”, z );
}
Повернення значення
Для повернення значення з функції використовується оператор return із вказаною змінною, значення якої повертається. Наприклад:
max( int a, int b )
{
int temp;
if (a > b) temp = a;
else temp = b;
return temp;
}
Зауважте, що функція повертає значення цілого типу, який по замовчуванню назначається функції, для якої явно не визначено жодного типу, тобто перед іменем функції в описі не вказано тип. Дозволяється використовувати більше одного оператора return. Це спрощує розуміння алгоритму.
Наприклад, попередня функція max() може бути написана так:
max( int a, int b )
{
if (a > b) return a;
else return b;
}
Функції, які повертають значення можна використовувати так, як показано у прикладі:
if ( max(x, y) > 20) printf( “Більше” );
Але функції не можна присвоювати значень:
max(x, y) = 20;
Часто виникають питання, чи потрібно оголошувати змінну для того, щоб повернути значення з функції. Відповідь - ні. Розглянемо приклад:
#include <stdio.h>
main()
{
int x, y;
x = 10; y = 20;
printf(“%d”, mul(x, y));
}
mul(int a, int b)
{
return( a*b );
}
Прототип функції
Прототип функції виконує два завдання. Перше полягає в ідентифікації типу значення, яке поверне функція, так, щоб компілятор міг з генерувати коректний код для типу даних, що повертаються. Друге завдання - у визначенні типу та кількості аргументів, які використовуються функцією. Загальний вигляд прототипу наступний:
тип ім’я_функції( список параметрів );
Прототипи звичайно розташовують перед головною програмою або у заголовочному файлі ( з розширенням .h).
Приклад:
#include <stdio.h>
float sum( float a, float b); /*прототип функції*/
main()
{
float f, s;
f = 10.5;
s = 15.3;
printf(“%f”, sum(f, s));
}
float sum( float a, float b)
{
return (a + b);
}
При передачі параметрів у функцію додатково виконуються приведення типів фактичних аргументів до типу, який вказано у прототипі функції.
Функція типу void
У випадку, коли функція не повертає жодного значення, вона оголошується як void. Приклад правильного використання такої функції:
#include <stdio.h>
void sign( int a);
main()
{
sign(5);
}
void sign( int a)
{
if (a > 0) printf(“Число додатнє”);
else printf(“Число від’ємне”);
}
У випадку, коли у функції відсутні параметри, і вона не повертає значення, то її вигляд наступний:
#include <stdio.h>
void myname( void );
main()
{
myname();
}
void myname(void)
{
printf(“John”);
}
До операторів у тілі функції можна доступитися тільки через виклик цієї функції і неможливо інакше, наприклад через оператор goto. Усі змінні, які оголошені всередені функції є локальними, тобто вони створюються при виклику функції і зникають при її завершенні.
Параметри та аргументи функцій
- виклик функції з передачею значення
Цей метод виклику функції полягає в тому, що значення аргументу копіюється як формальний параметр підпрограми. Тому зміни значення цього параметра всередині підпрограми не впливають на значення змінних, які використовувалися для виклику. Наприклад:
#include <stdio.h>
sqr(int x);
main()
{
int t = 10;
printf(“%d %d”, sqr(t), t);
}
sqr(int x)
{
x = x*x;
return (x);
}
У цьому прикладі значення аргументу для sqr() - 10 скопіювалося у параметр х. Коли відбулося присвоєння х = х*х, змінилося значення лише локальної змінної х.
Змінна t, яка використовувалася для виклику sqr(), і надалі має значення 10. А на екран виведеться: 100 10.
- виклик функції з передачею адрес змінних
Використовуючи вказівник на аргумент в якості формального параметра функції можна змінювати значення аргументу всередині функції. Вказівники передаються так само, як і інші типи. Звичайно, потрібно оголосити параметри як вказівник на тип. Попередній приклад з використанням адрес буде виглядати так:
#include <stdio.h>
sqr(int *x);
main(void)
{
int t = 10;
printf(“%d”, sqr(&t));
printf(“%d”, t);
}
sqr(int *x)
{
*x = (*x)*(*x);
return (*x);
}
У наведеному прикладі у функцію передається копія вказівника на комірку пам’яті (іншими словами, адреса в пам’яті). Ця адреса використовується для доступу до пам’яті. Вміст змінної t став рівним 100. На екрані: 100 100
Виклик функцій з аргументом у вигляді масиву
Коли масив використовується як аргумент, передається лише адреса масива, а не копія всього масиву. Коли викликається функція з аргументом у вигляді масиву, передається лише адреса першого елемента масиву. (Нагадаємо, що у мові С ім’я масиву без індексу є вказівником на перший елемент масиву.)
Найбільш широко вживаною формою у професійно написаних С-програмах передачі масиву як аргумента є:
#include <stdio.h>
void display( int *num);
main()
{
int t[10], i;
for(i = 0; i < 10; i++) t[i] = i;
display(t);
}
void display(int *num)
{
int i;
for(i = 0; i < 10; i++) printf(“%d”, num[i]);
}
Функція із змінним числом параметрів
Мова програмування С дозволяє використання змінного числа аргументів. Ознакою функції з і змінним числом аргументів є три крапки “...” у списку параметрів прототипу функції. Зустрівши “...”, компілятор зупиняє контроль відповідності типів параметрів для такої функції. Звичайно, функція зі змінним числом аргументів повинна мати засіб визначення точного їх числа при кожному виклику. Далі наводяться два приклади функції example(), яка приймає довільне число аргументів і виводить на екран їх кількість та набуті ними значення. У першому варіанті функції число дійсно переданих через стек значень задає перший аргумент:
#include <stdio.h>
void example( int, ...);
void main(void)
{
int var1 = 5, var2 = 6;
int var3 = 7, var4 = 8;
int var5 = 9;
example( 2, var1);
example( 3, var1, var2);
example(var1, var2, var3,
var4, var5, 0);
}
#include <stdio.h>
void example( int, ...);
void main(void)
{
int var1 = 5, var2 = 6;
int var3 = 7, var4 = 8;
int var5 = 9;
example(var1, 0);
example(var1, var2, 0);
example(var1, var2, var3,
var4, var5, 0);
}
void example( int arg1, ...)
{
int num = 1;
/*вказівник на перший аргумент*/
int *ptr = &arg1;
printf(“\nФункції передано %d
аргументи(ів),\n результат: ”, arg1);
for( ; arg1; arg1--){
printf(”%d ”, *ptr);
ptr++;
}
}
void example( int arg1, ...)
{
int num = 1;
/*вказівник на перший аргумент*/
int *ptr = &arg1;
printf(“\nФункції передано %d
аргументи(ів), \n результат: ”,
arg1);
while( *ptr) {
printf(”%d ”, *ptr);
ptr++;
}
}
Результат виконання програми (перший і другий варіанти відповідно):
Функції передано 2 аргументи(ів), Функції передано 2 аргументи(ів),
результат: 2 5 результат: 5
Функції передано 3 аргументи(ів), Функції передано 3 аргументи(ів),
результат: 3 5 6 результат: 5 6
Функції передано 6 аргументи(ів), Функції передано 6 аргументи(ів),
результат: 6 5 6 7 8 9 результат: 5 6 7 8 9
Зверніть увагу на те, що для доступу до фактичних параметрів функції example() використовується пересування вказівника вперед. Саме в такому порядку розташовуються у стеку копії аргументів при С-порядку передачі аргументів у функції (cdecl): останній аргумент записується в стек самим першим. Такий порядок вибирається по замовчуванню. Альтернативою С-порядку є послідовність передачі параметрів прийнята у мові Pascal: копії аргументів записуються в стек починаючи з першого. Тип функції у прототипі повинен співпадати з типом в описі функції.
Рекурсія
У С функції можуть викликати самі себе. Функція називається рекурсивною, якщо вираз в тілі функції містить виклик самої функції. Для того, щоб алгоритмічна мова була би рекурсивною, потрібно, щоб функції мали можливість викликати самі себе. Класичний приклад рекурсії демонструє функція factr(), яка обчислює факторіал цілого числа. Факторіал числа N є добутком всіх цілих чисел між 1 та N. Обидва варіанти цієї функції - рекурсивний та ітеративний наведені нижче:
factr( int n) /*рекурсивна*/
{
int ans;
if (n == 1) return 1;
ans = factr(n-1)*n;
return(ans);
}
factr( int n) /*не рекурсивна*/
{
int t, ans;
ans = 1;
for( t = 1; t <= n ; t++) ans = ans*t;
return (ans);
}
Робота нерекурсивної версії функції factr() легка в розумінні, а рекурсивної - більш складна. Коли factr() викликається з аргументом 1, функція повертає 1; в іншому випадку вона повертає добуток factr(n-1)*n. Для обчислення цього виразу factr() викликається з виразом n-1. Це повторюється до того часу , поки n не стане рівне 1.
Коли ви пишите рекурсивну функцію, необхідно в її тілі забезпечити для нерекурсивного виходу з функції if вираз. Якщо цього не зробити, то після виклику функції вона ніколи не поверне значення. Ця помилка є найбільш поширеною при написанні рекурсивних функцій.
Передача параметрів у функцію main()
Вихід з функції main()
Функція main() обов’язково присутня в кожній програмі. Вона отримує управління після запуску програми на виконання. В іншому - це звичайна функція, якій можуть передаватися аргументи і яка може повертати значення в точку виклику. Для того, щоб був можливим доступ до аргументів, функцію main() потрібно описати в одній з двох форм.
тип main(int argc, char **argv)
{
тіло функції
}
тип main(int argc, char **argv, char **env)
{
тіло функції
}
У першому випадку функція main() буде мати доступ до всіх слів командної стрічки, а у другому ще і до текстових стрічок середовища програми. Зміст аргументів, які передаються функції main():
- int argc - число слів у командній стрічці, при запуску програми з оболонки операційної системи;
- char **argv - вказівник на масив вказівників з argc елементів. argv[0] - назва запущеного на виконання файлу, argv[1] - початок першого слова командної стрічки і т.п.
- char **env - вказівник на масив вказівників зі змінним числом елементів.
Наведемо приклад роботи функції main():
#include <stdio.h>
main(int argc, char **argv)
{
if (argc != 3) {
printf(“Ви забули вказати Ваше прізвище та ім’я”);
return 1; /*Помилка*/
}
printf(“Привіт, %s %s”, argv[1], argv[2]);
}
Якщо ви назвали програму name, а ваше повне ім’я Левко Грубко, то для виконання програми ви повинні набрати: name Левко Грубко
В результаті виконання програми на екрані з’явиться стрічка
Привіт, Левко Грубко
Наведемо ще один приклад командної стрічки: arj.exe a -r name.arj
argc=4; argv[0] = “arj.exe”; argv[1] = “a”; argv[2] = “-r”; argv[3]=“name.arj”
Як і для звичайної функції повернення значення з main() виконується оператором return.
3. КОНТРОЛЬНІ ЗАПИТАННЯ
Коли виникає необхідність застосування функцій?
Який загальний вигляд функцій?
Як функція повертає значення?
Які завдання виконує прототип функції?
Як виглядає функція типу void?
Як реалізується виклик функції з передачею значень?
Як реалізується виклик функції з передачею адреси змінних?
Як реалізується виклик функції з масивом?
Як реалізується виклик функції зі змінним числом параметрів?
Що таке рекурсія?
Параметри функції main().
4. КОНТРОЛЬНЕ ЗАВДАННЯ
Ознайомитись із особливостями використання підпрограм у С.
Одержати індивідуальне завдання.
Скласти блок-схему алгоритму та програму на С, що дозволяє із використанням функції реалізувати розв’язок поставленої задачі.
Виконати обчислення по програмі.
5. ЗМІСТ ЗВІТУ
Мета роботи.
Короткий опис особливостей застосування підпрограм у С.
Індивідуальне завдання.
Блок-схема алгоритму для обчислення по індивідуальному завданню.
Текст програми на С.
Результати обчислень по програмі.
Аналіз результатів, висновки.
6. СПИСОК ЛІТЕРАТУРИ
Керниган Б., Ритчи Д. Язык программирования С. - М. - Финансы и статистика. - 1992. – 272 с.
Уэйт М., Прата С., Мартин Д. Язык С. Руководство для начинающих. - М. - Мир. - 1988. –512 с.
Глинський Я. М., Анохін В. Є., Ряжська В. А. C++ i C++ Builder. – Львів: Деол. – 2003. – 192 с.
Герберт Шилдт. Полный справочник по C++. М. – С.-П.-К., Вильямс. – 2003. – 800 с.
Демидович Е. М. Основы алгоритмизации и программирования. Язык Си. (Учебное пособие). – Санкт-Петербург: “БХВ Петербург”. – 2006. – 439 с.
Додаток
ВАРІАНТИ ІНДИВІДУАЛЬНИХ ЗАВДАНЬ
Використовуючи метод половинного ділення і метод хорд знайти корені рівняння: . Продемонструвати графічний розв’язок цього рівняння.
Протабулювати функцію: на проміжку . Скласти функцію, що побудує графік такої функції.
Задано комплексні числа . Представити ці числа в тригонометричній формі: де -модуль комплексного числа -аргумент комплексного числа; . Використовуючи функцію, обчислити добутки цих комплексних чисел:
Скласти підпрограму переводу десяткового числа в двійкову систему числення.
Використовуючи метод хорд знайти корінь рівняння: якщо відомо що розв’язок лежить в інтервалі . Обчислення провести з точністю
Використовуючи формулу прямокутників: де -крок; - кількість інтервалів. Обчислити приблизне значення інтеграла: . Результат порівняти з варіантом 15 і 25.
З точністю обчислити для значень з кроком 0.5.
Задано дійсні числа і . Сформувати масив А, елементи якого будуть визначатися: ; формування масиву завершити коли . Дослідити як залежить розмірність масиву від значення х.
Задано масиви А(30), В(30), С(30).
Обчислити значення: ,
Обчислити значення інтеграла: . По формулі Ньютона-Лейбніца: , де F(a) i F(b) - первісна функції f(x) і по формулі Сімпсона: де - крок; n - парна кількість елементарних ділянок. Порівняти результати.
Знайти розв’язок системи нелінійних рівнянь: , використавши метод половинного ділення.
З точністю обчислити вираз для обчислення коренів степені а використати такий ряд Тейлора: , де ; . Визначити похибки що виникають при представленні кореня степені а рядом Тейлора
Скласти підпрограму переводу шістнадцяткового числа в десяткову систему числення.
Знайти розв’язок нелінійного рівняння: знаючи, що корінь знаходиться на інтервалі [2.0;2.3]. Використати числовий метод Ньютона.
Обчислити значення інтеграла: використовуючи метод трапецій: , де: h - крок; n -кількість елементарних ділянок . Результат порівняти з варіантом 6 і 25.
З точністю обчислити вираз: . Вважаючи що х лежить в інтервалі [-10,10] і змінюється з кроком .
Скласти підпрограму переводу n-розрядного двійкового числа в десяткову систему числення.
Для кожного n, заданого в діапазоні від 1 до 50 знайти методом половинного ділення з точністю найбільший корінь рівняння . Побудувати графік і графічно перевірити свій розв’язок.
Задано масив дійсних чисел time(20) i t(20), де: в масиві time записано час, а в масиві t-температура повітря в цей час. За допомогою лінійної інтерполяції вказати всі моменти часу, в які температура була однакова і рівна t. По формулі лінійної інтерполяції , де .
Дослідити і протабулювати функції: , . Скласти процедуру, яка намалює графіки цих функцій. Межі табулювання задати самостійно.
Задано комплексні числа . Представити ці числа в тригонометричній формі: де -модуль комплексного числа -аргумент комплексного числа; . Використовуючи функцію піднести ці числа до степені n=2,3,4,,5,7. Результати подати у виді таблиці. Для того щоб піднести комплексне число до цілої степені n потрібно піднести до цілого степеня модуль, а аргумент помножити на показник степені n. .
Дослідити функцію: . Протабулювати в межах [-10,10]. Скласти підпрограму, яка помалює графік цієї функції.
Скласти підпрограму переводу десяткового числа в двійкову систему числення.
Знайти розв’язок нелінійного рівняння: використовуючи метод половинного ділення. Відомо що корінь рівняння знаходиться на інтервалі [0;1].
Обчислити значення інтеграла: . Використовуючи числовий метод Сімпсона:
де - крок; n - кількість елементарних ділянок . Результат порівняти з варіантами 6 і 15.
З точністю обчислити на інтервалі з кроком .
Задано дійсні числа і . Сформувати масив А, елементи якого будуть визначатися: ; формування масиву завершити коли . Дослідити як залежить розмірність масиву від значення х.
Задано три масиви дійсних чисел: X(40), Y(40), Z(40).Обчислити
Використовуючи метод половинного ділення обчислити корені таких нелінійних рівнянь: з точністю . Відсортувати ці значення в порядку зростання.
З точністю обчислити значення виразу: . Корінь обчислити по такій ітераційній формулі: де: n=0,1,2,… . Відповіддю вважати значення , для якого .
Методом простих ітерацій обчислити корінь рівняння: знаючи, що перше наближення кореня дорівнює . Обчислення провести з точністю .
Дослідити функцію протабулювати її в межах [-10,10]. Скласти процедуру, яка намалює графік такої функції.
Задані комплексні числа . Представити ці числа в тригонометричній формі: де -модуль комплексного числа -аргумент комплексного числа; . Використовуючи функцію, обчислити добутки цих комплексних чисел:
При діленні двох комплексних чисел їх модулі діляться, а аргументи віднімаються.