Указатели поддерживают ряд операций: присваивание, получение адреса указателя, получение значения по указателю, некоторые арифметические операции и операции сравнения.
Указателю можно присвоить адрес объекта того же типа, либо значение другого указателя. Для получения адреса объекта используется операция &:
int a {10}; int *pa {&a}; // указатель pa хранит адрес переменной a
При этом указатель и переменная должны иметь один и тот же тип, в данном случае это тип int.
Операция разыменования указателя представляет выражение в виде *имя_указателя
. Эта операция позволяет получить объект по адресу, который хранится в указателе.
#include <iostream> int main() { int a {10}; int *pa {&a}; // хранит адрес переменной a std::cout << "*pa = " << *pa << std::endl; // *pa = 10 std::cout << "a = " << a << std::endl; // a = 10 *pa = 25; // меняем значение по адресу в указателе std::cout << "*pa = " << *pa << std::endl; // *pa = 25 std::cout << "a = " << a << std::endl; // a = 25 }
Через выражение *pa
мы можем получить значение по адресу, который хранится в указателе pa
, а через выражение типа
*pa = значение
вложить по этому адресу новое значение.
И так как в данном случае указатель pa
указывает на переменную a
, то при изменении значения по адресу, на который указывает указатель, также изменится и значение
переменной a
.
Присвоение указателю другого указателя:
#include <iostream> int main() { int a {10}; int b {2}; int *pa {&a}; // указатель на переменную a int *pb {&b}; // указатель на переменную b std::cout << "pa: address=" << pa << "\t value=" << *pa << std::endl; std::cout << "pb: address=" << pb << "\t value=" << *pb << std::endl; pa = pb; // теперь указатель pa хранит адрес переменной b std::cout << "pa: address=" << pa << "\t value=" << *pa << std::endl; *pa = 125; // меняем значение по адресу в указателе pa std::cout << "b value=" << b << std::endl; }
Когда указателю присваивается другой указатель, то фактически первый указатель начинает также указывать на тот же адрес, на который указывает второй указатель:
pa: address=0x56347ffc5c value=10 pb: address=0x56347ffc58 value=2 pa: address=0x56347ffc58 value=2 b value=125
Нулевой указатель (null pointer) - это указатель, который не указывает ни на какой объект. Если мы не хотим, чтобы указатель указывал на какой-то конкретный адрес, то можно присвоить ему условное нулевое значение. Для определения нулевого указателя можно инициализировать указатель нулем или константой nullptr:
int *p1{nullptr}; int *p2{};
Так как ссылка не является объектом, то нельзя определить указатель на ссылку, однако можно определить ссылку на указатель. Через подобную ссылку можно изменять значение, на которое указывает указатель или изменять адрес самого указателя:
#include <iostream> int main() { int a {10}; int b {6}; int *p{}; // указатель int *&pRef {p}; // ссылка на указатель pRef = &a; // через ссылку указателю p присваивается адрес переменной a std::cout << "p value=" << *p << std::endl; // 10 *pRef = 70; // изменяем значение по адресу, на который указывает указатель std::cout << "a value=" << a << std::endl; // 70 pRef = &b; // изменяем адрес, на который указывает указатель std::cout << "p value=" << *p << std::endl; // 6 }
Указатель хранит адрес переменной, и по этому адресу мы можем получить значение этой переменной. Но кроме того, указатель, как и любая переменная, сам имеет адрес, по которому он располагается в памяти. Этот адрес можно получить также через операцию &:
int a {10}; int *pa {&a}; std::cout << "address of pointer=" << &pa << std::endl; // адрес указателя std::cout << "address stored in pointer=" << pa << std::endl; // адрес, который хранится в указателе - адрес переменной a std::cout << "value on pointer=" << *pa << std::endl; // значение по адресу в указателе - значение переменной a
К указателям могут применяться операции сравнения >, >=, <, <=,==, !=. Операции сравнения применяются только к указателям одного типа. Для сравнения используются номера адресов:
#include <iostream> int main() { int a {10}; int b {20}; int *pa {&a}; int *pb {&b}; if(pa > pb) std::cout << "pa (" << pa << ") is greater than pb ("<< pb << ")" << std::endl; else std::cout << "pa (" << pa << ") is less or equal pb ("<< pb << ")" << std::endl; }
Консольный вывод в моем случае:
pa (0xa9da5ffdac) is greater than pb (0xa9da5ffda8)
Иногда требуется присвоить указателю одного типа значение указателя другого типа. В этом случае следует выполнить операцию приведения типов с помощью операции (тип_указателя *)
:
#include <iostream> int main() { char c {'N'}; char *pc {&c}; // указатель на символ int *pd {(int *)pc}; // указатель на int void *pv {(void*)pc}; // указатель на void std::cout << "pv=" << pv << std::endl; std::cout << "pd=" << pd << std::endl; }
Для преобразования указателя к другому типу в скобках перед указателем ставится тип, к которому надо преобразовать. Причем если мы не можем просто создать объект, например, переменную типа void, то для указателя это вполне будет работать. То есть можно создать указатель типа void.
Кроме того, следует отметить, что указатель на тип char (char *pc {&c}
) при выводе на консоль система интерпретирует как строку:
std::cout << "pc=" << pc << std::endl;
Поэтому если мы все-таки хотим вывести на консоль адрес, который хранится в указателе типа char, то это указатель надо преобразовать к другому типу, например, к void* или к int*.