Указатели в языке C – тема достаточно сложная. Ее должен изучить каждый уважающий себя и свою работу программист. Если рассматривать указатели поэтапно, получится разобраться с этим направлением достаточно быстро.
Предложенная информация ориентирована на широкую публику. Она поможет не только понять, в чем заключается суть указателей C, но и научит грамотно ими пользоваться. Эта информация пригодится как новичкам, так и более опытным программистам.
Определение
Указатель – это некая переменная. Она будет поддерживаться в программном коде (исходном приложении). Диапазон значений этой самой переменной включает в себя адреса ячеек памяти или специального назначения – нулевого адреса. Последний используется для того, чтобы указать – в данный момент указатель не ссылается ни на одну из допустимых ячеек.
Появились рассматриваемые «адреса» в 1955 году. В это время в Адресном языке программирования появились такие понятия как косвенная адресация и адресация высшего ранга. Они покрывали понятие указателя и области его применения в современных средствах программирования. Сейчас подобные составляющие применяются достаточно часто.
Указатель – это объект, значением которых выступает адрес других объектов. В качестве них могут выступать совершенно разные составляющие. Примеры – переменные, константы, указатели или функции. Данный элемент является неотъемлемым компонентом управления памятью в C.
Как задать указатель
Для того, чтобы задать адрес указателя, нужно прописать тип объекта, на который ссылается рассматриваемый объект. Дополнительно в форме записи устанавливается символ «звездочка»:
Сначала будет указываться тип данных, на который будет ссылаться указатель, далее – символ-звездочка и непосредственное имя указателя. Вот пример, в котором определен указатель на объект типа int:
Int *p;
Пока указатель не ссылается ни на какой объект. Теперь можно присвоить ему адрес переменной:
Выше – наглядный пример реализации рассматриваемого элемента в C.
Адрес данных – получение
Указатель включает в себя адрес объекта в памяти компьютера или иного устройства. Для того, чтобы получить этот самый адрес, нужно применять к переменной определенную операцию. А именно – &. Данная операция применяется исключительно к элементам, которые будут храниться в памяти задействованного устройства: к переменным и элементам массива.
Переменная x, которая имеет тип int (целочисленное значение), а также указатель, который ссылается на ее адрес, будет тоже иметь тип int. В языке C должно соблюдаться полное соответствие типу.
Для того, чтобы вывести адрес x, необходимо воспользоваться специальным спецификатором – %p. В программном коде реализация будет выглядеть так:
Здесь машинный адрес переменной x окажется 0x0060FEA8. Для адресов памяти в компьютерах используется шестнадцатеричная система счислений. В каждом отдельном случае адрес способен меняться – это нормально. Фактически адрес – это целочисленное значение, которое записывается в 16-ричной системе счисления.
В сложившейся ситуации в памяти компьютера имеется адрес 0x0060FEA8. По нему будет располагаться некоторая переменная x:
Указатель в языке Си в виде переменной x будет представлять собой тип int. На большинстве архитектур она будет занимать следующие 4 байта (на некоторых устройствах размер памяти для типа int может отличаться). Это приводит к тому, что переменная типа int последовательно займет несколько ячеек:
- 0x0060FEA8;
- 0x0060FEA9;
- 0x0060FEAA;
- 0x0060FEAB.
Указатель p ссылается на адрес, по которому будет располагаться переменная x. В сложившейся ситуации это адрес 0x0060FEA8.
При выводе адрес указателя функция printf() будет ожидать, что указатель представляет void* – указатель на значение типа void. Из-за этого компиляторы при некоторых настройках способны отображать различные предупреждения. Для того, чтобы все было правильно, переданный указатель необходимо преобразовать в тип void*.
Значение по адресу
Рассматриваемый компонент в C будет хранить адрес. По нему можно получить хранящееся в соответствующей области значение. Речь идет о значении переменной x. Для этого будет применяться операция * или разыменование. Результатом послужит объект, на который указывает тот или иной указатель. Вот так будет выглядеть соответствующая операция при применении к x:
При помощи полученного значения в результате обработки операции можно присвоить его другой переменной:
В заданном примере переменной y присваивается значение по адресу указателя p. Это значит, что предстоит иметь дело с переменной x.
Используя рассматриваемый элемент, можно менять значение по адресу, который будет храниться непосредственно в указателе:
По адресу, на который указывает рассматриваемый элемент программного кода, располагается переменная x. Значение будет изменено. Вот пример создания еще нескольких указателей:
При выводе на консоль должен получиться соответствующий результат:
По адресам можно заметить, что переменные часто располагаются в памяти рядом, но не обязательно в том порядке, в котором они определяются непосредственно в тексте исходного программного приложения.
Выше – наглядный пример того, как могут располагаться переменные в памяти компьютера, ноутбука или другого задействованного устройства.
Где применяют указатели
На данный момент рассматриваемый элемент в C применяется в двух областях:
- При работе в системе косвенной адресации. Преимуществом данной концепции можно назвать значительную экономию памяти. Создавая «ссылки» на файлы, можно считать его с диска, а не загружать в ОЗУ. Передавая указатель на переменную в функцию, копия переменной не создается. Она редактируется напрямую. Указатели используются для хранения адресов точек входа для подпрограмм в процедурном программировании, а также для подключения динамически подключаемых библиотек.
- Динамическое управление памятью. В так называемой куче (динамической памяти) выделяется пространство. Переменная, для которой выделяется память, будет называться динамической. В языке C нет понятия строковых переменных, поэтому для строк обычно используются указатели на символьные массивы.
Для других целей указатели в языке Си (и других средствах программирования) практически не используются.
Ключевые проблемы
Рассматриваемым элементом управлять трудно. Это касается не только новичков, но и более опытных программистов. Примером может послужить изменение адреса указателя в памяти или неправильное выделение информации. Понять, где находится ошибка, а затем воспроизвести ее – легко. А вот устранение подобных ошибок – не всегда простая задача. Иногда приходится переписывать существенную часть приложения.
Для решения основной части проблем, связанных с рассматриваемым компонентом в Си, необходимо пользоваться такими приемами как:
- Инициализация указателей. Если они не «установлены» в приложении, работать подобные элементы не будут.
- Грамотное использование указателей. Перед запуском проекта необходимо всегда проверять, как реализованы изучаемые компоненты. Инициализировать переменные рекомендуется сразу при объявлении. Смешивать указатели с обычными переменными не рекомендуется.
- Следить за утечкой памяти.
Теперь понятно, как работать с указателями в C.
P. S. Интересует разработка? Обратите внимание на курс «Программист C». Также в Otus доступно множество других современных курсов.