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

Зубенко, Омельчук - Програмування. Поглиблений курс

.pdf
Скачиваний:
58
Добавлен:
07.03.2016
Размер:
5 Мб
Скачать
☆

Розділ ІІІ. МОВИ ПРОГРАМУВАННЯ С ТА С++

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

Приклад 3.51. Покажчики функцій як результат функції.

Нехай є два варіанти дій, що задаються функціями f1 та f2 із прототипами

int f1(void); int f2(void);

Функція для реалізації меню має прототип:

int (*menu(void))(void);

#include <stdio.h>

int f1(void); int f2(void);

int (*menu(void))(void);

int main(void)

{

int (*r) (void); /*опис покажчика на цілі функції з порожнім списком параметрів, такими функціями є f1 та f2*/

while (1)

{

r=menu(); /*виклик меню*/ if (!r) {

printf("\n The End"); return 0;

}

printf("\t%d", r()); return 0;

}

}

int f1(void)

{

return 1;

}

361

ПРОГРАМУВАННЯ

int f2(void)

{

return 2;

}

/*результатом роботи функції menu є одна з функцій f1, f2 або покажчик NULL*/

int (*menu(void))(void)

{

int choice;

printf("Pick the menu item (1 or 2), pls:"); scanf("%d",&choice);

switch (choice)

{

case 1: return f1; case 2: return f2; default: return NULL;

}

}

■

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

Приклад 3.52. Масив покажчиків функцій:

#include <stdio.h> int f1(void);

int f2(void); int f3(void);

/*Ініціалізований масив покажчиків на функції*/ int (*menu_items[3])(void)=

{

f1,

f2,

f3

}; /*ініціалізація масиву покажчиків на функції*/

int (*menu(void))(void); int main(void)

{

int (*r) (void); /*опис покажчика на цілі функції з порожнім списком параметрів, такими функціями можуть бути f1, f2 та f3*/

while (1)

{

r=menu(); /*виклик меню*/

362

Розділ ІІІ. МОВИ ПРОГРАМУВАННЯ С ТА С++

if (!r) {

printf("\n The End"); return 0;

}

printf("\t%d", r()); return 0;

}

}

int f1(void)

{

return 1;

}

int f2(void)

{

return 2;

}

int f3(void) {return 3;

}

/*результатом роботи цієї функції є функція*/ int (*menu(void))(void)

{

int choice;

printf("Pick the menu item (1, 2 or 3), pls:"); scanf("%d",&choice); if((choice<3)&&(choice>=0))

return menu_items[choice]; else return NULL;

}

■

3.6.5. ФУНКЦІЯ main() ІЗ ПАРАМЕТРАМИ

Функція main може мати параметри – символьні рядки, що форму- ються в командному рядку запуску програми. Для доступу до них у функції maіn() використовуються два параметри: перший – для озна- чення кількості переданих рядків, другий – самих рядків. Загально- прийняті (але не обов'язкові) імена цих параметрів – argc та argv. За- головок функції main із параметрами має вигляд

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

363

ПРОГРАМУВАННЯ

Параметр argc має тип іnt, його значення формується автоматично в процесі аналізу командного рядка й дорівнює кількості символьних ряд- ків у ньому, починаючи з імені самої програми. Рядки розділяються про- білом. Якщо символьний рядок містить символ-пробіл, то його подають у вигляді літерала. Параметр argv – це масив символьних покажчиків на рядки, його довжина збігається з argc. Функція maіn() може мати й тре- тій параметр argp, який служить для передавання їй параметрів ОС.

Приклад 3.53. Програма, що друкує значення фактичних параме- трів функції main():

#include <stdio.h>

int main(int argc, char *argv[], char * argp[]) /*

argc – кількість параметрів argv – вектор параметрів-рядків

argp – вектор змінних середовища*/

{

int i; char *p[];

/*друкування значень параметрів*/ for(i=0; i<argc; i++)

printf("arg %i:%s\n", i, argv[i]);

/*друкування значень змінних середовища*/ for(p=argp; *p !=(char*)0; p++)

printf("%s\n", *p);

return 0;

}

■

3.6.6. ФУНКЦІЇ Й ПОКАЖЧИКИ ФУНКЦІЙ ЯК ПАРАМЕТРИ ФУНКЦІЙ

Функції й покажчики функцій можуть використовуватись як па- раметри інших функцій (функції f10 та f11 із прикл. 3.47). Розгля- немо кілька прикладів детальніше.

Приклад 3.54. Обчислити наближене значення інтеграла 1∫ e−x2dx :

−1

Лістинг.

364

Розділ ІІІ. МОВИ ПРОГРАМУВАННЯ С ТА С++

#include <math.h> #include <stdio.h>

/*Прототип функції для обчислення інтеграла методом прямокутників*/

/*Покажчик функцій f використовується як параметр функції*/ double integral(double (*f)(double), double a, double b, int n);

/*Опис фактичного параметра – підінтегральної функціїекспоненти. Її тип має бути узгодженим із прототипом параметра f

функції integral*/ double f1(double x)

{

return exp(-x*x);

}

int main(void)

{

/*Покажчик f1 використовується як перший аргумент виклику функ-

ції integral*/

printf("\n integral=%f", integral(f1,-1.0,1.0,100)); return 0;

}

double integral(double (*f)(double), double a, double b, int n)

{

int i; double h, s;

for (i=0, h=(b-a)/n, s=0; i<n; i++) s+=f(a+i*h);

return s*h;

}

■

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

<stdlib.h>:

void qsort(void *base, size_t nelem, size_t width, int (*fcmp)(const void *p1, const void *p2));

Розглянемо її параметри:

base – покажчик на початок таблиці (масиву) елементів, що сорту- ються (адреса нульового елемента масиву);

365

ПРОГРАМУВАННЯ

nelem – кількість елементів, що сортуються, у таблиці (ціла величи- на, не більша за розмір масиву) – сортуються перші nelem елементів від початку масиву;

width – розмір одного елемента таблиці (масиву) у байтах;

fcmp – покажчик на функцію порівняння, яка отримує як парамет- ри два покажчики p1, p2 на елементи таблиці й повертає залежно від результату порівняння ціле число:

якщо *p1<*p2 – то від'ємне ціле;

якщо *p1==*p2 – то 0;

якщо *p1>*p2 – то додатне ціле.

Алгоритм роботи цієї функції буде розглянуто в підрозд. 4.1.6. Застосуємо функцію qsort для лексикографічного впорядкування

масиву покажчиків на рядки. Зауважимо, що рядки в процесі сорту- вання не міняють своїх положень, змінюються лише значення покаж- чиків у масиві.

Приклад 3.55. Лексикографічне впорядкування рядків:

#include <stdio.h>

#include <stdlib.h> /*для функції qsort()*/

#include <string.h> /*для порівняння рядків strcmp()*/

/*функція для порівняння*/

int comparePC(const void *a, const void *b);

/*друкування таблиці p розміром n*/ void printPC(int n, char *p[]);

int main(void)

{

char *pc[]={ "One", "Two", "Three", "Four", "Five", "Six", "Seven", "Eight", "Nine", "Ten"

};

/*обчислення розміру таблиці*/ int n=sizeof(pc)/sizeof(pc[0]);

366

Розділ ІІІ. МОВИ ПРОГРАМУВАННЯ С ТА С++

printf("\n До сортування:"); printPC(n, pc);

/*виклик функції qsort()*/

qsort((void *)pc, n, sizeof(pc[0]), comparePC /*покажчик на функцію порівняння*/);

printf("\n\n Після сортування:"); printPC(n, pc);

int i; scanf("%d", &i); return 0;

}

/*функція для порівняння*/

int comparePC(const void *a, const void *b)

{

unsigned long *pa=(unsigned long *)a, *pb=(unsigned long *)b;

return strcmp((char *)*pa, (char *)*pb);

}

/*друкування таблиці p розміром n*/ void printPC(int n, char *p[])

{

for(int i=0; i<n; i++)

printf("\np[%d]: point: %p\tvalue: %s", i, p[i], p[i]);

}

Результат виконання програми:

До сортування:

p[0]: point: 0042B0C8value: One p[1]: point: 0042B0D0value: Two p[2]: point: 0042B708value: Three p[3]: point: 0042B734value: Four p[4]: point: 0042B728value: Five p[5]: point: 0042B71Cvalue: Six p[6]: point: 0042B2E4value: Seven p[7]: point: 0042B48Cvalue: Eight p[8]: point: 0042B3A8value: Nine p[9]: point: 0042BB54value: Ten

Після сортування:

p[0]: point: 0042B48Cvalue: Eight p[1]: point: 0042B728value: Five

367

ПРОГРАМУВАННЯ

p[2]: point: 0042B734value: Four p[3]: point: 0042B3A8value: Nine p[4]: point: 0042B0C8value: One p[5]: point: 0042B2E4value: Seven p[6]: point: 0042B71Cvalue: Six p[7]: point: 0042BB54value: Ten p[8]: point: 0042B708value: Three p[9]: point: 0042B0D0value: Two

■

3.6.7. ФУНКЦІЇ ЗІ ЗМІННОЮ КІЛЬКІСТЮ ПАРАМЕТРІВ

Прототип функції зі змінним списком параметрів має вигляд

T f(<список-явних-параметрів>, …);

Кількість (не менше одного) і тип явних параметрів фіксовані й ві- домі до компіляції. З погляду композиційної семантики функції зі змінною кількістю параметрів є еквітонними параметризованими

X− арними функціями ( X −Y -операторами) (див. підрозд. 2.1.2). Існує кілька механізмів для визначення кількості й типів неявних

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

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

Приклад 3.56. Змінні списки параметрів функцій:

1) /*підрахунок суми параметрів функції*/

long suma(int n,…)

{

int *p=&n; /*адреса списку параметрів*/ long total=0;

for (; n;n--) total+=*(++p); /*++p – перехід до наступного параметра*/

return total;

}

int main (void)

{

printf("\n %ld", suma(2,4,6));

368

Розділ ІІІ. МОВИ ПРОГРАМУВАННЯ С ТА С++

return 0;

}

2) /*підрахунок добутку параметрів функції, список яких закінчується 0.0*/

double prod (double arg,…)

{

double prod1=1.0;

double *p=&arg; /*адреса списку параметрів*/ long total=0;

if (*p==0) return 0.0; for (; *p; p++) prod1*=*p; return prod1;

}

int main (void)

{

printf("\n %f", prod(1.1, 2.0, 3.3, 5.1, 0.0)); return 0;

}

■

У загальному випадку для забезпечення прямого доступу до неяв- них параметрів функції зі змінною кількістю параметрів різного типу й мобільності програм використовують спеціальні макроси, задекла- ровані в заголовних файлах <varargs.h> і <stdarg.h>. Основні з них –

void va_start (va_list parg, last); type va_arg(va_list parg, type); void va_end (va_list parg);

– містяться у <stdarg.h>.

Порядок роботи з ними такий. У функції зі змінною кількістю па- раметрів описується спеціальний покажчик ap із типом va_list, який використовується для звертання до параметрів. Викликом мак- росу va_start (ap, last); де last – ім'я останнього явного параметра в заголовку функції, покажчик ap установлюється на перший неяв- ний параметр. Для послідовного звертання до наступних неявних па- раметрів застосовується виклик макросу va_arg(ap, type);, що по- вертає значення поточного параметра типу type, при цьому ap ав- томатично пересувається до наступного параметра. Тип type має бути або одним зі стандартних (int, double тощо), або ім'ям типу, визначеним оператором typedef. Як і в попередньому випадку, необ- хідно передбачити спосіб обмеження кількості параметрів. Коли всі

369

ПРОГРАМУВАННЯ

параметри опрацьовані, перед виходом із функції викликають макрос va_end (ap); для обнулення змінної ap та інших змінних макросів.

Розглянемо приклад організації доступу до списку неявних параметрів певної функції void f(char *s,…) за допомогою спеціального форматного символьного рядка s, елементи якого відповідають першим латинським літерам у назві типу кожного з неявних параметрів. Аналогічно працю- ють і деякі стандартні функції – наприклад printf () та scanf().

Нехай типами неявних параметрів функції f є _Bool, сhar,double та long. Якщо список фактичних неявних параметрів у виклику f склада- ється, наприклад, з п'яти елементів та їхні типи – сhar, double, _Bool, long та long, то такому списку відповідає форматний рядок "cdBll":

#include <stdarg.h> void f(const char *s,…)

{_Bool bx; char cx; double dx; long lx;

va_list ap; /*покажчик на поточний неявний параметр*/ char *ss=s; /*покажчик для визначення типа параметра*/ va_start (ap,s); /*ініціалізація ap*/

while(*ss){

switch (*ss++){ /*обробка поточного параметра*/ case 'B':bx= va_arg(ap, _Bool);…;break; case 'c':cx= va_arg(ap, сhar);… ;break; case 'd':dx= va_arg(ap, double);… ;break;

case '1':lx= va_arg(ap, long);… ;break;

}

}

va_end (ap);

}

int main()

{double d1=1/3,d2=2e-3; char c1='a';

f("ddc" ,d1,d2,c1); /*обробка трьох параметрів*/ f("lcdcd",0L,'0',d2,c1,5.0); /*обробка п'ятьох параметрів*/ return 0;

}

3.6.8. ВБУДОВАНІ ФУНКЦІЇ

Вбудовані функції вперше з'явилися в С99. Ознакою їх є службове сло- во inline у заголовку функції. Саме це слово походить від оптимізаційно- го методу внутрішньорядкового кодування (англ. – inline expansion), що застосовується на етапі генерації коду, коли виклик функції замінюється

370