Єдина Країна!

Головне меню

Наша кнопка

Українські уроки про ІТ

Друзі

Підтримка української армії


Головна Програмування - C++ Базовий курс програмування на С++. Урок 16. Вказівники, посилання (частина 2)

Базовий курс програмування на С++. Урок 16. Вказівники, посилання (частина 2)
Написав Joker   
Неділя, 31 січня 2016 19:06
Переглядів: 9657

Життя належить тобі,

і не буде для тебе нічого неможливого,

якщо ти дійсно цього захочеш.

(с) Марк Леві

Сьогодні ми вивчимо

1. Посилання

2. Вказівники і функції

3. Посилання і функції

4. Оператори new і delete

5. Вказівники на вказівники

6. Домашнє завдання

 

 

Посилання

Посилання частково схожі на вказівники, але є декілька суттєвих відмінностей. По-перше, посилання завжди працює тільки з одним значенням. Воно пов’язане з об’єктом на яке посилається і не може посилатися на будь-який інший. При створенні завжди обов’язкова ініціалізація. Створюється посилання за допомогою вже знайомого нам оператора &. Давайте продемонструємо це на прикладі.

#include  <iostream>
#include  <iomanip>
#include  <vector>
#include  <time.h>
#include  <cstdlib>
#include  <conio.h>
#include  <cstring>
#include  <cmath>
#include  <algorithm>
#include  <ctime>
using namespace std;

int main ()
{
	int a = 5;
	int &p = a;	// Посилання на змінну а

	cout << "p = " << p << endl;
	cout << "a = " << a << endl;
	cout << "&a = " << &a << endl;
	cout << "&p = " << &p << endl;

	_getch();
	return  0;
}

Створивши посилання int &p = a, ми не можемо десь у подальшому коді програми створити іншу змінну, припустимо int b = 15; і сказати, щоб &p = b. Це призведе до помилки!

Але це все дає ще одну можливість керувати змінною. Достатньо просто використовувати її як і звичайну змінну.

Якщо додати у попередню програму такий рядок:

p = 99;

То змінна a досі буде такою самою як і p, але їхні значення зміняться на 99.

 

Вказівники і функції

Досі функції не повністю керували нашими даними.

Наприклад ось такий код не може змінити значення a.

#include  <iostream>
#include  <iomanip>
#include  <vector>
#include  <time.h>
#include  <cstdlib>
#include  <conio.h>
#include  <cstring>
#include  <cmath>
#include  <algorithm>
#include  <ctime>
using namespace std;

int func (int b)
{
	return  b+1;
}

int main ()
{
	int a = 19;

	cout << "func result is " << func(a) << endl;

	cout << "but a is still " << a << endl;

	_getch();
	return  0;
}


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

#include  <iostream>
#include  <iomanip>
#include  <vector>
#include  <time.h>
#include  <cstdlib>
#include  <conio.h>
#include  <cstring>
#include  <cmath>
#include  <algorithm>
#include  <ctime>
using namespace std;


void  func (int b[], int len)
{
	for (int i=0; i<len; ++i)
	{ 	
		b[i] = 0;
		cout << b[i] << "\t";
	}
}

int main ()
{
	const short int size = 10;

	int a[size];

	cout << "array: " << endl;

	for (int i = 0; i<size; ++i)
	{ 	
		a[i] = i;
		cout << a[i] << "\t";
	}
	cout << "\n";

	cout << "func result is " << endl;
	func(a,size);
	cout << endl;

	cout << "\nArray after func: " << endl;
	for (int i=0; i<size; ++i)
		cout << a[i] << "\t";
	cout << endl;

	_getch();
	return  0;
}


Ну добре, ми відхилилися від теми. Так от, наше завдання полягає у тому, щоб функцію змінила початкове значення змінної a з функції main. У нас є два варіанти. Перший – за допомогою вказівників. Реалізувати це можна так:

#include  <iostream>
#include  <iomanip>
#include  <vector>
#include  <time.h>
#include  <cstdlib>
#include  <conio.h>
#include  <cstring>
#include  <cmath>
#include  <algorithm>
#include  <ctime>
using namespace std;

int func (int *b)	// створюємо вказівник, а не просту змінну
{
	*b = *b + 1;
	return  *b;	// використовуєм вказівник у ф-ції
}

int main ()
{
	int a = 19;

	cout << "a = " << a << endl;
	cout << "func = " << func(&a) << endl; // Передаємо АДРЕСУ змінної а
	cout << "a = " << a << endl;


	_getch();
	return  0;
}


У функції main потрібно передати адресу змінної, а у func працюємо з вказівниками. Така функція змінила значення a = 19 на a = 20.

 

Посилання і функції

Тепер спосіб 2. Він дещо простіший з вигляду. Просто потрібно пам’ятати:

- Можна змінювати значення змінної за допомогою посилання.
- Можна просто дізнатися адресу змінної.

#include  <iostream>
#include  <iomanip>
#include  <vector>
#include  <time.h>
#include  <cstdlib>
#include  <conio.h>
#include  <cstring>
#include  <cmath>
#include  <algorithm>
#include  <ctime>
using namespace std;

int func(int &b)
{
	b++;
	return  b;
}

int main ()
{
	int a=19;

	cout << "a = " << a << endl;
	cout << "func = " << func(a) << endl;
	cout << "a = " << a << endl;

	_getch();
	return  0;
}


У такому варіанті нам не потрібно використовувати всюди значок * . В цьому випадку параметром є посилання. Посилання дає доступ із функції до фактичних параметрів і може змінювати їх. Як на мене таке використання посилань є більш поширеним і потрібним, ніж їхня можливість керувати змінними у функції main.

Але це ще не вершина! Посилання можна використовувати не тільки як параметри функцій, а й у ролі результатів функції. Тут є один підводний камінь. Функції можуть повертати посилання на об’єкт, якщо той існує в той час коли функція неактивна.

Для цього перед іменем функції ставимо знак &.

Давайте напишемо програму, яка визначить мінімальний елемент масиву і замінить його на -1 використовуючи посилання як результат роботи функції.

#include  <iostream>
#include  <iomanip>
#include  <vector>
#include  <time.h>
#include  <cstdlib>
#include  <conio.h>
#include  <cstring>
#include  <cmath>
#include  <algorithm>
#include  <ctime>
using namespace std;

int &Find_minimum (int b[], int len);

int main ()
{
	system ("color A");
	srand(time(NULL));

	const short int size = 10;
	int a[size];


	cout << "array: ";
	for (int i=0; i<size; ++i)
	{
		a[i] = 15 + rand() % 100;
		cout << a[i] << "\t";
	}
	cout << endl;

	cout << "Min --> " << Find_minimum(a,size) << endl;

	Find_minimum(a,size) = -1;
	cout << "\nFind_minimum(a,size) = -1" << endl;

	cout << "\nResult: ";
	for (int i=0; i<size; ++i)
		cout << a[i] << "\t";
	cout << endl;

	_getch();
	return  0;
}

int &Find_minimum (int b[], int len)
{
	int ix = 0,
	Min = b[ix];
	for (int i(1); i<len; ++i)
	{
		if (Min > b[i])
		{
			Min = b[i];
			ix = i;
		}
	}
	return  b[ix];
}


Спершу ми створили масив і заповнили його випадковими числами від 15 до 115. Функція Find_minimum приймає масив та його розмір, це все, що нам потрібно для роботи. Результатом функції є посилання на мінімальний елемент масиву.

Find_minimum(a,size) = -1;

Тут присвоюємо мінімуму значення -1. І виводимо новий масив, де змінився тільки один елемент.

 

Оператори new і delete

Тут ми навчимося одному із способів створювати масив, не знаючи його максимального розміру, у розумних межах. Зацікавило? У розумних межах означає, що обмеження все ж є, але про них пізніше. Спершу треба виділити пам’ять, де зберігати дані. Це робиться так

int *p = new int;

Це звісно для типу int, для інших типів аналогічно. Оператор new повертає адресу на вільне і доступне для використання місце у пам’яті. А адреси можна зберігати у вказівниках. Тому ми і створили вказівник у який записали адресу виділену оператором new.

Тепер важливий момент: якщо ми змінимо адресу вказівника, то втратимо можливість працювати з виділеною пам’ятю!

Щоб даремно не використовувати пам’ять, коли вона вже нам не потрібно потрібно її звільнити. Це робиться за допомогою delete. Завжди варто звільняти пам’ять. Тому візьміть собі за правило, якщо ви написали new, то десь має бути і delete.

delete p;

Ми звільнили пам’ять на яку посилався вказівник p, при цьому сам вказівник існує і можна ним користуватися. Щодо небезпек, ось приклад як втратити доступ до виділеної пам’яті.

// Неправильний код!!!!

int *p = new int;

int a = 5;

p = &a;

// Немає delete

В кінці змінено адресу вказівника p, тепер він посилається на змінну a, а не на виділену пам’ять. І до того ж немає оператора delete.

Наше завдання: помножити два числа, взагалі не використовуючи змінних.

Це доволі просто, спробуйте самі.

#include <iostream>
#include <iomanip>
#include <vector>
#include <time.h>
#include <cstdlib>
#include <conio.h>

using namespace std;

int main ()
{
	double *a = newdouble;
	double *b = newdouble;

	cin>> *a >> *b;
	
	cout<< "result = " << *a * *b <<endl;

	delete a;
	delete b;

	_getch();
	return  0;
}

Так, тепер поговоримо про масиви довільного розміру, це там значення розміру не повинно бути константою. Для прикладу створимо масив цілих чисел з 10 елементів.

int *ar  = new int [10];

І якщо є new, треба якось звільнити пам’ять, але для цілого масиву. Це робиться так

delete [] ar;

Все J

Давайте ще один приклад, простенький. Знайдемо суму всіх елементів масиву заповненого випадковими числами від -78 до 78.

#include  <iostream>
#include  <iomanip>
#include  <vector>
#include  <time.h>
#include  <cstdlib>
#include  <conio.h>
#include  <cstring>
#include  <cmath>
#include  <algorithm>
#include  <ctime>
using namespace std;

int main ()
{
	system ("color A");
	srand(time(NULL));

	int size;	// Не константа

	cin >> size;
	int *a = new int [size];

	for (int i=0; i<size; ++i)
	{
		a[i] = -78 + rand() % 156;
		cout << a[i] << "\t";
	}
	cout << endl;

	int *sum = new int (0);	// Ініціалізація нулем

	for (int i=0; i<size; ++i)
		*sum += a[i];

	cout << "res " << *sum << endl;

	delete [] a;
	delete sum;

	_getch();
	return  0;
}


Це все можна реалізувати і без використання змінних взагалі, що б ви подумали якби вам таке сказали декілька уроків тому?

 

Вказівники на вказівники

Звучить погрозливо, але тут нічого складного немає. Якщо звичайний вказівник зберігає адресу змінної, то вказівник на вказівник зберігає адресу вказівника, що вказує на змінну. Щоб отримати значення змінної за допомогою такого вказівника потрібно його розіменувати двічі. Наприклад така програма:

#include  <iostream>
#include  <conio.h>
using namespace std;

int main ()
{
	int a = 5;
	int *p = &a;
	int **p2 = &p;	// &p != &a   p = &a


	cout << "a = " << a << endl;
	cout << "*p = " << *p << endl;
	cout << "**p2 = " << **p2 << endl;

	_getch();
	return  0;
}

Важливо пам’ятати, що адреса вказівника не така сама як і адреса змінної на яку він посилається. Просто у частині пам’яті з його ім’ям зберігається адреса іншого об’єкта.

 

Домашнє завдання

1. Написати калькулятор без використання змінних
2. Знайти факторіал числа використовуючи тільки вказівники
3. За допомогою посилань на функції знайти суму трьох максимальних значень масиву. Розмір масиву задає користувач. Заповнення випадковими числами.
4. Підрахувати суму перших 20 натуральних чисел використовуючи вказівник на вказівник.


( 13 Проголосувало )

Схожі статті:
Новіші матеріали:
Старіші матеріали:

Коментарі
Добавити новий
Залишити коментар
Ім`я:
e-mail:
 
Тема:
 
:angry::0:confused::cheer:B):evil::silly::dry::lol::kiss::D:pinch:
:(:shock::X:side::):P:unsure::woohoo::huh::whistle:;):s
:!::?::idea::arrow:
 
Введіть цей настирливий код
Русская редакция: www.freedom-ru.net & www.joobb.ru

3.26 Copyright (C) 2008 Compojoom.com / Copyright (C) 2007 Alain Georgette / Copyright (C) 2006 Frantisek Hliva. All rights reserved."

 

Підписка

Хто онлайн?

Немає
На даний момент 16 гостей на сайті

Український рейтинг
TOP.TOPUA.NET