Указатель -- это переменная, которая хранит адрес другой переменной. Если переменная имеет тип ``int``, то указатель имеет тип ``int*`` (т.е. добавляется ``*``).
Для получения значения переменной, на которую указывает указатель, используется унарная операция ``*`` (*разыменования*), а для получения адреса переменной -- унарная операция ``&``.
```run-c
#include <stdio.h>
int main() {
int x=10;
int* ptr_x=&x; // сохраняем адрес x
printf("%p\n", ptr_x); // печать адреса
printf("%d\n", *ptr_x); // получение значения по адресу
++x; // x=11
printf("%d\n", *ptr_x); // получение значения по адресу
}
```
Чтобы показать, что указатель никуда не указывает, ему можно присвоить значение 0 (nullptr в С++/C23). Перед разыменованием указателя можно проверить, указывает ли он на что-то. Это не гарантирует, что адрес правильный, так как вы могли не инициализировать указатель (мусор) или там окажется адрес уже не существующей переменной, возможно ошибки выполнения не будет, но это считается undefined behavoir (UB).
```run-c
#include <stdio.h>
int main() {
int* ptr=0; // UB без =0
{ int x=10;
// ptr=&x; // UB при обращении к *ptr вне блока
}
if(ptr) // OK, не нулевой
printf("%d\n", *ptr);
else
printf("nullptr\n");
// printf("%d\n", *ptr); // ошибка выполнения при =0
}
```
Имя массива является константным указателем на первый элемент массива (т.е. сам указатель изменять нельзя, но можно изменять значение, на которое он указывает). Можно определить вспомогательный указатель и перемещать его по массиву (при этом нужно убедиться, что указатель не вышел за левую или правую границу массива).
```run-c
#include <stdio.h>
int main() {
int arr[10]={0}; // все нули
// int *const arr=&arr[0];
*arr=5;
int *ptr=arr; // ptr=&arr[0];
++ptr; // ptr=&arr[1];
*ptr=6;
ptr+=4; // ptr=&arr[5];
*ptr=7;
// печать массива
for(int i=0;i<10;++i)
printf("%d ",arr[i]);
printf("\n");
// тоже самое через указатель
for(ptr=arr;ptr!=arr+10;++ptr)
printf("%d ", *ptr);
printf("\n");
}
```
Адреса имеют не только переменные, но и функции, скомпилированный код которых хранится в памяти компьютера.
Можно определить указатель, который хранит адрес функции:\
``double (*f)(double);``\
Скобки вокруг ``(*f)`` обязательны, так как строка\
``double *f(double);``\
является заголовком функции, которая возвращает результат типа ``double*``
Имя функции (как и имя массива) является константным указателем на функцию. Например,
```c
f=sin; // f указывает на функцию sin
double x=f(0.5); // вызываем sin(0.5);
f=trunc;
x=f(1.5); // вызываем trunc(0.5);
```