Бочни ефекти функције

Нагласили смо да функције могу да врате само једну вредност наредбом return. Како сада решити проблем ако у функцији треба да израчунамо више вредности и њих предамо главном програму? У претходном поглављу смо нешто рекли о преносу параметара по референци. Такође смо споменули да се могу користити и показивачи на податке. Резултат је сличан – промене се дешавају над стварним параметрима на које показују показивачи дефинисани у функцији.

Дакле, основна идеја јесте да се у функцију проследи адреса стварне променљиве. Тако ћемо на индиректан начин, користећи знање које смо раније стекли о показивачима, приступити стварним параметрима и мењати њихове вредности. Функције које стварају бочне ефекте су углавном типа void, али не морају бити. У нашим наредним примерима су типа void.

Ово ћемо најлакше схватити ако анализирамо следеће задатке:

Креирати функцију којом се за задате вредности отпора R и струје I рачунају вредности напона U и снаге P на отпорнику.

Из задатка видимо да треба да израчунамо две вредности користећи познате улазне параметре. Користићемо познате формуле за:

  • Омов закон: \(U=R*I\) и

  • Закон снаге: \(P=R*I^2\).

Како функција napon_snaga резултате даје преко бочних ефеката, за тип функције користимо void.

#include<stdio.h>
void napon_snaga(float R, float I, float *pU, float *pP)
{
    *pU = R * I;
    *pP = R * I * I;
}

main()
{
    float R, I, U, P;
    printf("Unesi vrednost otpora R[Om] = ");
    scanf("%f", &R);
    printf("Unesi vrednost struje I[A] = ");
    scanf("%f", &I);
    napon_snaga(R, I, &U, &P);
    printf("Vrednost napona U[V] = %.2f\n", U);
    printf("Vrednost snage P[W] = %.2f\n", P);
}

Резултат извршавања програма:

Unesi vrednost otpora R[Om] = 23.55
Unesi vrednost struje I[A] = 17.36
Vrednost napona U[V] = 408.83
Vrednost snage P[W] = 7097.25

А сада да детаљно погледамо:

Како функција треба да дâ два резултата, користићемо бочне ефекте. Функција има четири параметра.

Улазни параметри R и I се у функцију преносе помоћу вредности.

Параметри *pU и *pP су показивачи у које смештамо адресе променљивих U и P. Префикс p користимо јер су у питању показивачке променљиве. Променљиве U и P су дефинисане у главном програму, тако да добијене вредности напона и снаге смештамо у њих.

Пренос параметара у функцију napon_snaga:

                           main()      napon_snaga(float R, float I, float *pU, float *pP)
../_images/Picture141.png

У самој функцији се рачунају вредности напона и снаге изразима:

*pU = R * I;
*pP = R * I * I;

Да се подсетимо значења ових израза.

Резултат израза се смешта у меморијску локацију на коју показивачка променљива указује.

У нашем случају показивач pU указује на стварни параметар U у главном програму јер садржи његову адресу.

Показивач pP указује на параметар P. У ова два параметра ће бити смештени резултати израза.

Вредности параметара U и P представљају бочне ефекте функције napon_snaga:

                           main()      napon_snaga(float R, float I, float *pU, float *pP)
../_images/Picture151.png

Креирати функцију max_min која проналази највећи и најмањи од три броја. У главном програму за три унета броја исписати производ највећег и најмањег.

void max_min(int a, int b, int c, int *max, int *min)
{
    *max = a;
    *min = a;
    if (b > *max) 
        *max = b;
    if (b < *min) 
        *min = b;
    if (c > *max) 
        *max = c;
    if (c < *min) 
        *min = c;
}

int main(void)
{
    int a, b, c;
    int max, min;
    scanf("%d%d%d", &a, &b, &c);
    max_min(a, b, c, &max, &min);
    printf("Proizvod max i min je: %d", max * min);
    return 0;	
}

Резултат извршавања програма:

20
7
12
Proizvod max i min je: 140

Пренос параметара у функцију max_min:

                          main()        max_min(int a, int, b, int c, int *max, int *min)
../_images/Picture161.png

У првом разреду си решавао задатке у којима се пореде три броја. Један од начина је да се на почетку претпостави да је први број и највећи-max и најмањи-min. Остатак програма је поређење тренутне вредности са max и min. Управо је та идеја искоришћена у овом задатку.

На почетку извршавања функције наредбама *max = a и *min = a вредности променљивих на које указују показивачи *max и *min добијају вредност прве променљиве a. Шта смо заправо урадили? На посредан начин смо приступили стварним параметрима max и min и доделили им вредност.

max=a, min=a;

Вредности променљивих после наредбе *max = a и *min = a:

                          main()        max_min(int a, int, b, int c, int *max, int *min)
../_images/Picture171.png

У наставку извршавања функције max_min, после наредбе if (b > *max), односно if (7 > 20), вредности се неће мењати.

Међутим, услов if (b < *min), односно if (7 < 20) је испуњен, па ће се извршити наредба *min = b;.

Вредности променљивих после наредбе *min = b:

                          main()        max_min(int a, int, b, int c, int *max, int *min)
../_images/Picture181.png

Последња два услова c > *max и c < *min нису испуњена – наредбе се неће извршити, вредност променљивих се неће мењати.

Вредности променљивих на крају извршења програма:

                          main()        max_min(int a, int, b, int c, int *max, int *min)
../_images/Picture191.png

Покушај да исти задатак урадиш са измењеним вредностима, нпр. а = 17, b = 12, с = 5.

Анализирај детаљно вредности променљивих током извршавања програма.

Обрати пажњу! Функција scanf је пример функције која даје бочне ефекте. Зато су параметри у њој адресе променљивих у које се смештају учитане вредности.

Погледајмо кôд:

   int a, b;
   scanf("%d%d", &a, &b);

У првом реду резервисали смо меморијске локације за променљиве а и b. У наредби scanf навели смо адресе ових променљивих у које смештамо унете вредности.

Вежбање:

Дата је функција void funkcija(int k, int* n).

Који од следећих позива функције су нетачни и зашто? Целобројне променљиве k, m и n су декларисане у главном програму.

funkcija(&k, &n);

funkcija(k, n);

funkcija(k, m, n);

funkcija(2 * m + 1, &n);

  • Позив функције под 1 је неисправан, јер се као први параметар преноси адреса, а не вредност.

  • Позив функције под 2 је неисправан, јер се као други параметар преноси вредност, а не адреса.

  • Позив функције под 3 је неисправан, јер се при позиву функције наводи један аргумент више.

  • Једини тачан одговор је под 4.

Шта се исписује по извршењу програма за наведене улазне вредности?

#include<stdio.h>
void funkcija(int* n)
{
    *n += 5;// *n = *n + 5;
}
int main(void)
{
    int m, n;
    scanf("%d", &n);
    funkcija(&n);
    printf("n = %d", n);
	return 0;
}

За n = -13 излаз је |blank|.

За n = 5 излаз је |blank|.

За n = 21 излаз је |blank|.

Шта се исписује по извршењу програма за наведене улазне вредности?

#include<stdio.h>
void funkcija(int* n)
{
    *n++;
}

int main(void)
{
    int m, n;
    scanf("%d", &n);
    funkcija(&n);
    printf("n = %d", n);
	return 0;
}

За n = 21 излаз је |blank|.

За n = -29 излаз је |blank|.

За n = 0 излаз је |blank|.

Излаз је исти као улазна вредност, јер се у функцији вредност показивача увећава за један, а не вредност параметра n. Овде мораш бити обазрив – сада показивач у функцији показује на недозвољени део меморије.

Шта се исписује по извршењу програма за наведене улазне вредности?

#include<stdio.h>
void funkcija(int* n)
{
    (*n)++;
}
int main(void)
{
    int m, n;
    scanf("%d", &n);
    funkcija(&n);
    printf("n = %d", n);
	return 0;
}

За n = 51 излаз је |blank|.

За n = -29 излаз је |blank|.

За n = 0 излаз је |blank|.

С обзиром да заграда има већи приоритет, наредбом *n се прво приступа променљивој n, а затим се њена вредност увећа за 1.

  1. Дата је функција

void funkcija(int x, int *y, int *z)
{
    x = *y + *z;
    (*z)--;
    (*y)--;
}

У главном програму су декларисане променљиве:

int a = 5, b = 10, c = 15

Које вредности ће имати променљиве a, b, c после позива функције у главном програму funkcija(a, &b, &c)? |blank|,|blank|,|blank|.