упражнение
№2
Указатели
и динамични
променливи
При стартиране на дадена програмна единица в паметта (за подпрограмите – в стека) се заделя място за локалните и променливи. За глобалните променливи се заделя памет при стартирането на програмата. Казаното до тук се отнася за статичните променливи. Това са тези променливи, които са описани с тип и име преди да бъдат използвани. Техният брой е известен по време на компилацията на програмата. Има обаче и случаи, в които броя на променливите се определя по време на изпълнение и варира в твърде широки граници, за да бъде целесъобразно да се ангажира памет за всички възможни случаи през цялото време на работа на съответната програмна единица. Например ако изграждате свързан списък от структури, трябва да заделяте нова памет всеки път, когато добавяте нова структура., ако четете файл в паметта, обемът необходима памет зависи от размера на файла и т. н. В такива случаи се употребяват динамични променливи.
Терминът
“Динамична
променлива”
се използва
когато
изискванията
ви към
паметта могат
да се
променят по
време на
изпълнението
на
програмата.
Динамичната
променлива
обикновено
няма име. Тя
се създава в
момента,
когато е
нужна и се унищожава
веднага щом
стане
излишна. За
създаването,
обработката
и
унищожаването
на динамичните
променливи
се използват
указатели.
С++
предоставя
два подхода,
свързани с
динамичната
памет. Можем
да
използваме
функциите malloc и free от
стандартната
библиотека
на С или
стандартните
оператори new и delete
които се
поддържат
само от С++.
Променливите
от тип
указател не
съхраняват
данни, които
програмата
обработва, а
адреси на
данни (на
статични или
динамични
променливи) С
други думи
във всяка
променлива
указател е
записан
адрес на
клетка от
оперативната
памет, където
се намира
стойност на някоя
променлива.
Размерът на
заеманата от всички
указатели
памет е един
и същи и се определя
от обема на
оперативната
памет на компютъра,
но въпреки
това всеки
указател има
тип – типа на
променливата,
чийто адрес съхранява.
Този тип е от
определщо
значение при
обработката
на
стойността,
която указателят
сочи, а също
така и при
евентуалното
определяне
адреса на
следващата
променлива
от същия тип.
Указател
се декларира
със символа * пред
името си:
int * pointer1;
float * p2;
int *pnum= #
Указателите
в първите два
примера не са
инициализирани,
т. е. не е
определено
към кои клетки
от паметта
сочат. В
такива
случаи стоиността
им е NULL ( не сочат
никъде) ако
са глобални
или е неопределена,
ако са
локални. За
това
указателите
непременно
трябва да
бъдат
инициализирани
или да им се
присвои
адрес на
променлива
преди да
бъдат
използвани.
В
последния пример
указателят е
инициализиран,
т.е. указано е
към коя точно
клетка от
оперативната
памет сочи. (Това
разбира се
записва
адреса на
тази клетка в
променливата
- указател, но
не записва
нищо в самта
променлива.
Нейната стойност
може да е
била
определенa
още преди свързването
и с указателя,
или да се
определи
след това
чрез присвояване
на стойност
на
съдържанието
на указател : *pnum=23; )
Инициализирането
на указател
може да се извърши
и чрез
създаване на
динамична
променлива: pnum= new int;
Операциите
new и delete се
използват за
създаване и
унищожаване
на динамични
променливи. Операцията new
има следния
синтаксис:
<Име
на
променлива
–указател> = new <име на тип
данни>;
Тя
заделя памет
за динамична
променлива
от указания
след new тип и
записва
адреса и в
указателя в
лявата
страна на оператора
за
присвояване
(ако
операционната
система не
предостави
необходимата
памет, в
указателя се
записва NULL).
Променливата
– указател и
създадената
динамична
променлива
трябва да са
от един и същи
тип.
Операцията
delete има
следния
синтаксис:
delete
<име на
указател>;
Тя
унищожава
динамична
променлива, сочена от
указания и
освобождава
заеманата от
нея памет.
Примери:
Int *p1, *p2;
p1 = new int;
p2 = new int[500];
int *p3=new int ;
delete p1;
delete [] p2; // за да се
унищожи
масив, delete трябва да
бъде
последван от []
указателят
р1 сочи
динамична
променлива от
тип int, която
няма име. Към
нея можем да
се обърнем, като
използваме
оператора за
препращане *
*p1=23;
// в
неименованото
поле, сочено
от указателя р1
се записва
числото 23.
Това не е
операция с
указателя, а
с неговото
съдържание.
Ако
една
динамична
променлива е
създадена с new,
то тя трябва
да бъде
унищожена с delete.
Динамично
заделената
памет не се
освобождава
при излизане
от
съответния
блок, нито при
прекратяване
изпълнението
на програмата.
За това
трябва
винаги,
когато
дадена динамична
променлива
вече не е
нужна, да се
унищожава с delete.
Операции
с укаазтели:
1)
присвояване
-
стойността
на друг
указател от
същия тип
-
адреса
на статична
променлива
от типа на указателя
( чрез
адресния
оператор &)
-
стойността
0 (NULL)
2)
сравнения
за равенство
или
неравенство
с друг
указател от
същия тип или
с NULL.
3)
събиране
и изваждане
на указатели
с числа от
тип integer
Резултатът
от тези
операции е
адресен израз.
Преди да се
събере или
извади с
указателя,
цялото число
се умножава
автоматично
с размера на
базовия му
тип. Могат да
се използват
и операторите
++ , -- , += , -= .
Пример:
int *ptr;
..... // нека в даден
момент ptr сочи
клетка с
адрес 1000.
Тогава ptr++ ще
сочи клетка //с адрес 1000+1*2
=1002.
4)
разлика
на два
указателя от
един и същи
тип е цяло число,
което се
получава,
като първо се
извадят
адресите,
записани в
указателите
и след това
полученото
число се
раздели на
размера на
базовия им
тип. При
работа с
масиви
разликата
между указателите
към два елемента
на един и
същи масив
представлява
броя
на
елементите,
разположени
между тях плюс
едно.
Приложение
на
указателите:
-
за
предаване на
параметри
към функции
по адрес
-
за
реализацията
на
символните
низове
-
за
работа с
масиви
-
за
работа с
динамични
обекти (списъци,
опашки,
стекове,
двоични
дървета и др.)
Задача: Да се
напише
програма,
която
генерира три
динамични
масива от
реални числа a, b, c,
въвежда
данни в
масивите a и b и намира и
извежда
масива с като
c[i]=a[i]-b[i].
#include <iostream.h>
int n;
int i;
void main(void)
{
//
деклариране
на указатели
към
динамичните
масиви
float *a, *b, *c;
cout<<"n =
";
cin>>n;
// запазване
на памет за
елементите
на динамичните
масиви (инициализиране
на
указателите)
a=new float[n];
b=new
float[n];
c=new
float[n];
cout<<"Vuvedete
stojnostite na Masiva а "<<'\n';
for(i=0;i<n;i++)
{
cout<<"a["<<i<<"]
= ";
cin>>*(а+i);
}
cout<<"Vuvedete stojnostite na Masiva b "<<'\n';
for(i=0;i<n;i++)
{
cout<<"b["<<i<<"]
= ";
cin>>*(b+i);
}
cout<<"izvegdane na stojnostite na Masiva c "<<'\n';
for(i=0;i<n;i++)
{
*(c+i)=*(a+i)-*(b+i);
cout<<"b["<<i<<"]
= "<<*(c+i);
}
delete []a,b,c;
}
Решете
същата
закача със
статични
масиви. Направете
сравнение.
Задачи
за
упражнение:
В
долните
задачи
масивите да
се генерират
динамично и
при обработката
им да се
използват
указатели
1 Даден е
масив А от N
цели числа (N£100). Да се
намери
средноаритметичната
стойност на
всички
елементи на
масива, които
са по-големи
от
предварително
зададено
число K и по-малки
от
предварително
зададено
число M.
2 Даден е
масив А от N
цели числа (N£100). Да се
намери
средноаритметичната
стойност на
всички
елементи на
масива, които
са четни
числа.
3 Даден е
целочислен
едномерен
масив A от N
елемента (N£100) и
целочислен
едномерен
масив B от N
елемента. Да
се генерира
масив C от N
елемента, за
който: C[I]=K*A[I]-P*B[I] . K и P са
цели числа,
които се
въвеждат от
клавиатурата.
4
Да се
въведат
стойности за
елементите
на едномерен
масив от N
цели числа (N£100). Да се
намери
минималният
елемент на
масива и
неговият индекс.