Указатели в языке 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 применяется в двух областях:

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

Для других целей указатели в языке Си (и других средствах программирования) практически не используются.

Ключевые проблемы

Рассматриваемым элементом управлять трудно. Это касается не только новичков, но и более опытных программистов. Примером может послужить изменение адреса указателя в памяти или неправильное выделение информации. Понять, где находится ошибка, а затем воспроизвести ее – легко. А вот устранение подобных ошибок – не всегда простая задача. Иногда приходится переписывать существенную часть приложения.

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

  1. Инициализация указателей. Если они не «установлены» в приложении, работать подобные элементы не будут.
  2. Грамотное использование указателей. Перед запуском проекта необходимо всегда проверять, как реализованы изучаемые компоненты. Инициализировать переменные рекомендуется сразу при объявлении. Смешивать указатели с обычными переменными не рекомендуется.
  3. Следить за утечкой памяти.

Теперь понятно, как работать с указателями в C.

P. S. Интересует разработка? Обратите внимание на курс «Программист C». Также в Otus доступно множество других современных курсов.