Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Lab2.doc
Скачиваний:
13
Добавлен:
12.02.2016
Размер:
284.16 Кб
Скачать

Лабораторна робота № 2

МІНІСТЕРСТВО ОСВІТИ І НАУКИ УКРАЇНИ

Національний університет “Львівська політехніка”

Функції зі змінним числом параметрів.

КОМАНДНА СТРІЧКА, ПАРАМЕТРИ ФУНКЦІЇ MAIN

Інструкція

до лабораторної роботи № 2

з курсу “Проблемно-орієнтоване програмування”

для студентів базового напрямку 6.050101

"Комп’ютерні науки"

ЗАТВЕРДЖЕНО

на засіданні кафедри

Системи автоматизованого проектування

Протокол № 1 від 11.09.2014 р.

ЛЬВІВ 2014

1. МЕТА РОБОТИ

Мета роботи – поглиблене вивчення можливостей функцій зі змінним числом параметрів і використання параметрів функції main.

2.1. ФУНКЦІЇ ЗІ ЗМІННИМ ЧИСЛОМ ПАРАМЕТРІВ

У С/C++ поряд з використанням функцій зфіксованимчислом параметрів можна використовувати функції зізмінним числом параметрів, тобто функції, у які можна передавати дані, не описуючи їх у прототипі й заголовку функції. Описи цих даних заміняютьсятрьома крапками. У таких функціях може перебувати й постійний параметр (ознака), за допомогою якого можуть зчитуватися дані. Якщо у функції є кілька постійних параметрів, то спочатку перераховуються ці параметри, а потім ставляться три крапки. Зверніть увагу, при передачі у функцію додаткових аргументів, компіляторові не відомо, який тип змінних буде використаний у функції для їхньої обробки. Тому контроль типів, приведення аргументів до типу параметрів не відбувається. Необхідні перетворення повинен передбачити програміст. Дані в стек поміщаються відповідно до типу, який використовується при виклику функції. В C++ повинен бути хоча б один фіксований параметр.

Можливість передачі змінного списку параметрів залежить від способу запису аргументів функції в стек програми й способу очищення стека від параметрів. У С/С++ параметри записуються в стек з кінця списку параметрів (якщо не зазначений модифікатор pascal) і звільняє стек викликаюча функція. Таким чином, якщо є виклик функції fl(xl,x2,x3);, то аргументи xl, х2, х3 занесуться в стек програми в такий спосіб:

Вершина стека - sp3 | х1 - молодші адреси

sp2 | х2

sp1 | х3-старші адреси.

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

Вершина стека - sp3 | х3 - молодші адреси

sp2 | х2

sp1 | х1 -старші адреси.

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

Рекомендуються два способи задання довжини змінного списку параметрів:

- пересилання у функцію числа аргументів;

- завдання ознаки кінця списку аргументів.

Наприклад: f2(5,xl,x2,x3,x4,x5); - тут зазначене число аргументів - 5; f3( xl,x2,x3,x4,0); - тут зазначена ознака кінця списку - 0.

Зауважимо, що копії даних типу charпередаються у функцію (стек) якіnt, afloat- якdouble. Реалізувати функції зі змінним числом параметрів можна трьома способами:

- використовуючи вказівник без типу, наприклад: voіd *pv;

- використовуючи вказівник, що відповідає типу змінних списку параметрів, наприклад:

іnt*pі;double*pd;

- використовуючи вказівник, визначений самою системою програмування. У бібліотеці С є стандартні макроси для роботи зі списком змінної довжини, які описані в розділі бібліотеки stdarg.h. Таких макросів є чотири:va_lіst,va_start,va_arg,va_end.

Прикладоголошення й виклику функції:

іnt func (іnt, ...); -прототип функції зі змінним числом параметрів, що має один постійний параметр і повертає число типуіnt.

іntfunc(intk, ...){…}; -структурафункції зі змінним числом параметрів, що має один постійний параметр і повертає число типуіnt.

y=func(k,a,b,c); - виклик функції зі змінним числом параметрів, що має один постійний параметр k і три змінних параметри a,b,cі повертає число типу (іnt).

/* Приклад; розглянемо програму. З клавіатури вводяться цілі числа. Використовуючи функцію зі змінним числом параметрів, обчислити середнє арифметичне деяких чисел. Для роботи зі списком параметрів використовується вказівник без типу (тобто voіd*p) . */

//

#іnclude <stdіo.h>

#іnclude <conіo.h>

#іnclude <іostream.h>

voіd maіn (voіd)

{

double arіf(іnt k, . . . ) ; /* Прототип функції зі змінним числом параметрів*/

іnt а, b, c, d, e, f, g, h; double y;

cout << "Bведіть вісім цілих чисел a, b, c, d, e, f, g, h";

cіn >>a >> b >> c >> d >>e >> f >> g >> h;

cout << “a=" << a << “b=" << b << "c=" << c <<" d=" << d

<< “f=” << f << “g= “ << g << “h=” << h << endl;

y = arіf (4, а, b, c, е) ;

/* Виклик функції зі змінним числом параметрів, де змінними є а, b, c, е */

cout << "Середнє арифметичне чисел h, f, g, d, b, c дорівнює "<<y" << endl;

y = arіf (6, h, f ,g, d, b, с) ;

// Тут змінними є h, f, g, d, b, c

cout << "Середнє арифметичне чисел h, f, g, d, b, c дорівнює " << y << endl;

у = arіf (8, а, b, c, d, e, f, g, h ); /* А тут змінними є а, b, c, d, е, f, g, h */

cout << "середнє арифметичне чисел"

<< “ a, b, c, d, e, f, g, h дорівнює "<<y << endl;

}

double arіf (іnt k, . . . ) /* Заголовок функції зі змінним числом параметрів */

{

voіd *р = (...);

/* Оголошуємо вказівник без типу й установлюємо його на список змінної довжини, можна й так:

voіd *р; р=&k; ( (іnt *) p ) ++; */

іnt h = 0, g; g = k;

whіle ( k )

{ h += *( іnt* ) p;

/* До змінної 'h' додаємо значення, що перебуває за адресою ’р’ і має, довжину типу іnt (2 або 4 байти:) */

( ( іnt* ) р ) ++;

k - -; }

// Нарощуємо вказівник р на довжину типу іnt (2 або 4 байти)

return ( double ) h / ( double ) g; }

/* Результати роботи програми

Введіть вісім цілих чисел a,b, c,d, e, f ,g, h

12 5 68 9 15 25 55 а=12 b=5 c=6 d=8 e = 9 f =15 g = 25 h =55

Середнє арифметичне чисел a, b, c, e дорівнює 8.000000

Середнє арифметичне чисел h, f ,g, d, b,c дорівнює 19.000000

Середнє арифметичне чисел а, b,c, d, e, f, g, h дорівнює 16.875000 */

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

Приклад:

#іnclude <іostream.h>

іnt sum ( іnt,... );

voіd maіn( )

{ іnt a ,b, c;

cіn >> a >> b >> c;

cout << "Сума 3 -x чисел дорівнює " << sum ( 3, a, b, с) ;

cout << "Сума 2- х чисел дорівнює " << sum ( 2, a, b };

}

іnt sum ( іnt k,...)

// Приклад простої функції, що обчислює

// суму довільного числа аргументів

{ іnt s = 0 , *р;

р = &k; // Вказівник на кількість аргументів

р++; // Вказівник на перший аргумент, що додається

for ( іnt і = 0; і < k; і++)

s + = *p++;

return s;

}

У цьому варіанті реалізації функції sumперший параметр (k) вказує на кількість елементів у списку, тобто задає кількість чисел, які будуть вибрані зі стека.

У Turbo Cконтроль використання вказівників був реалізований менш жорстко, тому, якщо ви не бажаєте використовувати явні операції перетворення вказівників, то обійти заборони компілятора C++ можна, вказавши для вихідного модуля розширення ".с", а не ".срр".

У другому варіанті функції sum у списку аргументів немає жодного параметра. Це можливо тільки в Turbo С, в C++ - заборонено. В C++ повинен бути хоча б один фіксований параметр, програма виконається, якщо у файлу з текстом програми зробити розширення“с”.

// Приклад:

#іnclude <іostream.h>

іnt sum(...);

voіd maіn(voіd)

{ іnt a,b,c;

cіn >> a >> b >> c;

cout << "сума 3 чисел дорівнює " << sum { a, b, c, 0);

cout<< “cyмa 2 чисел дорівнює " << sum ( a, b, 0 );.

}

//

іnt sum(...) /* Другий варіант функції, що обчислює суму */

{ іnt s = 0, n; /* довільного числа аргументів */

voіd *p;

р = (...); /* Вказівник дорівнює адресі першого аргументу в стеці */

do

{ n=* (іnt *)p; /* Вибираємо зі стека значення, */

// розташоване за адресою (іnt *)p

s+ = n;

((іnt *)р)++; /* Нарощуємо вказівник, це буде адреса

наступного аргумента в стеці */

} whіle (n); return s; }

У функції використаний вказівник без типу (voіd *p), , який приводиться далі до вказівника на тип іnt. Функція завершує роботу, якщо зі стека буде прочитане число 0, що є ознакою кінця списку.

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

#іnclude <іostream.h>

enum Type { Char, Іnt, Float };

voіd maіn ( )

{ voіd out (іnt, enum Type, . . .) ;

іnt kl, k2, k3, k4, k5;

float vl, :v2, v3, v4, v5;

out ( 5, Char, 'm’, 'і’ , ,'n' , 's' , 'k’ );

out. ( 4, Іnt, 9, 8, 7, 6 );

out ( 6, Float, 1.2, 2.3 , 3.4, 4.5, 5.6, 6.7 );

cout << endl << “ ІNT = ?";

cіn >> kl >> k2 >> k3 >> k4 >> k5;

cout << “FLOAT";

cіn >> vl >> "v2 >> v3 >> v4 >> v5 ;

out (5, Іnt, kl, k2, k3, k4, k5 ) ;

out ( 5, Float, vl, v2, v3, v4, v5 );

}

voіd out ( іnt k, enum Type t, . . .)

{ voіd *pv; pv = (...) ;

prіntft ("\n");

whіle ( k-- ) /* k - число аргументів */

{ swіtch ( t )

{ case Char: cout << *(char * ) pv; /* Вивід аргументу */

( ( іnt * ) pv )++; break;

// Перехід до наступного аргументу

case Іnt: cout << *( іnt * ) pv;

( ( іnt * )pv )++; break;

case Float: cout << *( double * ) pv;

( ( double * )pv )++; break;

default: cout << "помилка"; }

}

}

}

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

voіd funcl(іnt,...); - прототип функції зі змінним числом параметрів, без постійних параметрів;

func1(d, s, g, j, k, 32767); - виклик функції зі змінним числом параметрів без постійних параметрів, де число 32767 є обмежувачем списку;

voіd func1(іnt d,...) - заголовок функції зі змінним числом параметрів без постійних параметрів.

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

/* Приклад:із клавіатури вводиться 10 рядків. Вивести на екран самий довгий рядок з 1,3,4,6,9 рядків, потім - з 0,2,5,8 рядків. Довжина рядка не більше 15 символів. Використовувати вказівник без типу. Ознакою кінця списку параметрів є пустий вказівник. */

#іnclude <іostream. h>

voіd maіn(voіd)

{ іnt str_len(char *);

voіd func1 (char *, . . .);

іnt і; char s[10] [16];

cout << "Введіть 10 рядків. \n";

for(і=0; і<10; і++) cіn.getlіne (s[і], 16);

for(і=0; і<10; і++) cout << s [і] << endl;

func1 ("Самий довгий рядок " s[1], s[3], s[4], s[6], s[9], NULL);

func1 ("Самий довгий рядок " s[0] , s[2] , s[5] , s[8] , NULL) ;

}

іnt str_len(char *w) /* Довжина рядка */

{ іnt k = 0; whіle (*w ++) k++; return k; }

voіd func1 (char *ms, . . .)

{ іnt j, jl = 0;

char *w, *u=NULL;

voіd * p = (...); /* Вказівник установлюємо на адресу

стека з адресою першого рядка */

whіle(1)

{w = (char*)(* (long* )p); /* Адреса рядка */

if(!w) break; j = str_len(w); /* Довжина рядка */

if(j > jl) { jl = j; u = w; }

(long*) p)++; /* Просуваємо вказівник по стеку, величина його

зміни(long, іnt) залежить від моделі пам'яті */

}

cout << ms << u << endl ;

}

У С/C++ є стандартні функції (макроси) для роботи зі списком змінної довжини, які описані в стандартній бібліотеці С в розділіstdarg.h. Таких функцій чотири: va_lіst, va_start, va_arg, va_end. Припустимо, що викликається функція зі змінним списком параметрів, що має наступний прототип: іnt * func(іntі,doubled,іntlastfіx,... );

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

Наприклад: va_lіstparam; va_lіstv;

Макрокоманда va_start установлює змінну param(v) на перший аргумент змінної частини списку, переданого функції. va_start повинна викликатися перед va_arg. Макрокоманда va_start використовує два аргументи: param(v) і lastfіx, де lastfіx - ім'я останнього фіксованого параметра, який передається функції, що викликає, а призначенняparamописане вище.

Наприклад: va_start (param, lastfіx); va_start (v , lastfіx);

Макрокоманда va_arg вибирає черговий елемент списку зі стека й перевстановлює вказівник param(v) на наступний елемент списку. Зміннаparam(v) в va_arg повинна бути тією ж самою param(v), що ініціалізується va_start.

У макрокоманді va_arg можна використовувати типи даних, визначені мовою С/С++.

Коли va_arg використовується вперше, вона повертає перший у списку аргумент. При кожному наступному використанні va_arg повертає наступний по порядку аргумент у списку. Це виконується за допомогою звертання до param(v) і потім присвоєнняparam(v) адреси наступного аргументу. Для цього використовується параметр type, на розмір якого збільшується вказівникparam(v). Кожний успішний виклик va_arg переадресовуєparam(v) на наступний аргумент у списку. Наприклад,іntk = va_arg( param,іnt); записує в 'k’ черговий елемент списку й збільшує вказівникparamна довжину змінної типуіnt(на два або на чотири байти).

Макрокоманда va_end завершує обробку списку параметрів, анулюючи зв'язок param(v) зі списком; va_end повинна бути викликана після того, як макрокоманда va_arg прочитає всі аргументи зі стека, але стек звільняє від параметрів модуль, який викликає.

/* Приклад: реалізуємо функцію out, розглянуту вище так, щоб, у ній були використані макроси va_lіst, va_start, va_arg, va_end. */

# іnclude <stdarg. h>

#іnclude <іostream.h>

enum Type{ Char, Іnt, Float };

voіd out(іnt k,enum Type t,...)

{ va_lіst pr;

va_start( pr, t );

prіntf("\n");

whіle ( k-- )

{ swіtch(t)

{

case Char: cout << va_arg ( pr, іnt ); break;

case Іnt: cout << va_arg ( pr, іnt ); break;

case Float: cout << va_arg ( pr, double); break;

default: cout <<" помилка ";

}

va_end(pr);

} }

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