#адреса в памяти

Сначала нужно понят что каждая переменная имеет свой адрес в памяти компьютера. Что бы понять с чем мы имеем дело, запустим следующий код:

#include <stdio.h>
 
int main(void)
{
    int i = 0;
    printf ("i=%d, &i=%p \\n", i, &i);
    return (0);
}

Если вы запустили данный код то увидели, что значение адреса переменной i у вас другое. Это виртуальный адрес который предоставляет операционная система программе при её запуске. При каждом новом запуске программы, это адрес будет меняться.

#указатели

Указатели как раз работают с этими адресами. Рассмотрим синтаксис:

int *p; // инициализация указателя;

Тут мы говорим что переменная p будет хранить адрес какой-то другой переменной, обязательно типа int по тому что наш указатель *p тоже типа int.

Если указатель слева от знака равно, то в него можно положить адрес с помощью знака амперсанда & (операция взятия адреса)

Так:

int i = 5;
int *p = &i;

или так:

int i = 5;
int *p;
p = &i;

Объявив один раз что p будет указателем, дальше в любом месте кода написав p , мы будем иметь ввиду *p.

Также используя указатель, мы можем менять значение по адресу, который хранится в указателе:

#include <stdio.h>
 
int main(void)
{
    int x = 10;
    int *p = &x;
    *p = 45; // меняем значение в переменной x через указатель p
    printf("x = %d \\n", x);  
    return (0);
}

Если указатель справа от знака равно, то это значит что мы берем значение которое хранится в переменной на которую указывает указатель и записываем это значение в переменную слева от знака равно.