SlideShare a Scribd company logo
Бублик Володимир Васильович Програмування - 2 Лекція 8. Процедурне програмування. Функції  Лекції для студентів 2 курсу
1. Визначення та виклик функції
Визначення функції //Greatest Common Divider // заголовок int  gcd (int m, int n) //тіло функції { while  (m != n) if  ( m>n )  m=m-n;  else  n=n-m; // m == n //результат return  m; }
Виклик функції int  a, b, c; cin>>a>>b; c =  gcd (a, b); // 1. Ініціалізація параметрів // int m=a; int n=b; //2. Виконання тіла фукції //3. Обчислення результату  (c=m;) Звичайно виклик функції полягає в  припиненні  нормального ходу виконання об'єктного коду, що містить виклик. Відбувається  ініціалізація  і починає виконуватись об'єктний код функції. Після повернення результату виконання основного коду відновлюється
Способи виконання функцій Закрита функція Викликається командою переходу з поверненням Відкрита (вбудована) функція Підставляється в кожне місце виклику В обох випадках параметри і результат передаються в один і той же спосіб
Вбудовані функції Об'єктний код  закритої  функції присутній в програмі  один  раз. Відповідні команди керування приводять до  її активації,  виконання коду і наступного  повернення  в код виклику.   Це  накладні витрати виклику . Їх можна зменшити, якщо  розміщувати код функції безпосередньо в місці виклику. Накладними витратами в цьому разі стане  дублювання коду  в кожному місці виклику. Компілятор сам приймає рішення, як реалізувати функцію ( inline  лише порада). inline double  max( double  x,  double  y) {  return  (x>y)?x:y; }
Приблизна схема викликів z= max (5, 6); z= max(max(1,2),3); double  x = 5; double  y = 6; z =  (x>y)?x:y;   double  x = 1; double  y = 2; double  x1 =  (x>y)?x:y ; double  y1 = 3; z =  (x1>y1)?x1:y1 ;
Макровизначення Макровизначення на перший погляд дуже схожі на вбудовані функції, вони теж підставляються в місця виклику, але відрізняються від вбудованих функцій способом передачі параметрів текстовою підстановкою. Приклад макровизначення # define  MAX(x,y) ((x)>(y))?(x):(y) Приклади макровикликів MAX(1,2); MAX(a+ b ,  c-d );
Приклади макропідстановок double  a = MAX(1,2); //((1)>(2))?(1):(2) cout<<a<<endl; //2 double  b=2; a= MAX(++a,b); //  (( ++ a )>(b))?( ++ a ):(b) or // (3>2)?4:2) == 4   cout<<a<<endl; // 4 // The next expression is  not correct // because of  priorities cout<<MAX(++a,b)<<endl;   //means // cout<< (5>2)?6:4 <<endl or //  (cout<<(5>2))?6:(4<<endl )  ERROR
Виклик функції inline double  max( double  x,  double  y) {  return  (x>y)?x:y; } double a =  max (1,2); // double x=1; double y=2;  // ініціалізація параметрів // return (1>2)?1:2 == 2  виконання тіла і повернення cout<<a<<endl; //2 double  b=2; a=  max (++a,b); // double x=++a, double y=b; (x==3, y==2) // return (3>2)?3:2 == 3
2. Передача параметрів
Передача параметрів Розглянемо оголошення функції void  f   ( T  x); де  f  ―  ім'я функції,  T ―  тип параметру,  x ―  ім'я параметру Розглянемо виклик цієї функції f(e) ; де  e   ―  вираз підходящого типу (фактичний параметр) Виконання функції починається визначенням локальної змінної для параметру  x  з її одночасною ініціалізацією T x  =  e ;
Передача параметрів значенням Ініціалізація T x  =  e ; говорить про те, що перед виконанням функції обчислюється  значення  фактичного параметру  е  ( r-value) Кажуть, що параметри передаються значенням Залежно від типу  T  ці значення можуть бути  значеннями базових типів, структур, адресами (у випадку указників) відсилками (у випадку псевдонімів)
Сторонній ефект Якщо передаються значення базових типів або структур, то виконання функції не впливає на наступні значення фактичних параметрів Якщо формальний параметр є указником, то можлива зміна значення елементу пам'яті, на який цей указник встановлено Якщо формальний параметр псевдонім, то через нього функція одержує прямий доступ до елементу пам'яті, до якого псевдонім відсилає Якби не було стороннього ефекту, то який сенс мали б функції виду  void  f   (T x) ?
Приклад 1. swap void  swap(  double  x,  double  y) { // значення базового типу  double double  z = x; x=y; y=z; cout<<x<<‘,’<<y<<endl;  //2,1 }   double  a=1, b=2; swap( a,b); //  double x=a, y=b;  cout<<a<<‘,‘<<b<<endl; // 1,2
Приклад  2 .  p swap void   p swap( double   * x,  double   * y) { double   z =  * x;  * x= * y;  * y=z; cout<<*x<<‘,’<<*y<<endl;  //2,1 }   double  a=1, b=2; p swap( & a,  & b); // за коректність відповідає користувач //  double *x= &a, *y= &b;   cout<<a<<‘,‘<<b<<endl; // 2,1
Приклад  3 .  r swap  ( тільки С++) void   r swap(  double   & x,  double   & y) { double   z = x; x=y; y=z; cout<<x<<‘,’<<y<<endl;  // 2,1 }   double  a=1, b=2; r swap( a,b);  // за коректність відповідає розробник //  double  & x=a,  & y=b;  cout<<a<<‘,‘<<b<<endl; // 2, 1
Безпечність виклику Безпечні swap( a,b); r swap( a,b); Небезпечний p swap( &a, &b); // p swap( 0, &b);  ??? void  pswap(double *x, double *y) { assert((x!=0)&&(y!=0)); double  z = *x; *x=*y; *y=z; cout<<*x<<','<<*y<<endl;  }
Масиви як параметри Масиви  ― головна причина використання параметрів-указників Використання параметром масиву нічим не відрізняється від параметра-указника T *a T a[]
Задача додому Що б це значило? Організуйте виклик функції ptrswap та графічно зобразіть роботу з пам'яттю void  ptrswap(  int  *&u,  int  *&v )  {  int  *w = v; v = u; u = w; }
Приклад 4. Не має значення, як визначити параметр в кожній з трьох сигнатур void  init   ( char * ch); void  show   ( char  ch[]); void  swapar   ( char  []);
Приклад 4 (1). const int  n=26; int main () { char  ch[n]; init (ch); show(ch); swapar(ch); show(ch); return  0; }
Приклад 4 ( 2 ) . void  init( char * ch) { for (int i= 'a'; i<'a'+ n ; i++)   // не дуже добре ch[i-'a'] = i; } Ніхто не заборонить навіть таке for (int i= 'a'; i<'a'+ 100000 ; i++) ch[i-'a'] = i; Результат передбачити неможливо, швидше всього  access violation
Приклад 4 ( 3 ) . Допустимий навіть зовсім безглуздий виклик char  ch; init( & ch) ; Теж  access violation
Приклад 4 ’ (1).  Краще рішення   передати розмір масиву додатковим параметром void  init   ( char * ch,  int n ); void  show   ( char  ch[],  int n ); void  swapar   ( char  [],  int );
Приклад 4 ’ (2). int main () { const int n=26; char  ch[n]; init (ch,  n ); show (ch,  n ); swapar (ch,  n ); show (ch,  n ); return  0; }
Приклад 4 ’(2) . void  init   ( char * ch , size_t n ) { for ( int  i= 'a'; i<'a'+ n ; i++)   //тепер в порядку ch [ i-'a' ]  = i; }
Приклад 4 ’(3) . void  show( char  ch[n], size_t   n) { cout<<&quot;start   string:&quot;<<endl; for  ( int  i=0; i< n ; i++) cout<<ch[i]<<' '; cout<<endl; }
Приклад 4 ’(4) . void  swapar( char  ch[], size_t n) { for  ( int  i = 0; i< n /2; i++) pswap(ch +i ,ch+n-1-i); };
Приклад 4 ’( 5 ) . Результат роботи s tart   string : a b c d e f g h i j k l m n o p q r s t u v w x y z  s tart   string : z y x w v u t s r q p o n m l k j i h g f e d c b a
Висновок Передаючи в функцію параметром масив (або указник як масив) ,  передбачаємо розмір масиву додатковим параметром void  f (T *array, size_t size); або краще void  f (T  []  array, size_t size);
“ Загорнуті” масиви // Визначення “загорнутого” вектора struct   WrappedVector { static const   int  n; // статичне поле одне на всіх double  * x; }; // сигнатура добутку (дуже погана) double  prod ( WrappedVector ,  WrappedVector );
Параметри сталі відсилки // сигнатура добутку (краще) // структури не копіюються double  prod ( WrappedVector & ,  WrappedVector & ); // сигнатура добутку (ще краще) // структури не копіюються, // доступ для зміни фактичного // параметру закрито double  prod ( const  WrappedVector & ,   const   WrappedVector & );
Скалярний добуток // визначення статичного поля   структури const int  WrappedVector::n = 100; // визначення скалярного добутку double  prod ( const  WrappedVector& a,   const  WrappedVector& b) { double  s = 0; for  ( int  i=0; i<a.n; i++) s+=a.x[i]*b.x[i]; return  s; }
Створення вектору  ―  конструктор // Відсилка  ―  повний доступ void   construct ( WrappedVector   & ); void  construct (WrappedVector & a) { a.x =  new double  [a.n]; for  ( int  i=0; i<a.n; i++) a.x[i] = rand(); return ; }
Видалення об'єкту  ― д еструктор void  destroy (Wrapped Vector &  a ) { delete  [] a.x; a.x = 0; return ; } // Як примусити кожного прибирати за собою?
Замовчувані значення параметрів T f (T1 x1=a1, T2 x2=a2, …, Tn xn=an); f(); f(c1); f(c1,c2);…. void  createPoint(Point & u,  double  x=0,  double  y=0) { u._x = x; u._y=y; } createPoint (u); // (0,0) createPoint (v, 1); // (1,0) createPoint (w, 1, 2); // (1,2)
Типи параметрів (перший підсумок) const T T T& const T& Копіювання,  незмінний  формальний параметр ,  немає впливу на фактичний параметр Копіювання,  змінний   формальний  параметр, немає впливу на фактичний параметр Відсилка без копіювання,  повний доступ  до фактичного параметру  (C++) Стала відсилка без копіювання,  фактичний   параметр незмінний   (C++)
Порівняння типів параметрів Найнадійніший  параметр-результат T& Параметри-значення Точка зору користувача: параметри  T  ,  const T  і  const T&  не відрізняються з точки зору використання Точка зору розробника: const T  надійніший, бо не створює непорозумінь; const T&  найефективніший, оскільки не вимагає витрат на копіювання
Сталий параметр //Greatest Common Divider int  gcd ( const  int  m 0 ,  const  int  n 0 ) { int  m = m0, n = n0; while  (m != n) if  ( m>n )  m=m-n;  else  n=n-m; // m == n // Початкові значення не втрачено assert((m0%m==0) && (n0%n==0) ); return  m; }
Параметри-указники T*   const T* T* const const T*const Повний але непрямий доступ до фактичного параметру; передача масивів ( T[])  (в стилі чистого С) Маловживаний тип параметру; доступ лише для читання: сталий фактичний параметр ( vs const T&) Незмінний  формальний параметр - указник ,  немає впливу на фактичний параметр-адресу ( this   в  C++) Незмінний указник на  сталий  фактичний параметр В усіх випадках 5-8 сам указник передається значенням
Головна проблема параметра-указника Як відрізнити масив від скалярного значення? Вихід: жорстка дисципліна програмування, додатковий параметр типу  size_t  з відповідним коментарем void  f ( double  * px, size_t  size_of_px );
Типова помилка в параметрі-указнику // Думалось, що ця функція виділятиме пам’ять для // указника  t void  allocate ( char  *t, size_t n,  char  c) { t =  new char  [n]; t[n-1]='\0'; for  (int i=0;i<n-1;i++) t[i]=c; cout<<“This is inside: “<<t<<endl; return ; }
Результат // Виклик функції  allocate   char  a[]=“This was before”; cout<<a<<endl; // This was before allocate (a,  10 , ‘a’);  //  This is inside: aaaaaaaaaa cout<<a<<endl; // This was before // Вихід: передати указник відсилкою void construct ( WrappedVector   & );
Параметри-указники T** T*& . . . . . . . . . . . . . . . . . const T* const * const Указник-вихідний параметр (чистий С) Те ж саме, але в стилі С++ А ще різні варіації на тему  const
Указник другого рівня // Тепер правильно   void  allocate ( char  ** t  size_t  n,  char  c) { * t =  new char   [n]; (*t) [n-1]='\0'; for  ( int  i=0;i<n-1;i++) (*t) [i]=c; cout<< *t <<endl; return ; }
Виклик указником другого рівня // Виклик функції  allocate  char   * a=“This was before”; cout<<a<<endl; // This was before allocate ( & a,  10 , ‘a’);  //  This is inside: aaaaaaaaaa cout<<a<<endl; //  aaaaaaaaaa
Псевдонім указника void  allocate ( char  * & t,  size_t  n,  char  c) { t =  new char   [n]; t[n-1]='\0'; for  ( int  i=0;i<n-1;i++) t[i]=c; cout<<t<<endl; return ; }
Виклик відсилкою до указника // Виклик функції  allocate   char   * a=“This was before”; cout<<a<<endl; // This was before allocate (a,  10 , ‘a’);  //  This is inside: aaaaaaaaaa cout<<a<<endl; //  aaaaaaaaaa
Особливості нульового параметру-указника Структура дерева struct  Tree  { int  node; Tree *left; Tree *right; }; Вузли-листя мають нульові указники  left  і  right
Вершина дерева Параметр  aTree  відіграє роль параметру-результату void  createTree (Tree  ** aTree,  int  node, Tree *left, Tree *right) { *aTree =  new  Tree; (*aTree) -> node = node; (*aTree) -> left = left; (*aTree) -> right = right; return ; }
Видалення дерева void  destroyTree(Tree **aTree) { Tree *subTree; if  ((*aTree)==0)  return ; subTree = (*aTree)->left;   //left subtree destroyTree(&subTree); subTree = (*aTree)->right; //right subtree destroyTree(&subTree); delete  *aTree; //delete root *aTree = 0; return ; }
Приклад використання int main () { Tree * t, * t1, *t2 ; createTree(&t1,1,0,0); createTree(&t2,2,0,0); createTree(&t,3,t1,t2); destroyTree (&t); // Проблема: значення t1 і t2 return  0; }
Завданння Переписати обробку дерев, замінивши скрізь, де можна указники відсилками
Указники  vs.  відсилки Замінимо void  createTree (Tree **aTree,  int  node, Tree *left, Tree *right); на void  createTree (Tree * & aTree,  int  node, Tree *left, Tree *right); але не void  createTree (Tree * & aTree,  int  node,  Tree &left, Tree &right ); бо виклик  createTree(t1,1,0,0); стане незаконним
Зведення типів   при передачі параметрів void  show ( doubl e  x) { cout<<x<<endl; } // OK int  k=1; show(k); //1 long int  lk=2; show(lk); //2 float  x=3; show(x); //3
Неявне зведення відсилок   неможливе void  show ( double   &  x) { cout<<x<<endl; } // ERRORS int  k=1; show(k); long int  lk=2; show(lk);  float  x=3; show(x);
Явне зведення відсилок   хибне void  show ( double   &  x) { cout<<x<<endl; } // Type conversion int  k=1; show(( double &) k);   //-9.25596e+061 long int  lk=2; show(( double &)  lk);  //2.122e-314 float  x=3; show(( double &)  x);    //4.77656e-314 Не намагайтеся допомогти компілятору: не вийде!
Неявне зведення указників   неможливе теж void  show ( double   *  x) { cout<<x<<endl; } // ERRORS int  k=1; show(&k); long int  lk=2; show(&lk);  float  x=3; show(&x);
3. Обчислення результату
Результат функції Результати значення T f(…); const  T f(…); Результати відсилки T& f(…); const  T& f(…); Результати указники T* f(…):
Результати-значення Типи результатів  T  і  const  T  не різняться для типів даних, прийнятих в С ( далі в С++ будуть відрізнятися )
Точка площини struct  Point {  double _x; double _y; }; const   Point  plus (Point u, Point v) {  Point res; res._x = u._x+v._x; res._y = u._y+v._y; return  res; }
Дерево Як і в попередньому прикладі результат повністю копіюється при виході з функції Tree createTree (  int  node, Tree * left, Tree * right) { Tree aTree; aTree.node = node; aTree.left = left; aTree.right = right; return  aTree ; }
Результати-відсилки Дають повний доступ до частин агрегатів даних, але не забувайте про  правильну передачу  параметру double & x (Point u)  {   return  u._x; } Point v; v._x = 10; v._y = 20; cout<<x(v) <<endl; //10 x(v)= 125; cout<< x(v)<<endl; //10 ,   а не  125 ?!
Результати   і параметри-відсилки Тепер повний доступ до параметру double & x (Point   &  u)  {   return  u._x; } Point v; v._x = 10; v._y = 20; cout<<   x(v)  <<endl; //10 x(v)=  125; cout<<  x(v)   <<endl; // 125 ,   а не  10?!
Сталі відсилки Результати не копіюються, але й не доступні для змін const double & x ( const  Point   &  u)  {   return  u._x; } Point v; v._x = 10; v._y = 20; cout<<   x(v)  <<endl; //10 // x(v)= 125; not possible now
Результати-указники Tree* createTree ( int  node, Tree *left, Tree *right) { Tree * aTree = new Tree; aTree->node = node; aTree->left = left; aTree->right = right; return  aTree; } Тепер можлива суперпозиція t  = createTree (3, createTree (1,0,0), createTree (2,0,0));
Висновки Результат, створений у тілі функції ,  передаємо значенням Доступ до частини або всього агрегату даних, переданого фактичним параметром, забезпечуємо результатом відсилкою Створені у функції агрегати даних передаються указником
Рекурсія Перетворює визначення в програму int  gcd ( int  v1,  int  v2) { return  v2 ? gcd (v2, v1%v2)   : v1; }
Неефективність рекурсії int  BadFib ( int  n ) { // Так не варто рахувати!!! if  (n==0)  return  0;  if  (n==1)  return  1;  return  BadFib(n-1)+BadFib(n-2); }
Ефективна рекурсія void  fib   ( int  &f1,  int  &f2,  int  n) { // n  зменшується на 1,  f1  і  f2  зсуваються вправо int  f; if  (n>=2) { f=f2; f2+=f1; f1=f; fib(f1, f2, n-1); } }

More Related Content

PPT
09 Static Polymorphism
PPT
02 Copying Objects
PPT
07 Containers
PPT
01 Incapsulation
PPTX
передача параметрів в функції
PDF
Coding for Future in Lutsk. JavaScript. Part 6
PPT
04 Operators
PPT
JavaScript. Lectures. part 1. basis
09 Static Polymorphism
02 Copying Objects
07 Containers
01 Incapsulation
передача параметрів в функції
Coding for Future in Lutsk. JavaScript. Part 6
04 Operators
JavaScript. Lectures. part 1. basis

What's hot (20)

PDF
лекц2 невизн інт
PPT
05 Operations And Utilities
PDF
Lecture 13 Local Optimization on Mobile Devices
PPT
07 Localisation
PDF
Lecture 07 swift
PPT
06 Pointers To Class Members
PPTX
PPT
03 Constants And Variables
PPTX
Neviznacheny integral
PPT
05 Arrays
PPT
Programuvanna na movi_pascal
PPTX
урок 19 цикли Складання програм
PPT
08 Templates
PPT
Programuvanna na movi_pascal
PPT
General Functors
PPT
Презентація на тему :"Первісна та невизначений інтеграл"
PPT
General Functors ...
лекц2 невизн інт
05 Operations And Utilities
Lecture 13 Local Optimization on Mobile Devices
07 Localisation
Lecture 07 swift
06 Pointers To Class Members
03 Constants And Variables
Neviznacheny integral
05 Arrays
Programuvanna na movi_pascal
урок 19 цикли Складання програм
08 Templates
Programuvanna na movi_pascal
General Functors
Презентація на тему :"Первісна та невизначений інтеграл"
General Functors ...
Ad

Viewers also liked (7)

PDF
2013 AllStar Celebrity Kickoff Party
PPT
Lc Presentation
PPT
01 Paradigms
PPT
Holistic Approach
PPT
06 Data Structures
PDF
The Race Party - Indianapolis 500 weekend
PDF
2014 Super Bowl NYC
2013 AllStar Celebrity Kickoff Party
Lc Presentation
01 Paradigms
Holistic Approach
06 Data Structures
The Race Party - Indianapolis 500 weekend
2014 Super Bowl NYC
Ad

Similar to 08 Functions (20)

PDF
Функції в C++
PPTX
Вказівники с++
PPTX
тема 7
PPTX
Lecture 09 for distance courses "Fundamentals of Informatics"
PPTX
Lecture 10 for distance courses "Fundamentals of Informatics"
PPTX
Lecture 7.1 for distance courses "Fundamentals of Informatics"
PPTX
Lecture 03 for distance courses "Fundamentals of Informatics"
PPT
Масиви Структури даних (1).ppt
PPTX
рядки
PPTX
Lecture 08 for distance courses "Fundamentals of Informatics"
PPTX
Lecture 7.2 for distance courses "Fundamentals of Informatics"
PPT
02 Arithmetic Data Types
Функції в C++
Вказівники с++
тема 7
Lecture 09 for distance courses "Fundamentals of Informatics"
Lecture 10 for distance courses "Fundamentals of Informatics"
Lecture 7.1 for distance courses "Fundamentals of Informatics"
Lecture 03 for distance courses "Fundamentals of Informatics"
Масиви Структури даних (1).ppt
рядки
Lecture 08 for distance courses "Fundamentals of Informatics"
Lecture 7.2 for distance courses "Fundamentals of Informatics"
02 Arithmetic Data Types

08 Functions

  • 1. Бублик Володимир Васильович Програмування - 2 Лекція 8. Процедурне програмування. Функції Лекції для студентів 2 курсу
  • 2. 1. Визначення та виклик функції
  • 3. Визначення функції //Greatest Common Divider // заголовок int gcd (int m, int n) //тіло функції { while (m != n) if ( m>n ) m=m-n; else n=n-m; // m == n //результат return m; }
  • 4. Виклик функції int a, b, c; cin>>a>>b; c = gcd (a, b); // 1. Ініціалізація параметрів // int m=a; int n=b; //2. Виконання тіла фукції //3. Обчислення результату (c=m;) Звичайно виклик функції полягає в припиненні нормального ходу виконання об'єктного коду, що містить виклик. Відбувається ініціалізація і починає виконуватись об'єктний код функції. Після повернення результату виконання основного коду відновлюється
  • 5. Способи виконання функцій Закрита функція Викликається командою переходу з поверненням Відкрита (вбудована) функція Підставляється в кожне місце виклику В обох випадках параметри і результат передаються в один і той же спосіб
  • 6. Вбудовані функції Об'єктний код закритої функції присутній в програмі один раз. Відповідні команди керування приводять до її активації, виконання коду і наступного повернення в код виклику. Це накладні витрати виклику . Їх можна зменшити, якщо розміщувати код функції безпосередньо в місці виклику. Накладними витратами в цьому разі стане дублювання коду в кожному місці виклику. Компілятор сам приймає рішення, як реалізувати функцію ( inline лише порада). inline double max( double x, double y) { return (x>y)?x:y; }
  • 7. Приблизна схема викликів z= max (5, 6); z= max(max(1,2),3); double x = 5; double y = 6; z = (x>y)?x:y; double x = 1; double y = 2; double x1 = (x>y)?x:y ; double y1 = 3; z = (x1>y1)?x1:y1 ;
  • 8. Макровизначення Макровизначення на перший погляд дуже схожі на вбудовані функції, вони теж підставляються в місця виклику, але відрізняються від вбудованих функцій способом передачі параметрів текстовою підстановкою. Приклад макровизначення # define MAX(x,y) ((x)>(y))?(x):(y) Приклади макровикликів MAX(1,2); MAX(a+ b , c-d );
  • 9. Приклади макропідстановок double a = MAX(1,2); //((1)>(2))?(1):(2) cout<<a<<endl; //2 double b=2; a= MAX(++a,b); // (( ++ a )>(b))?( ++ a ):(b) or // (3>2)?4:2) == 4 cout<<a<<endl; // 4 // The next expression is not correct // because of priorities cout<<MAX(++a,b)<<endl; //means // cout<< (5>2)?6:4 <<endl or // (cout<<(5>2))?6:(4<<endl ) ERROR
  • 10. Виклик функції inline double max( double x, double y) { return (x>y)?x:y; } double a = max (1,2); // double x=1; double y=2; // ініціалізація параметрів // return (1>2)?1:2 == 2 виконання тіла і повернення cout<<a<<endl; //2 double b=2; a= max (++a,b); // double x=++a, double y=b; (x==3, y==2) // return (3>2)?3:2 == 3
  • 12. Передача параметрів Розглянемо оголошення функції void f ( T x); де f ― ім'я функції, T ― тип параметру, x ― ім'я параметру Розглянемо виклик цієї функції f(e) ; де e ― вираз підходящого типу (фактичний параметр) Виконання функції починається визначенням локальної змінної для параметру x з її одночасною ініціалізацією T x = e ;
  • 13. Передача параметрів значенням Ініціалізація T x = e ; говорить про те, що перед виконанням функції обчислюється значення фактичного параметру е ( r-value) Кажуть, що параметри передаються значенням Залежно від типу T ці значення можуть бути значеннями базових типів, структур, адресами (у випадку указників) відсилками (у випадку псевдонімів)
  • 14. Сторонній ефект Якщо передаються значення базових типів або структур, то виконання функції не впливає на наступні значення фактичних параметрів Якщо формальний параметр є указником, то можлива зміна значення елементу пам'яті, на який цей указник встановлено Якщо формальний параметр псевдонім, то через нього функція одержує прямий доступ до елементу пам'яті, до якого псевдонім відсилає Якби не було стороннього ефекту, то який сенс мали б функції виду void f (T x) ?
  • 15. Приклад 1. swap void swap( double x, double y) { // значення базового типу double double z = x; x=y; y=z; cout<<x<<‘,’<<y<<endl; //2,1 } double a=1, b=2; swap( a,b); // double x=a, y=b; cout<<a<<‘,‘<<b<<endl; // 1,2
  • 16. Приклад 2 . p swap void p swap( double * x, double * y) { double z = * x; * x= * y; * y=z; cout<<*x<<‘,’<<*y<<endl; //2,1 } double a=1, b=2; p swap( & a, & b); // за коректність відповідає користувач // double *x= &a, *y= &b; cout<<a<<‘,‘<<b<<endl; // 2,1
  • 17. Приклад 3 . r swap ( тільки С++) void r swap( double & x, double & y) { double z = x; x=y; y=z; cout<<x<<‘,’<<y<<endl; // 2,1 } double a=1, b=2; r swap( a,b); // за коректність відповідає розробник // double & x=a, & y=b; cout<<a<<‘,‘<<b<<endl; // 2, 1
  • 18. Безпечність виклику Безпечні swap( a,b); r swap( a,b); Небезпечний p swap( &a, &b); // p swap( 0, &b); ??? void pswap(double *x, double *y) { assert((x!=0)&&(y!=0)); double z = *x; *x=*y; *y=z; cout<<*x<<','<<*y<<endl; }
  • 19. Масиви як параметри Масиви ― головна причина використання параметрів-указників Використання параметром масиву нічим не відрізняється від параметра-указника T *a T a[]
  • 20. Задача додому Що б це значило? Організуйте виклик функції ptrswap та графічно зобразіть роботу з пам'яттю void ptrswap( int *&u, int *&v ) { int *w = v; v = u; u = w; }
  • 21. Приклад 4. Не має значення, як визначити параметр в кожній з трьох сигнатур void init ( char * ch); void show ( char ch[]); void swapar ( char []);
  • 22. Приклад 4 (1). const int n=26; int main () { char ch[n]; init (ch); show(ch); swapar(ch); show(ch); return 0; }
  • 23. Приклад 4 ( 2 ) . void init( char * ch) { for (int i= 'a'; i<'a'+ n ; i++) // не дуже добре ch[i-'a'] = i; } Ніхто не заборонить навіть таке for (int i= 'a'; i<'a'+ 100000 ; i++) ch[i-'a'] = i; Результат передбачити неможливо, швидше всього access violation
  • 24. Приклад 4 ( 3 ) . Допустимий навіть зовсім безглуздий виклик char ch; init( & ch) ; Теж access violation
  • 25. Приклад 4 ’ (1). Краще рішення передати розмір масиву додатковим параметром void init ( char * ch, int n ); void show ( char ch[], int n ); void swapar ( char [], int );
  • 26. Приклад 4 ’ (2). int main () { const int n=26; char ch[n]; init (ch, n ); show (ch, n ); swapar (ch, n ); show (ch, n ); return 0; }
  • 27. Приклад 4 ’(2) . void init ( char * ch , size_t n ) { for ( int i= 'a'; i<'a'+ n ; i++) //тепер в порядку ch [ i-'a' ] = i; }
  • 28. Приклад 4 ’(3) . void show( char ch[n], size_t n) { cout<<&quot;start string:&quot;<<endl; for ( int i=0; i< n ; i++) cout<<ch[i]<<' '; cout<<endl; }
  • 29. Приклад 4 ’(4) . void swapar( char ch[], size_t n) { for ( int i = 0; i< n /2; i++) pswap(ch +i ,ch+n-1-i); };
  • 30. Приклад 4 ’( 5 ) . Результат роботи s tart string : a b c d e f g h i j k l m n o p q r s t u v w x y z s tart string : z y x w v u t s r q p o n m l k j i h g f e d c b a
  • 31. Висновок Передаючи в функцію параметром масив (або указник як масив) , передбачаємо розмір масиву додатковим параметром void f (T *array, size_t size); або краще void f (T [] array, size_t size);
  • 32. “ Загорнуті” масиви // Визначення “загорнутого” вектора struct WrappedVector { static const int n; // статичне поле одне на всіх double * x; }; // сигнатура добутку (дуже погана) double prod ( WrappedVector , WrappedVector );
  • 33. Параметри сталі відсилки // сигнатура добутку (краще) // структури не копіюються double prod ( WrappedVector & , WrappedVector & ); // сигнатура добутку (ще краще) // структури не копіюються, // доступ для зміни фактичного // параметру закрито double prod ( const WrappedVector & , const WrappedVector & );
  • 34. Скалярний добуток // визначення статичного поля структури const int WrappedVector::n = 100; // визначення скалярного добутку double prod ( const WrappedVector& a, const WrappedVector& b) { double s = 0; for ( int i=0; i<a.n; i++) s+=a.x[i]*b.x[i]; return s; }
  • 35. Створення вектору ― конструктор // Відсилка ― повний доступ void construct ( WrappedVector & ); void construct (WrappedVector & a) { a.x = new double [a.n]; for ( int i=0; i<a.n; i++) a.x[i] = rand(); return ; }
  • 36. Видалення об'єкту ― д еструктор void destroy (Wrapped Vector & a ) { delete [] a.x; a.x = 0; return ; } // Як примусити кожного прибирати за собою?
  • 37. Замовчувані значення параметрів T f (T1 x1=a1, T2 x2=a2, …, Tn xn=an); f(); f(c1); f(c1,c2);…. void createPoint(Point & u, double x=0, double y=0) { u._x = x; u._y=y; } createPoint (u); // (0,0) createPoint (v, 1); // (1,0) createPoint (w, 1, 2); // (1,2)
  • 38. Типи параметрів (перший підсумок) const T T T& const T& Копіювання, незмінний формальний параметр , немає впливу на фактичний параметр Копіювання, змінний формальний параметр, немає впливу на фактичний параметр Відсилка без копіювання, повний доступ до фактичного параметру (C++) Стала відсилка без копіювання, фактичний параметр незмінний (C++)
  • 39. Порівняння типів параметрів Найнадійніший параметр-результат T& Параметри-значення Точка зору користувача: параметри T , const T і const T& не відрізняються з точки зору використання Точка зору розробника: const T надійніший, бо не створює непорозумінь; const T& найефективніший, оскільки не вимагає витрат на копіювання
  • 40. Сталий параметр //Greatest Common Divider int gcd ( const int m 0 , const int n 0 ) { int m = m0, n = n0; while (m != n) if ( m>n ) m=m-n; else n=n-m; // m == n // Початкові значення не втрачено assert((m0%m==0) && (n0%n==0) ); return m; }
  • 41. Параметри-указники T* const T* T* const const T*const Повний але непрямий доступ до фактичного параметру; передача масивів ( T[]) (в стилі чистого С) Маловживаний тип параметру; доступ лише для читання: сталий фактичний параметр ( vs const T&) Незмінний формальний параметр - указник , немає впливу на фактичний параметр-адресу ( this в C++) Незмінний указник на сталий фактичний параметр В усіх випадках 5-8 сам указник передається значенням
  • 42. Головна проблема параметра-указника Як відрізнити масив від скалярного значення? Вихід: жорстка дисципліна програмування, додатковий параметр типу size_t з відповідним коментарем void f ( double * px, size_t size_of_px );
  • 43. Типова помилка в параметрі-указнику // Думалось, що ця функція виділятиме пам’ять для // указника t void allocate ( char *t, size_t n, char c) { t = new char [n]; t[n-1]='\0'; for (int i=0;i<n-1;i++) t[i]=c; cout<<“This is inside: “<<t<<endl; return ; }
  • 44. Результат // Виклик функції allocate char a[]=“This was before”; cout<<a<<endl; // This was before allocate (a, 10 , ‘a’); // This is inside: aaaaaaaaaa cout<<a<<endl; // This was before // Вихід: передати указник відсилкою void construct ( WrappedVector & );
  • 45. Параметри-указники T** T*& . . . . . . . . . . . . . . . . . const T* const * const Указник-вихідний параметр (чистий С) Те ж саме, але в стилі С++ А ще різні варіації на тему const
  • 46. Указник другого рівня // Тепер правильно void allocate ( char ** t size_t n, char c) { * t = new char [n]; (*t) [n-1]='\0'; for ( int i=0;i<n-1;i++) (*t) [i]=c; cout<< *t <<endl; return ; }
  • 47. Виклик указником другого рівня // Виклик функції allocate char * a=“This was before”; cout<<a<<endl; // This was before allocate ( & a, 10 , ‘a’); // This is inside: aaaaaaaaaa cout<<a<<endl; // aaaaaaaaaa
  • 48. Псевдонім указника void allocate ( char * & t, size_t n, char c) { t = new char [n]; t[n-1]='\0'; for ( int i=0;i<n-1;i++) t[i]=c; cout<<t<<endl; return ; }
  • 49. Виклик відсилкою до указника // Виклик функції allocate char * a=“This was before”; cout<<a<<endl; // This was before allocate (a, 10 , ‘a’); // This is inside: aaaaaaaaaa cout<<a<<endl; // aaaaaaaaaa
  • 50. Особливості нульового параметру-указника Структура дерева struct Tree { int node; Tree *left; Tree *right; }; Вузли-листя мають нульові указники left і right
  • 51. Вершина дерева Параметр aTree відіграє роль параметру-результату void createTree (Tree ** aTree, int node, Tree *left, Tree *right) { *aTree = new Tree; (*aTree) -> node = node; (*aTree) -> left = left; (*aTree) -> right = right; return ; }
  • 52. Видалення дерева void destroyTree(Tree **aTree) { Tree *subTree; if ((*aTree)==0) return ; subTree = (*aTree)->left; //left subtree destroyTree(&subTree); subTree = (*aTree)->right; //right subtree destroyTree(&subTree); delete *aTree; //delete root *aTree = 0; return ; }
  • 53. Приклад використання int main () { Tree * t, * t1, *t2 ; createTree(&t1,1,0,0); createTree(&t2,2,0,0); createTree(&t,3,t1,t2); destroyTree (&t); // Проблема: значення t1 і t2 return 0; }
  • 54. Завданння Переписати обробку дерев, замінивши скрізь, де можна указники відсилками
  • 55. Указники vs. відсилки Замінимо void createTree (Tree **aTree, int node, Tree *left, Tree *right); на void createTree (Tree * & aTree, int node, Tree *left, Tree *right); але не void createTree (Tree * & aTree, int node, Tree &left, Tree &right ); бо виклик createTree(t1,1,0,0); стане незаконним
  • 56. Зведення типів при передачі параметрів void show ( doubl e x) { cout<<x<<endl; } // OK int k=1; show(k); //1 long int lk=2; show(lk); //2 float x=3; show(x); //3
  • 57. Неявне зведення відсилок неможливе void show ( double & x) { cout<<x<<endl; } // ERRORS int k=1; show(k); long int lk=2; show(lk); float x=3; show(x);
  • 58. Явне зведення відсилок хибне void show ( double & x) { cout<<x<<endl; } // Type conversion int k=1; show(( double &) k); //-9.25596e+061 long int lk=2; show(( double &) lk); //2.122e-314 float x=3; show(( double &) x); //4.77656e-314 Не намагайтеся допомогти компілятору: не вийде!
  • 59. Неявне зведення указників неможливе теж void show ( double * x) { cout<<x<<endl; } // ERRORS int k=1; show(&k); long int lk=2; show(&lk); float x=3; show(&x);
  • 61. Результат функції Результати значення T f(…); const T f(…); Результати відсилки T& f(…); const T& f(…); Результати указники T* f(…):
  • 62. Результати-значення Типи результатів T і const T не різняться для типів даних, прийнятих в С ( далі в С++ будуть відрізнятися )
  • 63. Точка площини struct Point { double _x; double _y; }; const Point plus (Point u, Point v) { Point res; res._x = u._x+v._x; res._y = u._y+v._y; return res; }
  • 64. Дерево Як і в попередньому прикладі результат повністю копіюється при виході з функції Tree createTree ( int node, Tree * left, Tree * right) { Tree aTree; aTree.node = node; aTree.left = left; aTree.right = right; return aTree ; }
  • 65. Результати-відсилки Дають повний доступ до частин агрегатів даних, але не забувайте про правильну передачу параметру double & x (Point u) { return u._x; } Point v; v._x = 10; v._y = 20; cout<<x(v) <<endl; //10 x(v)= 125; cout<< x(v)<<endl; //10 , а не 125 ?!
  • 66. Результати і параметри-відсилки Тепер повний доступ до параметру double & x (Point & u) { return u._x; } Point v; v._x = 10; v._y = 20; cout<< x(v) <<endl; //10 x(v)= 125; cout<< x(v) <<endl; // 125 , а не 10?!
  • 67. Сталі відсилки Результати не копіюються, але й не доступні для змін const double & x ( const Point & u) { return u._x; } Point v; v._x = 10; v._y = 20; cout<< x(v) <<endl; //10 // x(v)= 125; not possible now
  • 68. Результати-указники Tree* createTree ( int node, Tree *left, Tree *right) { Tree * aTree = new Tree; aTree->node = node; aTree->left = left; aTree->right = right; return aTree; } Тепер можлива суперпозиція t = createTree (3, createTree (1,0,0), createTree (2,0,0));
  • 69. Висновки Результат, створений у тілі функції , передаємо значенням Доступ до частини або всього агрегату даних, переданого фактичним параметром, забезпечуємо результатом відсилкою Створені у функції агрегати даних передаються указником
  • 70. Рекурсія Перетворює визначення в програму int gcd ( int v1, int v2) { return v2 ? gcd (v2, v1%v2) : v1; }
  • 71. Неефективність рекурсії int BadFib ( int n ) { // Так не варто рахувати!!! if (n==0) return 0; if (n==1) return 1; return BadFib(n-1)+BadFib(n-2); }
  • 72. Ефективна рекурсія void fib ( int &f1, int &f2, int n) { // n зменшується на 1, f1 і f2 зсуваються вправо int f; if (n>=2) { f=f2; f2+=f1; f1=f; fib(f1, f2, n-1); } }