Операции с указателями

Последнее обновление: 24.02.2023

Указатели поддерживают ряд операций: присваивание, получение адреса указателя, получение значения по указателю, некоторые арифметические операции и операции сравнения.

Присваивание адреса

Указателю можно присвоить адрес объекта того же типа, либо значение другого указателя. Для получения адреса объекта используется операция &:

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*.

Помощь сайту
Юмани:
410011174743222
Перевод на карту
Номер карты:
4048415020898850