Указатель (тип данных)
Указатель (англ. 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]
- В языке Паскаль: 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++; // После операции сложения указывает на 204 p--; // Обратно указывает на 200.
Примечания [править]
- ↑ Для чего используются указатели?. Архивировано из первоисточника 26 февраля 2013. Проверено 20 февраля 2013.
- ↑ 14.1. Распределение памяти. — «Адрес начала выделенной памяти возвращается в точку вызова функции и записывается в переменную-указатель. Созданная таким образом переменная называется динамической переменной.» Архивировано из первоисточника 26 февраля 2013. Проверено 22 февраля 2013.
- ↑ "американский сайт". Архивировано из первоисточника 26 февраля 2013. Проверено 20 февраля 2013.
- ↑ A name for the null pointer: nullptr (англ.). JTC1.22.32. JTC1/SC22/WG21 — The C++ Standards Committee (2 October 2007). Архивировано из первоисточника 11 февраля 2012. Проверено 4 октября 2010.
- ↑ 1 2 3 Проблемы, связанные с указателями. Архивировано из первоисточника 26 февраля 2013. Проверено 22 февраля 2013.
| Типы данных | |
|---|---|
| Неинтерпретируемые | |
| Числовые | |
| Текстовые | |
| Указатель | |
| Композитные |
Алгебраический тип данных (обобщённый) • Массив • Ассоциативный массив • Класс • Список • Кортеж • Объект • Option type • Product • Структура • Множество • Объединение (tagged) |
| Другие |
Логический • Низший тип • Коллекция • Перечисляемый тип • Исключение • First-class function • Opaque data type • Recursive data type • Семафор • Поток • Высший тип • Type class • Unit type • Void |
| Связанные темы | |

