Указатель (тип данных)

Материал из Википедии — свободной энциклопедии
Перейти к: навигация, поиск
переменная num объявляется и инициализируется. После чего объявляется переменная-указатель pNum. Затем указателю pNum присваивается адрес переменной num. Таким образом обе переменные можно использовать для доступа к одному и тому же месту в памяти.

Указатель (англ. pointer) — переменная, диапазон значений которой состоит из адресов ячеек памяти или специального значения — нулевого адреса. Последнее используется для указания того, что в данный момент там ничего не записано.

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

Область применения[править | править вики-текст]

Указатели применяются в двух сферах:

  • использования выгоды косвенной адресации (как в языках ассемблера). Одной из выгод можно назвать экономию памяти. Делая указатель на файл — мы читаем его из памяти, а не загружаем в ОЗУ. Передавая указатель на переменную в функцию мы не делаем копию этой переменной и редактируем её напрямую[1]. Указатели используют для хранения адресов точек входа для так называемых подпрограмм в процедурном программировании и для подключения динамических подключаемых библиотек.
  • методы динамического управления памятью. В таком случае выделяется место в так называемой куче (динамической памяти), а переменные для которых выделили так память называются динамическими[2]. В языке Си нет понятия строковой переменной, так что для строк часто используют указатель на массив символов.

Действия над указателями[править | править вики-текст]

Языки программирования, в которых предусмотрен тип указателей, содержат, как правило, две основные операции над ними: присваивание и разыменование. Первая из этих операций присваивает указателю некоторый адрес. Вторая служит для обращения к значению в памяти, на которое указывает указатель. Разыменование может быть явным и неявным, в большинстве современных языков программирования разыменование происходит только при явном указании.

Пример указателей на языке Си:

int  n  = 6;    // Объявление переменной n типа int и присваивание ей значения 6
int *pn = malloc( sizeof ( int ) ); // Объявления указателя pn и выделение под него памяти.
    *pn = 5;    // Разыменование указателя и присваивание значения 5.
     n = *pn;   // Присвоить n то значение (5), на которое указывает pn.
     free(pn);  // Освободить занятую память.
     pn = &n;   // Присваивает указателю pn адрес переменной n(указатель будет ссылаться на n).
     n = 7;     // *pn тоже стало равно 7

Существует понятие унарной операции (&). Унарная операция & — выдает адрес объекта. Применима только к переменным и элементам массива. pX = &x;. Унарная операция * — рассматривает свой операнд как адрес конечной цели и обращается по этому адресу, чтобы извлечь содержимое.

int sourceNum1 = 100;
int sourceNum2 = 200;
int* pNum1 = &sourceNum1;
int* pNum2 = &sourceNum2;
printf(“Pointer value of 1-%d, 2-%d\n”, *pNum1, *pNum2);  
pNum1 = pNum2;
printf(“Pointer value of 1-%d, 2-%d\n”, *pNum1, *pNum2);

В случае, если указатель хранит адрес какого-либо объекта, то говорят, что указатель ссылается или указывает на этот объект.

Языки, предусматривающие использование указателей для управления динамической памятью, должны содержать оператор явного размещения переменных в памяти. В некоторых языках помимо этого оператора предусмотрен ещё и оператор явного удаления переменных из памяти. Обе эти операции часто принимают форму встроенных подпрограмм (функции malloc и free в Си, операторы new и delete в C++ и т. п.). При использовании простого, а не умного указателя следует всегда своевременно удалять переменную из памяти, дабы избежать утечки памяти.

Нулевой указатель[править | править вики-текст]

Нулевой указатель − это указатель, хранящий специальное значение, используемое для того, чтобы показать, что данная переменная-указатель не ссылается (не указывает) ни на какой объект. В различных языках программирования представлен различными константами[3].

  • В языках C# и Java: null
  • В языках Си и C++: 0 или макрос NULL. Кроме того, в стандарте C++11 для обозначения нулевого указателя предложено новое ключевое слово nullptr[4]
  • В языках Паскаль и Ruby: nil
  • В языке Python: None

Основные проблемы применения[править | править вики-текст]

Указателями сложно управлять. Достаточно легко записать в указатель неправильное значение, что может вызвать трудно воспроизводимую ошибку. Например, вы случайно поменяли адрес указателя в памяти, или неправильно выделили под информацию память и тут вас может ожидать сюрприз: другая очень важная переменная, которая используется только внутри программы будет перезаписана. В таком случае вам будет сложно воспроизвести баг. Нелегко будет и понять, где именно находится ошибка. И не всегда тривиально будет её устранить (иногда придётся переписать существенную часть программы)[5].

Для решения части проблем есть методы предохранения и страховки:

Инициализируйте указатели[править | править вики-текст]

Пример ошибки с неинициализированным указателем:

/* программа неверна. */
int main (void)
{
    int х, *р; // Выделилась память под x, но не под *p
    х = 10;    // В память записано 10
    *р = х;    // В неясное место в памяти вписывается 10, что может привести к аварийному прекращению программы.
    return 0;
}

В такой маленькой программе проблема может остаться незамеченной. Но, когда программа разрастется, то, внезапно, может выясниться, что переменная записана промеж других данных важных для программы. Просто инициализируйте указатель[5].

Верно используйте указатели[править | править вики-текст]

Неправильное использование указателя:

#include <stdio.h>
/* программа неверна */
int main(void)
{
    int x, *p;
    x = 10;
    p = x;
    printf ("%d", *p);
    return 0;
}

Вызов printf() не выводит значения х, которое равно 10, на экран. В итоге выводится некоторое неизвестное значение из-за неправильного оператора присваивания р = х; Этот оператор присваивает значение 10 указателю р, который должен содержать адрес, а не значение. К счастью, ошибка в данной программе обнаруживается компилятором. Компилятор выдает предупреждение о необычном преобразовании указателя. Для устранения ошибки следует написать p = &х;[5].

Утечка памяти[править | править вики-текст]

Утечка памяти — процесс неконтролируемого уменьшения объёма свободной оперативной памяти (RAM) компьютера, связанный с ошибками в работающих программах, вовремя не освобождающих ненужные уже участки памяти, или с ошибками системных служб контроля памяти.

char *pointer = NULL;
for( int i = 0; i < 10; i++ )
{
    pointer = new char[100]; // Память выделяет 10 раз
}
delete [] pointer; // А освобождается только в последнем случае

Сравнение указателей[править | править вики-текст]

Кроме операций с адресами их можно проверять. pNum1 < pNum2 и pNum1 > pNum2 могут помочь при путешествии по массиву (проверять дошли до конца массива или нет сравнивая два указателя, где первый — текущее положение в памяти, а второй — конец массива в памяти), pNum1 == pNum2 вернет истину в том случае, если оба указателя указывают на одну ячейку памяти.

Адресная арифметика[править | править вики-текст]

Адресная арифметика появилась как логичное продолжение идеи указателей наследованной от ассемблерных языков. В последних имеется возможность указать некое смещение от текущего положения.

int* p; // Если p указывает на адрес - 200
p++;    // После операции сложения указывает на 200 + sizeof(int) = 204
p--;    // Обратно указывает на 200.

Умный указатель[править | править вики-текст]

Существуют, как правило, шаблонные классы, реализующие указатели, но избавляясь от ряда их недостатков.

Указатель в биологии человека[править | править вики-текст]

Мозг использует группы клеток, похожие на указатели, для решения некоторых задач[6].

Примечания[править | править вики-текст]

  1. Для чего используются указатели?. Проверено 20 февраля 2013. Архивировано из первоисточника 26 февраля 2013.
  2. 14.1. Распределение памяти. — «Адрес начала выделенной памяти возвращается в точку вызова функции и записывается в переменную-указатель. Созданная таким образом переменная называется динамической переменной.»  Проверено 22 февраля 2013. Архивировано из первоисточника 26 февраля 2013.
  3. "американский сайт". Проверено 20 февраля 2013. Архивировано из первоисточника 26 февраля 2013.
  4. A name for the null pointer: nullptr (англ.). JTC1.22.32. JTC1/SC22/WG21 — The C++ Standards Committee (2 October 2007). Проверено 4 октября 2010. Архивировано из первоисточника 11 февраля 2012.
  5. 1 2 3 Проблемы, связанные с указателями. Проверено 22 февраля 2013. Архивировано из первоисточника 26 февраля 2013.
  6. Мозг использует приемы из программирования для решения новых задач. russian-newspaper.ru (24 сентября 2013). Проверено 27 сентября 2013.