Лекції для студентів 2 курсу Консультації: вівторок, середа, четвер год., кімн. 204/1, к афедра мультимедійних систем Cologn Бублик Володимир Васильович Процедурне програмування C/C++ Лекція 9. Передача параметрів
© 2006 Бублик В.В. Процедурне програмування. 9. Передача параметрів 2 (57) Виклик функції Розглянемо оголошення функції void f (T x); де f ім'я функції, T тип параметру, x ім'я параметру Розглянемо виклик цієї функції f(e); де e вираз підходящого типу (фактичний параметр) Виконання функції починається визначенням локальної змінної для параметру x з її одночасною ініціалізацією T x = e;
© 2006 Бублик В.В. Процедурне програмування. 9. Передача параметрів 3 (57) Передача параметрів значенням Ініціалізація T x = e; говорить про те, що перед виконанням функції обчислюється значення фактичного параметру е (r- value) Кажуть, що параметри передаються значенням Залежно від типу T ці значення можуть бути 1.значеннями базових типів, структур, 2.адресами (у випадку указників) 3.відсилками (у випадку псевдонімів)
© 2006 Бублик В.В. Процедурне програмування. 9. Передача параметрів 4 (57) Сторонній ефект 1.Якщо передаються значення базових типів або структур, то виконання функції не впливає на наступні значення фактичних параметрів 2.Якщо формальний параметр є указником, то можлива зміна значення елементу пам'яті, на який цей указник встановлено 3.Якщо формальний параметр псевдонім, то через нього функція одержує прямий доступ до елементу пам'яті, до якого псевдонім відсилає Якби не було стороннього ефекту, то який сенс мали б функції виду void f (T x)?
© 2006 Бублик В.В. Процедурне програмування. 9. Передача параметрів 5 (57) Приклад 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
© 2006 Бублик В.В. Процедурне програмування. 9. Передача параметрів 6 (57) Приклад 2. pswap void pswap(double *x, double *y) { double z = *x; *x=*y; *y=z; cout<<*x<<,<<*y<<endl; //2,1 } double a=1, b=2; pswap(&a, &b);//за коректність відповідає користувач // double *x= &a, *y= &b; cout<<a<<,<<b<<endl;// 2,1
© 2006 Бублик В.В. Процедурне програмування. 9. Передача параметрів 7 (57) Приклад 3. rswap (тільки С++) void rswap( double &x, double &y) { double z = x; x=y; y=z; cout<<x<<,<<y<<endl; // 2,1 } double a=1, b=2; rswap(a,b); //за коректність відповідає розробник // double &x=a, &y=b; cout<<a<<,<<b<<endl;// 2, 1
© 2006 Бублик В.В. Процедурне програмування. 9. Передача параметрів 8 (57) Безпечність виклику Безпечні swap(a,b); rswap(a,b); Небезпечний pswap(&a, &b);// pswap(0, &b); ??? void pswap(double *x, double *y) { assert((x!=0)&&(y!=0)); double z = *x; *x=*y; *y=z; cout<<*x<<','<<*y<<endl; }
© 2006 Бублик В.В. Процедурне програмування. 9. Передача параметрів 9 (57) Задача додому Що б це значило? Організуйте виклик функції ptrswap та графічно зобразіть роботу з пам'яттю void ptrswap( int *&u, int *&v ) { int *w = v; v = u; u = w; }
© 2006 Бублик В.В. Процедурне програмування. 9. Передача параметрів 10 (57) Масиви як параметри Масиви головна причина використання параметрів- указників Використання параметром масиву нічим не відрізняється від параметра-указника T *a T a[]
© 2006 Бублик В.В. Процедурне програмування. 9. Передача параметрів 11 (57)11 Приклад 4. Не має значення, як визначити один і той же параметр в кожній з трьох сигнатур void init (int* ch); void show (int ch[]); void swapar (int []); В усіх трьох прикладах передача параметра невдала, бо незрозуміло, скільки елементів у масиві
© 2006 Бублик В.В. Процедурне програмування. 9. Передача параметрів 12 (57) Приклад 4 (1). const int n=26; int main() { int ch[n]; init (ch); show(ch); swapar(ch); show(ch); return 0; }
© 2006 Бублик В.В. Процедурне програмування. 9. Передача параметрів 13 (57) Приклад 4(2). void init(int* ch) { for(int i= 'a'; i<'a'+n; ++i) //не дуже добре ch[i-'a'] = i; } Ніхто не заборонить навіть таке for(int i= 'a'; i<'a' ; ++i) ch[i-'a'] = i; Результат передбачити неможливо, швидше всього access violation
© 2006 Бублик В.В. Процедурне програмування. 9. Передача параметрів 14 (57) Приклад 4(3). Допустимий навіть зовсім безглуздий виклик int ch; init(&ch); Теж access violation
© 2006 Бублик В.В. Процедурне програмування. 9. Передача параметрів 15 (57) Приклад 4(1). Краще рішення передати розмір масиву додатковим параметром void init (int* ch, int n); void show (int ch[], int n); void swapar (int [], int);
© 2006 Бублик В.В. Процедурне програмування. 9. Передача параметрів 16 (57) Приклад 4(2). int main() { // Розмір масиву визначено const int n=26; int ch[n]; init (ch, n); show (ch, n); swapar (ch, n); show (ch, n); return 0; }
© 2006 Бублик В.В. Процедурне програмування. 9. Передача параметрів 17 (57) Приклад 4(2). void init (int* ch, size_t n) { for(int i= 'a'; i<'a'+n; i++) //тепер в порядку ch[i-'a'] = i; }
© 2006 Бублик В.В. Процедурне програмування. 9. Передача параметрів 18 (57) Приклад 4(3). void show(int ch[n], size_t n) { cout<<"start string:"<<endl; for (int i=0; i<n; i++) cout<<ch[i]<<' '; cout<<endl; }
© 2006 Бублик В.В. Процедурне програмування. 9. Передача параметрів 19 (57) Приклад 4(4). void swapar(int ch[], size_t n) { for (int i = 0; i<n/2; i++) pswap(ch+i,ch+n-1-i); };
© 2006 Бублик В.В. Процедурне програмування. 9. Передача параметрів 20 (57) Приклад 4(5). Результат роботи start 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 start 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
© 2006 Бублик В.В. Процедурне програмування. 9. Передача параметрів 21 (57) Висновок Передаючи в функцію параметром масив (або указник як масив), передбачаємо розмір масиву додатковим параметром void f (T *array, size_t size); або краще void f (T [] array, size_t size);
© 2006 Бублик В.В. Процедурне програмування. 9. Передача параметрів 22 (57) Загорнуті масиви //Визначення загорнутого вектора struct WrappedVector { static const int n;//статичне поле одне на всіх double * x; }; //сигнатура добутку (дуже погана) double prod (WrappedVector, WrappedVector);
© 2006 Бублик В.В. Процедурне програмування. 9. Передача параметрів 23 (57) Параметри сталі відсилки //сигнатура добутку (краще) //структури не копіюються double prod (WrappedVector&, WrappedVector&); //сигнатура добутку (ще краще) //структури не копіюються, //доступ для зміни фактичного //параметру закрито double prod (const WrappedVector&, const WrappedVector&);
© 2006 Бублик В.В. Процедурне програмування. 9. Передача параметрів 24 (57) Скалярний добуток //визначення статичного поля структури 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; }
© 2006 Бублик В.В. Процедурне програмування. 9. Передача параметрів 25 (57) Створення вектору конструктор //Відсилка повний доступ 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; }
© 2006 Бублик В.В. Процедурне програмування. 9. Передача параметрів 26 (57) Видалення об'єкту деструктор void destroy (WrappedVector& a) { delete [] a.x; a.x = 0; return; } //Як примусити кожного прибирати за собою?
© 2006 Бублик В.В. Процедурне програмування. 9. Передача параметрів 27 (57) Типи параметрів (перший підсумок) 1.const T 2.T 3.T& 4.const T& Копіювання, незмінний формальний параметр, немає впливу на фактичний параметр Копіювання, змінний формальний параметр, немає впливу на фактичний параметр Відсилка без копіювання, повний доступ до фактичного параметру (C++) Стала відсилка без копіювання, фактичний параметр незмінний (C++)
© 2006 Бублик В.В. Процедурне програмування. 9. Передача параметрів 28 (57) Порівняння типів параметрів Найнадійніший параметр-результат T& Параметри-значення Точка зору користувача: параметри T, const T і const T& не відрізняються з точки зору використання Точка зору розробника: const T надійніший, бо не створює непорозумінь; const T& найефективніший, оскільки не вимагає витрат на копіювання
© 2006 Бублик В.В. Процедурне програмування. 9. Передача параметрів 29 (57) Параметри-указники 5.T* 6.const T* 7.T* const 8.const T*const Повний але непрямий доступ до фактичного параметру; передача масивів (T[]) (в стилі чистого С) Маловживаний тип параметру; доступ лише для читання: сталий фактичний параметр (vs const T&) Незмінний формальний параметр- указник, немає впливу на фактичний параметр-адресу (this в C++) Незмінний указник на сталий фактичний параметр В усіх випадках 5-8 сам указник передається значенням
© 2006 Бублик В.В. Процедурне програмування. 9. Передача параметрів 30 (57) Головна проблема параметра-указника Як відрізнити масив від скалярного значення? Вихід: жорстка дисципліна програмування, додатковий параметр типу size_t з відповідним коментарем void f (double * px, size_t size_of_px);
© 2006 Бублик В.В. Процедурне програмування. 9. Передача параметрів 31 (57) Типова помилка в параметрі-указнику //Думалось, що ця функція виділятиме память для //указника 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; }
© 2006 Бублик В.В. Процедурне програмування. 9. Передача параметрів 32 (57) Результат //Виклик функції 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 &);
© 2006 Бублик В.В. Процедурне програмування. 9. Передача параметрів 33 (57) Параметри-указники 9.T** 10.T*& const T* const * const Указник-вихідний параметр (чистий С) Те ж саме, але в стилі С++ А ще різні варіації на тему const
© 2006 Бублик В.В. Процедурне програмування. 9. Передача параметрів 34 (57) Указник другого рівня //Тепер правильно 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; }
© 2006 Бублик В.В. Процедурне програмування. 9. Передача параметрів 35 (57) Виклик указником другого рівня //Виклик функції allocate char *a=This was before; cout<<a<<endl; //This was before allocate (&a, 10, a); // This is inside: aaaaaaaaaa cout<<a<<endl; // aaaaaaaaaa
© 2006 Бублик В.В. Процедурне програмування. 9. Передача параметрів 36 (57)© Бублик В.В. Програмування Процедурне програмування. Функції Псевдонім указника 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; }
© 2006 Бублик В.В. Процедурне програмування. 9. Передача параметрів 37 (57) Виклик відсилкою до указника //Виклик функції allocate char *a=This was before; cout<<a<<endl; //This was before allocate (a, 10, a); // This is inside: aaaaaaaaaa cout<<a<<endl; // aaaaaaaaaa
© 2006 Бублик В.В. Процедурне програмування. 9. Передача параметрів 38 (57) Особливості нульового параметру-указника Структура дерева struct Tree { int node; Tree *left; Tree *right; }; Вузли-листя мають нульові указники left і right
© 2006 Бублик В.В. Процедурне програмування. 9. Передача параметрів 39 (57) Вершина дерева Параметр aTree відіграє роль параметру-результату void createTree (Tree **aTree, int node, Tree *left, Tree *right) { *aTree = new Tree; (*aTree) -> node = node; (*aTree) -> left = left; (*aTree) -> right = right; return; }
© 2006 Бублик В.В. Процедурне програмування. 9. Передача параметрів 40 (57) Видалення дерева 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; }
© 2006 Бублик В.В. Процедурне програмування. 9. Передача параметрів 41 (57) Приклад використання 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; }
© 2006 Бублик В.В. Процедурне програмування. 9. Передача параметрів 42 (57) Завданння Переписати обробку дерев, замінивши скрізь, де можна указники відсилками
© 2006 Бублик В.В. Процедурне програмування. 9. Передача параметрів 43 (57) Указники 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); стане незаконним
© 2006 Бублик В.В. Процедурне програмування. 9. Передача параметрів 44 (57) Зведення типів при передачі параметрів void show (double x) { cout<<x<<endl; } //OK int k=1; show(k);//1 long int lk=2; show(lk);//2 float x=3; show(x);//3
© 2006 Бублик В.В. Процедурне програмування. 9. Передача параметрів 45 (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);
© 2006 Бублик В.В. Процедурне програмування. 9. Передача параметрів 46 (57) Явне зведення відсилок хибне void show (double & x) { cout<<x<<endl; } //Type conversion int k=1; show((double&)k); // e+061 long int lk=2; show((double&) lk); //2.122e-314 float x=3; show((double&) x); // e-314 Не намагайтеся допомогти компілятору: не вийде!
© 2006 Бублик В.В. Процедурне програмування. 9. Передача параметрів 47 (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);
Повернення результату Результат функції може бути значенням, сталим значенням, відсилкою, сталою відсилкою або указником
© 2006 Бублик В.В. Процедурне програмування. 9. Передача параметрів 49 (57) Результат функції Результати значення 1.T f(…); 2.const T f(…); Результати відсилки 3.T& f(…); 4.const T& f(…); Результати указники 5.T* f(…):
© 2006 Бублик В.В. Процедурне програмування. 9. Передача параметрів 50 (57) Результати-значення Типи результатів T і const T не різняться для типів даних, прийнятих в С (далі в С++ будуть відрізнятися)
© 2006 Бублик В.В. Процедурне програмування. 9. Передача параметрів 51 (57) Точка площини 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; }
© 2006 Бублик В.В. Процедурне програмування. 9. Передача параметрів 52 (57) Дерево Як і в попередньому прикладі результат повністю копіюється при виході з функції Tree createTree ( int node, Tree * left, Tree * right) { Tree aTree; aTree.node = node; aTree.left = left; aTree.right = right; return aTree; }
© 2006 Бублик В.В. Процедурне програмування. 9. Передача параметрів 53 (57) Результати-відсилки Дають повний доступ до частин агрегатів даних, але не забувайте про правильну передачу параметру 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 ?!
© 2006 Бублик В.В. Процедурне програмування. 9. Передача параметрів 54 (57) Результати і параметри-відсилки Тепер повний доступ до параметру 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?!
© 2006 Бублик В.В. Процедурне програмування. 9. Передача параметрів 55 (57) Сталі відсилки Результати не копіюються, але й не доступні для змін 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
© 2006 Бублик В.В. Процедурне програмування. 9. Передача параметрів 56 (57) Результати-указники 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));
© 2006 Бублик В.В. Процедурне програмування. 9. Передача параметрів 57 (57) Висновки Результат, створений у тілі функції, передаємо значенням Доступ до частини або всього агрегату даних, переданого фактичним параметром, забезпечуємо результатом відсилкою Створені у функції агрегати даних передаються указником