Указатели в программировании на C: что такое указатель, и что он делает?

Добавлено 27 мая 2019 в 13:41

Данная статья поможет вам понять указатели, которые являются интересным и важным аспектом языка C.

С точки зрения инженера-электронщика, который пишет прошивки для встраиваемых систем, указатели не являются необходимым инструментом. Однако я собираюсь рассказать о них сейчас, а не позже в этой серии статей, потому что они тесно связаны с массивами, которые мы обсуждали в предыдущей статье. Кроме того, указатели помогают укрепить ваше понимание взаимосвязи между кодом и аппаратным обеспечением.

Я написал немало прошивок, и у меня осталось всего несколько случайных воспоминаний об использовании указателей. Однако я рад, что понимаю их, потому что, когда ситуация требует указателей, я бы предпочел использовать их, а не какое-то простое неэффективное решение по умолчанию.

Что такое указатель?

Указатель – это переменная. Как и другие переменные, он имеет тип данных и идентификатор. Однако указатели используются таким образом, которой принципиально отличается от того, как мы используем «нормальные» переменные, и при объявлении мы должны добавить звездочку, чтобы сообщить компилятору, что данная переменная должна рассматриваться как указатель.

char  *RxByte_ptr;
int   *ADCValue_ptr;

Идентификатор не обязательно должен содержать символы, которые помечают переменную как указатель (такие как "ptr"). Тем не менее, я очень рекомендую использовать эту практику. Это поможет вам сохранить ваши мысли более организованными, и если у вас все указатели будут помечены таким образом, другим инженерам будет легче понять ваш код.

Что делает указатель?

Он указывает. Более конкретно, он указывает на данные другой переменной или на данные, которые хранятся в памяти, но не связаны с переменной.

Рисунок 1 – На что указывает указатель
Рисунок 1 – На что указывает указатель

Обычно мы думаем о переменной как о чем-то, что хранит данные, а под «данным» мы подразумеваем информацию, которая будет использоваться в вычислениях, или отправляться на другое устройство, или загружаться в регистр конфигурации, или использоваться для управления пикселями LCD дисплея. Указатель – это переменная, но она не используется для хранения такого типа данных. Вернее указатель хранит адрес памяти.

Рисунок 2 – Данные о температуре хранятся в переменной, расположенной по адресу 0x01, а синяя переменная является указателем, который содержит адрес, по которому хранятся данные о температуре
Рисунок 2 – Данные о температуре хранятся в переменной, расположенной по адресу 0x01, а синяя переменная является указателем, который содержит адрес, по которому хранятся данные о температуре

Возможно, именно в этот момент некоторые люди начинают немного путаться, и я думаю, что это происходит потому, что легко упустить из виду физическую реальность памяти процессора. Блок памяти представляет собой набор цифровых ячеек памяти, которые организованы в группы. В случае 8-разрядного процессора каждая группа ячеек памяти соответствует одному байту. Единственный способ отличить одну группу от другой – это адрес, а этот адрес – просто число. Указатель – это переменная, в которой хранится число, но это число интерпретируется как адрес, т.е. как значение, указывающее точное местоположение в памяти.

Подкрепим эту концепцию краткой аналогией. Представьте, что я стою в библиотеке, и кто-то подходит ко мне и говорит: «Кто такой Ричард Львиное Сердце?». Если я отвечаю, говоря: «Король Англии с 1189 по 1199 года», я похож на обычную переменную. Я предоставляю информацию, данные, которые хочет получить человек. И напротив, если я отвечаю, указывая на книгу под названием «Монархи средневековой Англии», я действую как указатель. Вместо того чтобы предоставлять нужные данные, я указываю, где именно эти данные можно найти. Я до сих пор храню полезную информацию, но эта информация – это не сам факт, а место, где человек может получить доступ к этому факту.

Понятие типов данных указателей

Как вы могли заметить в приведенных выше примерах, указатели объявляются с типом данных. Возможно, это усугубляет сложность понимания, что такое указатель. Если указатель – это просто число, соответствующее адресу ячейки памяти, то как могут использоваться разные типы данных? Например, если ваш микроконтроллер имеет 4 КБ RAM, как вы можете иметь указатель с типом данных char? Максимальное значение unsigned char составляет 255; что произойдет, если этот указатель должен указывать на переменную, расположенную по адресу памяти 3000?

Ключ к пониманию этой проблемы заключается в следующем: тип данных указателя не указывает, сколько байтов используется для хранения его значения. Скорее, число байтов, используемых для хранения значения указателя, соответствует количеству адресов памяти, к которым необходимо получать доступ, независимо от типа данных указателя. Кроме того, размер указателя определяется компилятором и непосредственно не виден программисту.

Рассмотрим следующую диаграмму:

Рисунок 3 – Представления в памяти переменной и указателя на эту переменную
Рисунок 3 – Представления в памяти переменной и указателя на эту переменную

Допустим, мы используем жалкий микроконтроллер с оперативной памятью всего 11 байтов. Диапазон значений, предлагаемых 8-разрядным числом, составляет от 0 до 255, поэтому один байт памяти более чем достаточен для представления всех возможных областей памяти в этом устройстве.

Диаграмма подчеркивает тот факт, что даже переменная, объявленная как long, может быть доступна через однобайтовый указатель. Синяя переменная – это указатель, который содержит адрес 32-битной переменной Seconds_Cnt. Эта переменная использует четыре байта памяти, но адрес переменной (который в этом примере соответствует младшему значащему байту) всегда будет числом, равным или меньшим 0x0A. Указатель должен быть объявлен с типом данных long, потому что он используется вместе с переменными long, но сам указатель потребляет один байт памяти, а не четыре.

Продолжение следует...

Для такой темы, как указатели, одной статьи просто не хватает. Теперь вы знаете, что такое указатель, и основные возможности, которые он предоставляет в контексте программирования на C. В следующей статье мы сможем узнать, как они действуют, то есть, как на самом деле использовать указатели в ваших проектах встроенного программного обеспечения (прошивок).

Теги

MCUВысокоуровневые языки программированияВычисления во встраиваемых системахМикроконтроллерРазработка ПО для встраиваемых системЯзык C

На сайте работает сервис комментирования DISQUS, который позволяет вам оставлять комментарии на множестве сайтов, имея лишь один аккаунт на Disqus.com.

В случае комментирования в качестве гостя (без регистрации на disqus.com) для публикации комментария требуется время на премодерацию.