Ссылки в C++ для начинающих. Повторение и продолжение знакомства

Сайт не является учебником по программированию. Это только небольшой авторский сборник информации в помощь начинающим программистам.

В чем отличия ссылки от указателя. Вопрос, да… Для тех, кто переходит от С к С++ не часто очевидно что за две такие сущности ссылка и указатель. С указателем — то понятно, а с ссылкой не очевидно. Это касается не только переходящих от языка к языку, но и различных новичков тоже.
Различие ссылки и указателя в том, что это две разные сущности.

Чтобы понять природу ссылки, можно разобрать скелет переменной. Что такое переменная и из чего она состоит? Переменная — это область в памяти, которая хранит некоторые данные, которые можно обработать, а еще эта область памяти именуется некоторым допустимым именем, которое помогает связаться операционной системе с этой самой областью памяти. Имя переменной — это упрощенное обращение к адресу памяти и выступает в роли идентификатора. Благодаря идентификации памяти по имени области памяти получается связаться с данными, которые в этой памяти расположились. Графически это выглядит приблизительно так.

рис1. Объект — переменная
ссылки С++

У переменной есть три важные части.

      1. Адрес в памяти
      2. Тип данных ( минимально и более чем минимально необходимые размеры для размещения в памяти)
      3. Значение переменной (сами данные, размещенные в памяти)

Всё это вместе создает сущность, которую мы называем переменной.
Если вытащить из этой сущности адрес, то этот адрес, отдельно от типа и значения, как раз и будет ссылкой на адрес памяти. Такой нельзя изменить, он фиксирован. Можно записать данные в другую область памяти (скопировав их туда), но нельзя заставить адрес памяти быть другим адресом памяти. Это физически невозможно. Зато можно именовать адрес памяти в удобном для нас виде. Такое именование адреса равносильно созданию ссылки.

В этом маленьком коде скрыто много нюансов. Имя переменной a — это идентификатор области памяти. Тип int — это размер, выделенный в памяти для хранения подходящего значения. Связь операционной системы с этой областью памяти происходит благодаря адресу, который операционная система выбирает сама. Нам не надо выискивать свободное адресное пространство. Операционная система находит подходящее адресное пространство, резервирует его для программы. У зарезервированного адресного пространства есть свое начало. Начало этого пространства имеет какой-то адрес. А имя переменной — это удобный для нас способ связи с этим самым началом адресного пространства. Имя переменной — это идентификатор на адрес такого зарезервированного пространства.

Именование адресного пространства — это и есть создание ссылки в С++. Смотрите. Мы указываем тип данных, чтобы размер области в памяти был как минимум не меньше размера зарезервированной области памяти. Далее мы идентифицируем начало этой области некоторым имененем, а чтобы указать, что мы именуем именно адрес начала области памяти, мы используем знак амперсанда, который обозначает адрес.
int &x; // Идентифицировали область памяти, обозначив, что это какой-то адрес. Но в отличии от указателя, адрес у которого создается, как у любого другого объекта, ссылка не имеет своего автоматически создаваемого адреса. Этот адрес — наша забота. Нельзя использовать такой объект, у которого в принципе нет адреса начала. Поэтому создание ссылки требует указание адреса. Имя обычной переменной является идентификатором физического адреса в памяти. Наша забота связать адрес ссылки с физически существующим адресом. Такая связь реализуется с помощью присваивания в ссылку имени переменной. Фактически получается, что имя переменной выступает в роли своеобразного маячка для привязки к адресу памяти. Ссылка забирает физический адрес переменной и фактически становится именно той переменной. Физический адрес памяти изменить нельзя, а ссылка жестко привязана к физическому адресу — изменение ее равносильно попытке изменить не данные внутри себя, а непосредственно сам физический адрес.

так сказать у p своя собственная область памяти и свое значение, а еще p указывает на другую область памяти с лежащими там данными.
Таким образом — ссылки по природе своей проще чем указатели. В то время как указатели можно интерпретировать как объект, содержащий внутри себя объект (это совсем не так (это важно), но это очень близко к действительности), ссылку можно интерпретировать только как отдельный объект. Внутри указателя может быть ссылка (объект в объекте), внутри ссылки указателя быть не может (не может быть объекта в объекте, если объект не может содержать объекта).
Надеюсь понятно, что ссылка похожа на демо версию указателя и является синонимом имени переменной. Иначе, дальше я только запутаю читателя.
Хотя ссылка внутри себя ничего не содержит, она может привязываться к любому физическому адресу памяти. Следовательно она может привязаться как к значению, на которое указывает указатель, так и к самому указателю.

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

  • Ни в коем случае не думайте, что внутри указателя лежит полноценный объект. Это не так. Для удобства восприятия можно условно принять такую идею. Это действительно может поспособствовать пониманию. Только всегда помните, что внутри указателя есть только объект-адрес, который указывает куда-то. Можно сказать, что то, что там по тому адресу находится привязано к указателю, но нельзя говорить, что оно оттуда целиком и полностью внутри указателя. Внутри указателя только адрес.

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

Такой вот момент. В С++ всегда, когда типы не соответствуют друг другу, происходит приведение типов. Т.е. в данном случае при присваивании в z Значения из x, происходит не копирование x, а копирование некоторой переменной, создаваемой из x. Любое приведение типов не делает переменную переменной нужного типа, а создает дополнительную временную переменную подходящего типа. Копирует туда значения и уже из этой созданной временной переменной копирует значения в конечную переменную. Такой вот нюанс.

Обещание не изменять временную переменную обозначает просьбу продления жизни такой переменной. Такая маленькая фишка.

Зачем я об этом говорю? Всё просто. При передаче параметров вовнутрь функций, во избежание накладных расходов часто нужно передавать данные по ссылке. Казалось бы, что добавление квалификатора const только обещание не измененения данных и ничего не дает. Только именно оговариваемый нюанс позволяет использовать небольшой бонус. Вот смотрите, что происходит при работе с функциями

По этой причине всегда можно увидеть, как нам буквально кричат: "Используйте константные ссылки" Только большинство из нас успешно игнорирует эти полезные советы.

  • Используйте константы везде, где это возможно

Теперь попробуйте такое же проделать с указателем

Это одно из отличий при передаче в функцию. Указателю нужно выделить отдельное пространство и уже потом указать ему куда ему идти. Т.е. выполнить двойное указание. В параметрах функции нельзя выделять память, там можно только указывать параметры. Такие дела.

Кроме этого отличия ссылки и указатели в следующих моментах

  • Присвоение чего-либо указателю изменяет значение указателя, а не объекта на который он установлен
  • Для того чтобы получить указатель, как правило, необходимо использовать оператор new или &
  • Для доступа к объекту, на который установлен указатель,
    используются операторы * и [ ]
  • Присвоение ссылке нового значения изменяет то, на что она ссылается,а не саму ссылку.
  • После инициализации ссылку невозможно установить на другой объект.
  • Присвоение ссылок основано на глубоком копировании (новое значение присваивается объекту, на который указывает ссылка); присвоение указателей не использует глубокое копирование (новое значение присваивается указателю, а не объекту)
  • Нулевые указатели представляют опасность.
  • Как ссылка, так и указатель основаны на адресации памяти, но предоставляют программисту разные возможности.

Можно задаться вопросом: "В каких случаях в функцию передавать указатель, а в каких ссылку"
Есть совет у Страуструпа

  • Для маленьких объектов предпочтительнее передача по значению
  • Для функций, допускающих в качестве своего аргумента "нулевой объект"
    (представленный значением 0), следует использовать передачу указателя(и не забывать проверку нуля)
  • В противном случае в качестве параметра следует использовать cсылку.

Я попробовал описать природу ссылки и отличия ее указателей. Уж не знаю насколько оно получилось понятно, но я действительно старался. В завершении темы добавлю свое художество, именуемое круговоротом в природе ссылок и указателей.
ссылки С++

Все комментарии на сайте проверяются, поэтому ваш комментарий может появиться не сразу. Для вставки кода в комментарий используйте теги: [php]ВАШ_КОД[/php]

8 комментариев: Ссылки в C++ для начинающих. Повторение и продолжение знакомства

  • ander говорит:

    Немного сумбурно получилось, новому человеку понять тяжело.  Может быть, проще объяснить так:

    Ссылок на самом деле в программе не существует, также как и имен переменных.  Используются они только при компиляции и линковке.  А вот указатель — это вполне реальная ячейка памяти, содержащая адрес.

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

    • admin говорит:

      Объяснение плохо.
      1. Понять понятием программы можно как и саму программу, уже созданную из кода и работающую, так и сам код программы. Мы же говорим, что пишем программу, опуская слово код.
      2. Отвод в ассемблер, словно намек урыться в ассемблер.
      3. Указатель- не ячейка памяти.
      4. Команда — неудачное здесь слово. ссылка — новое имя (новые имена) для первоначального имени переменной и всё.

      • ander говорит:

        Хорошо, попробую переформулировать.
        В исполняемом процессором коде нет ни имен переменных, ни ссылок. Там присутствуют только адреса.
        Адрес может быть прописан непосредственно в коде — тогда мы имеем дело с именем переменной или ссылкой, которые по сути просто удобные для человека алиасы адреса, такой во время выполнения кода изменяться не может.
        Но адрес ведь сам по себе число и его тоже можно положить в переменную. Вот такой находящийся в переменной адрес, это и есть указатель. С данными по указателю процессор работает в два этапа — сначала берет из переменной значение адреса, а потом уже обращается к памяти по этому адресу. Разумеется, в этом случае адрес в переменной может быть изменен в процессе исполнения.

        • admin говорит:

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

          По моему скромному мнению, для человека только сейчас знакомящегося с этой темой, Ваш вариант весьма и весьма запутан. Это не изучение процессора, как ни крути, а впечатление именно, что тут про процессор рассказывают.

          Адрес — это адрес. Он не число, но может обозначаться как угодно, в информационных технологиях, он, как правило, обозначается шестнадцатеричным числом.
          Согласно Вашей формулировке: С указателем процессор может обращаться и непосредственно к указателю (один этап) и к значению, на которое указывает указатель (два этапа).

          И, кстати, в зависимости от компилятора, ссылка может превращаться в константный указатель со свойствами ссылки, что означает существование её в природе. Во всяком случае, некоторые компиляторы Visual Studio показывают независимый  адрес у ссылки во время отладки.

          Такое ощущение, что чем сильнее стараетесь, тем сильнее запутываете всё. Это исключительно мое ощущение, совсем не стоит к нему относится серьезно.

           

           

  • Павел говорит:

    И так плохо понимал что такое указатели и ссылки. Сей час вообще запутался

  • Павел говорит:

    Вот это мракобесие вообще не понял. С первой строчки идут какие-то непонятные символы и ничего не объясняется. Во первых нафига нам выделение памяти для ньюинт? Во вторыз как можно присваивать ссылке указатель?

    • admin говорит:

      Выделение памяти не для ньюинт, а выделение места в памяти для размещения в ней целого значения (переменной типа int).
      Ссылке можно навязать любой тип переменой, если тип ссылки такой же как тип навязываемой ей переменой.

      Как можно присваивать указатель? — Указатель — это такой же тип, как double, int, char — только хранит адреса.

      Ваша проблема начинается с того, что Вы не понимаете что такое указатель. Весь этот вопрос, он об указателях.

  • Павел говорит:

    И что это ща запись такая заморская *&ptr?? Можно примеры более приземленные? В простых вещах надоразобраться,а тут каскад бреда какой-то

Добавить комментарий

Ваш e-mail не будет опубликован.

Поиск

 
     
Яндекс.Метрика

НАГРАДИ АВТОРА САЙТА
WEBMONEY
R375024497470
U251140483387
Z301246203264
E149319127674

Демотиватор китайской клавиатуры

Выражаю свою признательность

  • Максиму очень признателен за указание на мои ошибки и неточности.
  • Sergio ===> за оказание помощи в исправлении моих ошибок
  • Gen ===> за правильное стремление помочь другим новичкам и выявления моих ошибок