Могут ли несколько указателей ссылаться на одну и ту же переменную в C?

9

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

Рассмотрим такой сценарий. Вы объявляете целочисленную переменную i. Затем объявляете три указателя p, q и r. Вы присваиваете им все адрес переменной i.

Посмотрите на последнюю строку. r не обязан знать о i напрямую. Он просто указывает туда, куда указывает p. А p указывает на i. Следовательно, r также указывает на i.

Оператор присваивания здесь копирует адрес, а не значение. Когда вы выполняете r = p, вы не копируете целое число, содержащееся в i. Вы копируете адрес памяти, который хранится в p.

После выполнения этого кода у i по сути появляется четыре имени. Вы можете обращаться к ней через i. Вы можете обращаться к ней через *p. Вы можете обращаться к ней через *q. Или вы можете обращаться к ней через *r.

Технических ограничений на количество указателей, которые можно таким образом «накопить», нет.

Почему это важно

Это не просто академический синтаксис. Это меняет ваше представление о потоке данных.

В языках со строгими объектными ссылками копирование объекта часто создает новый экземпляр в памяти. В C копирование указателя дешево. Это просто адрес памяти. Быстро. Маленько.

Это позволяет различным частям вашего кода манипулировать одними и теми же данными без дублирования. Измените *p, и *q сразу увидит это изменение.

Вы создаете псевдонимы для памяти.

Риск возникновения псевдонимов

Здесь «крутая особенность» превращается в недостаток.

Если p, q и r все указывают на i, любой из них может изменить i.

Теперь i равно 10. *q равно 10. *r равно 10.

Но что, если вы забудете, что r также следит за i? Вы можете предположить, что i стабильна, потому что вы не трогали её напрямую. Вы тронули только *q.

Это общий ресурс. А общие ресурсы — это место, где живут состояния гонки (race conditions) и неожиданные перезаписи данных.

Как предотвратить непреднамеренные побочные эффекты

Если вы хотите убедиться, что один указатель случайно не перезапишет данные, предназначенные для другого, используйте const.

Теперь r может читать i. Он может указывать на неё. Но он не может её изменять.

Таким образом вы устанавливаете границы в языке, который доверяет вам управление памятью вручную.

Где это применяется в системном программировании

Этот шаблон повсюду в системном коде.

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

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