Указатели в языке Си.



Указатель — переменная, содержащая адрес объекта. Указатель не несет информации о содержимом объекта, а содержит сведения о том, где размещен объект.

Указатели похожи на метки, которые ссылаются на места в памяти. Они тоже имеют адрес, а их значение является адресом некоторой другой переменной. Переменная, объявленная как указатель, занимает 4 байта в оперативной памяти (в случае 32-битной версии компилятора).

Синтаксис указателей

тип *ИмяОбъекта;

Тип указателя— это тип переменной, адрес которой он содержит. Для работы с указателями в Си определены две операции:

  • операция * (звездочка) — позволяет получить значение объекта по его адресу — определяет значение переменной, которое содержится по адресу, содержащемуся в указателе;
  • операция & (амперсанд) — позволяет определить адрес переменной.

Например:

сhar c;   // переменная
char *p; // указатель
p = &c;  // p = адрес c

Объявление указателя, получение адреса переменной

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

int var = 5; // простое объявление переменной с предварительной инициализацией
int *ptrVar; // объявили указатель, однако он пока ни на что не указывает 
ptrVar = &var; // теперь наш указатель ссылается на адрес в памяти, где хранится число 5

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

Указатель хранит адрес области памяти. Можно создать указатель на указатель, тогда он будет хранить адрес указателя и сможет обращаться к его содержимому. Указатель на указатель определяется как:

<тип> **<имя>;

Пример работы указателя на указатель:

#include <conio.h>
#include <stdio.h>
   
#define SIZE 10
 
void main() {
    int A;
    int B;
    int *p;
    int **pp;
 
    A = 10;
    B = 111;
    p = &A;
    pp = &p;
 
    printf("A = %d\n", A);
    *p = 20;
    printf("A = %d\n", A);
    *(*pp) = 30;    //здесь скобки можно не писать
    printf("A = %d\n", A);
 
    *pp = &B;
    printf("B = %d\n", *p);
    **pp = 333;
    printf("B = %d", B);
 
    getch();
}

Указатели и приведение типов

Так как указатель хранит адрес, можно кастовать его до другого типа. Это может понадобиться, если нужно взять часть переменной, или если мы знаем, что переменная хранит нужный нам тип.

В следующем примере мы пользуемся тем, что размер типа int равен 4 байта, а char 1 байт. За счёт этого, получив адрес первого байта, можно пройти по остальным байтам числа и вывести их содержимое.

#include <conio.h>
#include <stdio.h>
   
#define SIZE 10
 
void main() {
    int A = 10;
    int *intPtr;
    char *charPtr;
 
    intPtr = &A;
    printf("%d\n", *intPtr);
    printf("--------------------\n");
    charPtr = (char*)intPtr;
    printf("%d ", *charPtr);
    charPtr++;
    printf("%d ", *charPtr);
    charPtr++;
    printf("%d ", *charPtr);
    charPtr++;
    printf("%d ", *charPtr);
     
    getch();
}

NULL pointer — нулевой указатель

Указатель до инициализации хранит мусор, как и любая другая переменная. Но в, то, же время, этот «мусор» вполне может оказаться валидным адресом. Например, есть указатель. Каким образом узнать, инициализирован он или нет? В общем случае никак. Для решения этой проблемы был введён макрос NULL библиотеки stdlib.

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

int *ptr = NULL;

По стандарту гарантировано, что в этом случае указатель равен NULL, и равен нулю, и может быть использован как булево значение false. Хотя в зависимости от реализации NULL может и не быть равным 0. То есть указатель можно сравнивать с нулём, или с NULL, но нельзя NULL сравнивать с переменной целого типа или типа с плавающей точкой.

1 Звезда2 Звезды3 Звезды4 Звезды5 Звезд (Пока оценок нет)
Загрузка...

Добавить комментарий

Ваш e-mail не будет опубликован. Обязательные поля помечены *