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

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

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

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

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

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

int a = 10;
int *pa = &a;	// указатель pa хранит адрес переменной a

Причем указатель и переменная должны иметь тот же тип, в данном случае int.

Присвоение указателю другого указателя:

#include <stdio.h>

int main(void)
{
	int a = 10;
	int b = 2;
	
	int *pa = &a;
	int *pb = &b;
	
	printf("Variable a: address=%p \t value=%d \n", (void*)pa, *pa);
	printf("Variable b: address=%p \t value=%d \n", (void*)pb, *pb);
	
	pa = pb;	// теперь указатель pa хранит адрес переменной b
	printf("Variable b: address=%p \t value=%d \n", (void*)pa, *pa);
	
	return 0;
}

Когда указателю присваивается другой указатель, то фактически первый указатель начинает также указывать на тот же адрес, на который указывает второй указатель.

Если мы не хотим, чтобы указатель указывал на какой-то конкретный адрес, то можно присвоить ему условное нулевое значение с помощью константы NULL, которая определена в заголовочном файле stdio.h:

int *pa = NULL;

Разыменование указателя

Операция разыменования указателя в виде *имя_указателя, позволяет получить объект по адресу, который хранится в указателе.

#include <stdio.h>

int main(void)
{
	int a = 10;
	
	int *pa = &a;
	int *pb = pa;
	
	*pa = 25;
	
	printf("Value on pointer pa: %d \n", *pa);	// 25
	printf("Value on pointer pb: %d \n", *pb);	// 25
	printf("Value of variable a: %d \n", a);	// 25
	
	return 0;
}

Через выражение *pa мы можем получить значение по адресу, который хранится в указателе pa, а через выражение типа *pa = значение вложить по этому адресу новое значение.

И так как в данном случае указатель pa указывает на переменную a, то при изменении значения по адресу, на который указывает указатель, также изменится и значение переменной a.

Указатель на void

Указатели указывают на данные определенных типов. Например, указатель типа int* указывает на значение типа int, но не может указывать на данные других типов, скажем, на объект типа float. Однако можно также определять указатели типа void*, которые могут указывать на данные любого типа. И неявно указатели любых можно преобразовать в указатель типа void*:

#include <stdio.h>
 
int main(void)
{
    int x = 123;
    int *ip = &x;       // указатель хранит адрес объекта int
    void *vp; 
    vp = ip;            // void-указатель получает адрес из указателя ip
    printf("Value: %d\n", *((int *)vp));    // Value: 123

    return 0;
}

Следует учитывать, что к void-указателю мы НЕ можем применить операцию разыменования и тем самым получить значение под адресу, который хранится в этом указателе. Поэтому для получения значения надо приводить к указателю соответствующего типа:

printf("Value: %d\n", *((int *)vp));

Одно из распространенных применений void-указателя - это вывод адреса на консоль:

#include <stdio.h>

int main(void)
{
    int x = 123;
    int *ip = &x;                   // указатель хранит адрес объекта int
    void *vp = ip;                  // void-указатель получает адрес из указателя ip
    printf("vp: %p\n", vp);         // получаем адрес, который хранится в указателе vp
    printf("ip: %p\n", (void*)ip);  // преобразование к типу void* - получаем адрес из указателя ip

    return 0;
}

Если мы хотим получить адрес из указателя другого типа, то, в соответствии со стандартами, его сначала надо преобразовать к типу void*.

Адрес указателя

Указатель хранит адрес переменной, и по этому адресу мы можем получить значение этой переменной. Но кроме того, указатель, как и любая переменная, сам имеет адрес, по которому он располагается в памяти. Этот адрес можно получить также через операцию &:

int a = 10;
int *pa = &a;
printf("address of pointer=%p \n",  (void*)&pa);  // адрес указателя
printf("address in pointer=%p \n",  (void*)pa);	  // адрес, который хранится в указателе - адрес переменной a			
printf("value on pointer=%d \n", *pa);			  // значение по адресу в указателе - значение переменной a

Операции сравнения

К указателям могут применяться операции сравнения >, >=, <, <=,==, !=. Операции сравнения применяются только к указателям одного типа и константе NULL. Для сравнения используются номера адресов:

int a = 10;
int b = 20;
int *pa = &a;
int *pb = &b;
if(pa > pb)
	printf("pa (%p) is greater than pb (%p) \n",  (void*)pa,  (void*)pb);
else
	printf("pa (%p) is less or equal pb (%p) \n",  (void*)pa,  (void*)pb);

Консольный вывод в моем случае:

pa (0060FEA4) is greater than pb (0060FEA0)

Приведение типов

Иногда требуется присвоить указателю одного типа значение указателя другого типа. В этом случае следует выполнить операцию приведения типов:

char c = 'N';
char *pc = &c;
int *pd = (int *)pc;
printf("pc=%p \n",  (void*)pc);
printf("pd=%p \n",  (void*)pd);
Дополнительные материалы
Помощь сайту
Юмани:
410011174743222
Перевод на карту
Номер карты:
4048415020898850