Website Summary Files Commits Branches @commit @diff @stats
path: root / Seminar01 / README.md
blob: 47f9f8388e550da52fd8165049d00287227934de (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
# Указатели и памет.

## Указатели и референции. Работа с памет.
Указателите, които вече добре познаваме, са променливи, които съхраняват адреса на обект в паметта. Указателите най - често са използвани за:
* Да заделяме динамично памет в heap-а
* Итерираме елементите на масив или друга структура от данни
* Подаваме функции като аргументи на други функции

и много други. 

Указателите са удобен и изключително гъвкав инструмент, с който често ще ни се налага да работим.
Нека разгледаме следния сценарий.
```c++
int main() {
    int a = 10;
    int b = 12;
    int* ptr = &a;
}
```
Искаме да напишем функция, която приема указател и променлива и кара указателя да сочи към адреса на тази променлива. Дали следния код ще свърши работа:
```c++
#include<iostream>

void redirect(int* ptr, int dest) {
    ptr = &dest;
}

int main() {
    int a = 10;
    int b = 12;
    int* ptr = &a;

    redirect(ptr, b);
    std::cout << *ptr;      // *ptr връща стойността записана в този адрес
}
```
Очевидно това не сработи, но защо? Във функцията *redirect* на *ptr* се прави **копие**. После това копие започва да сочи към *dest*, но на нас това не ни помага! Ние искаме да пренасочим **самия указател а не неговото копие**. Тук на помощ идват референциите (или псевдонимите). Ако пренапишем функцията по следния начин
 
```c++
void redirect(int*& ptr, int& dest) {
    ptr = &dest;
}
```
то вече функцията върши желаната работа. Когато приемаме обект по референция **копие не се прави**. Това важи за всички типове - включително и за указателите. 
Можем да правим указатели към всякакви типове включително и към указатели. Типът **int\*\*** не е нищо повече от указател към указател.

Тогава не можем ли просто да вземем указател към указателя и да го променим по този начин? 
```c++
#include<iostream>

void redirect(int** ptr, int& dest) {
    *ptr = &dest;
}

int main() {
    int a = 10;
    int b = 12;
    int* ptr = &a;

    redirect(&ptr, b);          // &ptr връща адреса на указателя, точно това което е int**, адрес на указател.
    std::cout << *ptr;
}
```

Този код също върши работа. Тогава каква е разликата между указател и референция? 
Разликата е, че указателите са по - гъвкави. Не можем да пренасочим референция. Също така не можем да имаме референция която не сочи към обект. При указателите имаме специален тип наречен nullptr, който сочи към "нищото". Често референцията е представяна като указател, който не може да се пренасочва, вика \* вместо теб и винаги сочи към обект.

## const и указателите
За *const* важи следното правило - *const* винаги модифицира това което е в негово ляво, освен когато няма нищо отляво, тогава модифицира това което е отдясно. 

* const int\* -> Указател, който сочи към константен *int*. Можем да пренасочваме, но не можем да променяме стойността. 
* int const\* -> Абсолютно същото.
* int \* const -> Константен указател, не можем да го пренасочваме.
* const int\* const -> Константен указател към константен *int*. Нито можем да пренасочваме нито да променяме стойността.