10-10-2023
Указатель (англ. pointer) — переменная, диапазон значений которой состоит из адресов ячеек памяти или специального значения — нулевого адреса. Последнее используется для указания того, что в данный момент там ничего не записано.
Русское название термин получил по аналогии с дорожными указателями, на них обозначают направление и расстояние до того или иного города.
Указатели применяются в двух сферах:
Языки программирования, в которых предусмотрен тип указателей, содержат, как правило, две основные операции над ними: присваивание и разыменование. Первая из этих операций присваивает указателю некоторый адрес. Вторая служит для обращения к значению в памяти, на которое указывает указатель. Разыменование может быть явным и неявным, в большинстве современных языков программирования разыменование происходит только при явном указании.
Пример указателей на языке Си:
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].
Указателями сложно управлять. Достаточно легко записать в указатель неправильное значение, что может вызвать трудно воспроизводимую ошибку. Например, вы случайно поменяли адрес указателя в памяти, или неправильно выделили под информацию память и тут вас может ожидать сюрприз: другая очень важная переменная, которая используется только внутри программы будет перезаписана. В таком случае вам будет сложно воспроизвести баг. Нелегко будет и понять, где именно находится ошибка. И не всегда тривиально будет её устранить (иногда придётся переписать существенную часть программы)[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; int i = 0; for( i = 0; i < 10; i++ ) { pointer = (char *)malloc(100); // Память выделяет 10 раз } free(pointer); // А освобождается только в последнем случае
Кроме операций с адресами их можно проверять. pNum1 < pNum2
и pNum1 > pNum2
могут помочь при путешествии по массиву (проверять дошли до конца массива или нет сравнивая два указателя, где первый — текущее положение в памяти, а второй — конец массива в памяти), pNum1 == pNum2
вернет истину в том случае, если оба указателя указывают на одну ячейку памяти.
Адресная арифметика появилась как логичное продолжение идеи указателей наследованной от ассемблерных языков. В последних имеется возможность указать некое смещение от текущего положения.
int* p; // Если p указывает на адрес - 200 p++; // После операции сложения указывает на 200 + sizeof(int) = 204 p--; // Обратно указывает на 200.
Существуют, как правило, шаблонные классы, реализующие указатели, но избавляясь от ряда их недостатков.
Мозг использует группы клеток, похожие на указатели, для решения некоторых задач[6].
Типы данных | |
---|---|
Неинтерпретируемые | |
Числовые | |
Текстовые | |
Ссылочные |
Адрес • Ссылка • Ссылка в С++ • Указатель • Обёртка |
Композитные |
Алгебраический тип данных (обобщённый) • Массив • Ассоциативный массив • Класс • Список • Кортеж • Объект • Структура • Множество • Объединение (меченое) |
Другие |
Логический • Низший • Высший • Перечисляемый • Коллекция • Исключение • Функциональный • Род (Метакласс) • Монада • Семафор • Поток • Void |
Связанные темы |
Указатель (тип данных).