Указатель - это переменная, в которой хранятся адреса памяти других объектов.
```
#include <stdio.h>
int main(int argc, char *argv[])
{
int i = 2;
int *pi = &i;
int **ppi = π
printf("i = %d, &i = %p\n", i, &i);
printf("pi = %p, &pi = %p\n", pi, &pi);
printf("ppi = %p, &ppi = %p\n", ppi, &ppi);
}
//i = 1, &i = 0x7ffee283d8fc
//pi = 0x7ffee283d8fc, &pi = 0x7ffee283d8f0
//ppi = 0x7ffee283d8f0, &ppi = 0x7ffee283d8e8
```
Это означает, что целое число в переменной i находится по адресу в памяти 0x7ffee283d8fc. Когда мы присвоили pi=&i, то pi получил этот адрес в качестве своего значения. Значение находится по адресу &pi, который равен 0x7ffee283d8f0. Переменная ppi содержит адрес pi, 0x7ffee283d8f0, и сама находится по адресу 0x7ffee283d8e8.
![Структура данных буферного кэша](https://whoisdeveloper.ru/static/img/pointers7.png)
### Вызов по ссылке
```
#include <stdio.h>
void mutate(int *ip)
{
*ip += 42;
}
int main(void)
{
int j = 0;
mutate(&j);
printf("j = %d\n", j);
}
// j = 42
```
Gеременная ip внутри вызова функции содержит адрес j, а не ее значение. Это все еще локальная переменная, в стеке по-прежнему находится объект, содержащий его память, но локальная переменная является указателем на j. Если мы разыменовываем ip, мы смотрим на память, где находится j, и если мы изменяем память там, то мы изменяем память, которую хранит j. Итак, с помощью этой функции мы меняем j. С помощью указателя у вас есть доступ к памяти в вызывающей функции, а не только к памяти переменной в вызываемом объекте. Вы получаете ссылку на объект вместо его значения — это вызов по ссылке.
Если вы хотите изменить указатель, то вам нужен указатель на указатель:
```
#include <stdio.h>
void mutate(int **ipp)
{
**ipp = 42;
}
int main(void)
{
int j = 0;
int *p = &j;
mutate(&p);
printf("j = %d\n", j);
}
```
Допустим, нам нужно написать программу, которая манипулирует точками.
```
#include <stdio.h>
typedef struct point
{
double x, y;
} point;
void move_hor(point *p, double amount)
{
p->x += amount;
}
void move_vert(point *p, double amount)
{
p->y += amount;
}
void move(point *p, double d_x, double d_y)
{
move_hor(p, d_x);
move_vert(p, d_y);
}
void get_cord(point *p)
{
printf("point <%2f, %2f>\n", p->x, p->y);
}
int main(int argc, char *argv[])
{
point p = {.x = 0.0, .y = 0.0};
get_cord(&p);
move(&p, 10, 10);
get_cord(&p);
}
//point <0.000000, 0.000000>
//point <10.000000, 10.000000>
```
### Нулевые указатели (null и nullptr)