Указатели в языке Си поддерживают ряд операций: присваивание, получение адреса указателя, получение значения по указателю, некоторые арифметические операции и операции сравнения.
Указателю можно присвоить либо адрес объекта того же типа, либо значение другого указателя или константу 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.
Указатели указывают на данные определенных типов. Например, указатель типа 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);