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