Все данные, которыми оперирует программа, хранятся в ячейках памяти, каждая из которых имеет свой уникальный адрес. Переменные, в которых можно хранить эти адреса называются - указателями. Другими словами, указатель - это переменная, которая хранит адрес другого объекта (переменой, константы, указателя) или функции. Указатели - являются неотъемлемым компонентом для управления памятью в языке Си.
Определение указателя
Для определения указателя надо указать тип объекта, на который указывает указатель, и символ звездочки *.
тип_данных* название_указателя;
Сначала идет тип данных, на который указывает указатель, и символ звездочки *. Затем имя указателя.
Например, определим указатель на объект типа int:
int *p;
Пока указатель не ссылается ни на какой объект. Теперь присвоим ему адрес переменной:
int main( void )
{
int x = 10; // определяем переменную
int *p; // определяем указатель
p = &x; // указатель получает адрес переменной
return 0;
}
Получение адреса данных
Для получения адреса к переменной применяется операция & (!!! Это не логическая "И" !!!). Эта операция применяется только к таким объектам, которые хранятся в памяти компьютера, то есть к переменным и элементам массива.
ВАЖНО !!! Указатель и переменная должны быть одного типа !!!
Для вывода значения указателя можно использовать специальный спецификатор %p. Пример:
#include <stdio.h>
int
main(
void
)
{
int
x = 10;
int
*p;
p = &x;
printf
(
"%p \n"
, p);
return
0;
}
Адресом данных является адрес первой ячейки памяти в шестнадцатеричном формате. Например, в памяти компьютера есть адрес 0x0060FEA8, по которому располагается переменная x. Так как переменная x представляет тип int, то на большинстве архитектур она будет занимать следующие 4 байта (на конкретных архитектурах размер памяти для типа int может отличаться). Таким образом, переменная типа int последовательно займет ячейки памяти с адресами 0x0060FEA8, 0x0060FEA9, 0x0060FEAA, 0x0060FEAB.
Стоит отметить, что при выводе адреса указателя функция printf() ожидает, что указатель будет представлять void*, то есть указатель на значение типа void. Поэтому некоторые компиляторы могут отображать предупреждения. Чтобы было все канонически правильно переданный указатель нужно преобразовать в указатель типа void *:
printf
(
"%p \n"
, (
void
*)p);
Получение значения по адресу
Так как указатель хранит адрес, то мы можем по этому адресу получить хранящееся там значение. Для этого применяется операция * или операция разыменования (dereference operator). Результатом этой операции всегда является объект, на который указывает указатель. Применим данную операцию и получим значение переменной x:
#include <stdio.h>
int
main(
void
)
{
int
x = 10;
int
*p;
p = &x;
printf
(
"Address = %p \n"
, (
void
*) p);
printf
(
"x = %d \n"
, *p);
return
0;
}
Графическое пояснение операций * и &
Указатели можно объявлять не только на переменные, но и на другие указатели:
#include <stdio.h>
int main()
{
int a = 77;
int *ptrA = &a;
int** ppA = &ptrA;
*ptrA = 88;
printf("%d\n",a);// << std::endl; // 88
**ppA = 99;
printf("%d\n",a);// << std::endl; // 99
return 0;
}
Комментарии ()