Операции с указателями
Указатели поддерживают ряд операций: присваивание, получение адреса указателя, получение значения по указателю, некоторые арифметические операции и операции сравнения.
Присваивание
Указателю можно присвоить либо адрес объекта того же типа, либо значение другого указателя.
Присвоение указателю адреса уже рассматривалось в прошлой теме. Для получения адреса объекта используется операция &:
1
2
int a = 10;
int *pa = &a; // указатель pa хранит адрес переменной a
При этом указатель и переменная должны иметь один и тот же тип, в данном случае это тип int.
Присвоение указателю другого указателя:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
#include <iostream>
using std::cout;
using std::endl;
int main()
{
int a = 10;
int b = 2;
int *pa = &a;
int *pb = &b;
cout << "Variable a: address=" << pa << "\t value=" << *pa << endl;
cout << "Variable b: address=" << pb << "\t value=" << *pb << endl;
pa = pb; // теперь указатель pa хранит адрес переменной b
cout << "Variable b: address=" << pa << "\t value=" << *pa << endl;
return 0;
}
Когда указателю присваивается другой указатель, то фактически первый указатель начинает также указывать на тот же адрес, на который указывает второй указатель.
Нулевые указатели
Нулевой указатель (null pointer) - это указатель, который не указывает ни на какой объект. Если мы не хотим, чтобы указатель указывал на какой-то конкретный адрес, то можно присвоить ему условное нулевое значение. Для создания нулевого указателя можно применять различные способы:
1
2
3
int *p1 = nullptr;
int *p2 = NULL;
int *p3 = 0;
Ссылки на указатели
Так как ссылка не является объектом, то нельзя определить указатель на ссылку, однако можно определить ссылку на указатель. Через подобную ссылку можно изменять значение, на которое указывает указатель или изменять адрес самого указателя:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
#include <iostream>
int main()
{
int a = 10;
int b = 6;
int *p = 0; // указатель
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
return 0;
}
Разыменование указателя
Операция разыменования указателя представляет выражение в виде *имя_указателя. Эта операция позволяет получить объект по адресу, который хранится в указателе.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
#include <iostream>
using std::cout;
using std::endl;
int main()
{
int a = 10;
int *pa = &a;
int *pb = pa;
*pa = 25;
cout << "Value on pointer pa: " << *pa << endl; // 25
cout << "Value on pointer pb: " << *pb << endl; // 25
cout << "Value of variable a: " << a << endl; // 25
return 0;
}
Через выражение *pa мы можем получить значение по адресу, который хранится в указателе pa, а через выражение типа *pa = значение вложить по этому адресу новое значение.
И так как в данном случае указатель pa указывает на переменную a, то при изменении значения по адресу, на который указывает указатель, также изменится и значение переменной a.
Адрес указателя
Указатель хранит адрес переменной, и по этому адресу мы можем получить значение этой переменной. Но кроме того, указатель, как и любая переменная, сам имеет адрес, по которому он располагается в памяти. Этот адрес можно получить также через операцию &:
1
2
3
4
5
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
Операции сравнения
К указателям могут применяться операции сравнения >, >=, <, <=,==, !=. Операции сравнения применяются только к указателям одного типа и к значениям NULL и nullptr. Для сравнения используются номера адресов:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#include <iostream>
using std::cout;
using std::endl;
int main()
{
int a = 10;
int b = 20;
int *pa = &a;
int *pb = &b;
if(pa > pb)
cout << "pa (" << pa << ") is greater than pb ("<< pb << ")" << endl;
else
cout << "pa (" << pa << ") is less or equal pb ("<< pb << ")" << endl;
return 0;
}
Консольный вывод в моем случае:
pa (0x60fe94) is greater than pb (0x60fe90)
Приведение типов
Иногда требуется присвоить указателю одного типа значение указателя другого типа. В этом случае следует выполнить операцию приведения типов с помощью операции (тип_указателя *):
1
2
3
4
5
6
7
8
9
10
11
12
13
#include <iostream>
int main()
{
char c = 'N';
char *pc = &c;
int *pd = (int *)pc;
void *pv = (void*)pc;
std::cout << "pv=" << pv << std::endl;
std::cout << "pd=" << pd << std::endl;
return 0;
}
Для преобразования указателя к другому типу в скобках перед указателем ставится тип, к которому надо преобразовать. Причем если мы не можем просто создать объект, например, переменную типа void, то для указателя это вполне будет работать. То есть можно создать указатель типа void.
Кроме того, следует отметить, что указатель на тип char (char *pc = &c) при выводе на консоль система интерпретирует как строку:
1
std::cout << "pc=" << pc << std::endl;
Поэтому если мы все-таки хотим вывести на консоль адрес, который хранится в указателе типа char, то это указатель надо преобразовать к другому типу, например, к void* или к int*.