Указатель - это переменная, в которой хранятся адреса памяти других объектов. ``` #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)