В чем реальная разница между указателями и ссылками? - PullRequest
29 голосов
/ 18 сентября 2008

АКА - Что за одержимость указателями?

Поскольку я действительно использовал только современные объектно-ориентированные языки, такие как ActionScript, Java и C #, я не совсем понимаю важность указателей и то, для чего вы их используете. Что мне здесь не хватает?

Ответы [ 22 ]

79 голосов
/ 18 сентября 2008

Это всего лишь косвенность: способность не иметь дело с данными, а сказать: «Я направлю вас к некоторым данным, там». У вас та же концепция в Java и C #, но только в справочном формате.

Ключевые различия заключаются в том, что ссылки являются фактически неизменными указателями - они всегда указывают на что-то. Это полезно и легко понять, но менее гибко, чем модель указателя Си. Указатели C - это указатели, которые вы можете с радостью переписать. Вы знаете, что искомая нить находится рядом с нитью, на которую указывают? Ну, просто немного измените указатель.

Это хорошо сочетается с подходом C "близко к кости, требуются знания низкого уровня". Мы знаем , что char* foo состоит из набора символов, начинающихся в месте, указанном указателем foo. Если мы также знаем, что длина строки не менее 10 символов, мы можем изменить указатель на (foo + 5), чтобы он указывал на ту же строку, но начать с половины длины в.

Эта гибкость полезна, когда вы знаете, что делаете, и смерть, если вы не знаете (где «знать» - это больше, чем просто «знать язык», это «знать точное состояние программы»). Поймите неправильно, и ваш указатель направит вас с края утеса. Ссылки не дают вам повозиться, поэтому вы гораздо увереннее в том, что можете следовать за ними без риска (особенно в сочетании с такими правилами, как «объект, на который ссылаются, никогда не исчезнет», как в большинстве языков сборки мусора).

28 голосов
/ 18 сентября 2008

Вы упускаете много! Понимание того, как компьютер работает на более низких уровнях, очень полезно в нескольких ситуациях. C и ассемблер сделают это за вас.

В основном указатель позволяет вам записывать данные в любую точку памяти компьютера. На более примитивном оборудовании / ОС или во встроенных системах это может действительно сделать что-то полезное Скажем, включите и выключите blinkenlichts.

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

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

В некоторой степени это происходит даже с Java. Вы передаете ссылки на объекты, а не сами объекты. Помните, что в конечном итоге каждый объект представляет собой набор бит в основной памяти компьютера.

14 голосов
/ 18 сентября 2008

Указатели предназначены для непосредственного манипулирования содержимым памяти.

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

Языки высокого уровня скрывают указатели за кулисами: например, ссылка в Java реализована в виде указателя практически в любой JVM, с которой вы столкнетесь, поэтому она называется NullPointerException, а не NullReferenceException. Но он не позволяет программисту напрямую обращаться к адресу памяти, на который он указывает, и его нельзя изменить, чтобы он принял значение, отличное от адреса объекта правильного типа. Поэтому он не обладает той же силой (и ответственностью), что указатели на языках низкого уровня.

[Редактировать: это ответ на вопрос «что это за одержимость указателями?». Все, что я сравнил, это указатели в стиле ассемблера / C с ссылками на Java. С тех пор название вопроса изменилось: если бы я решил ответить на новый вопрос, я мог бы упомянуть ссылки на языках, отличных от Java]

12 голосов
/ 18 сентября 2008

Это все равно что спросить: «Что это за одержимость инструкциями процессора? Могу ли я что-то упустить, не разбрасывая инструкции x86 MOV повсюду? »

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

Итак ... Не волнуйся. Вы уже используете указатели - и без опасности сделать это неправильно тоже. :)

10 голосов
/ 18 сентября 2008

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

Это очень похоже на программирование прямо сейчас. Существует необходимость в разработке C / C ++ для некоторых программ. Некоторые примеры - это высококачественные 3D-игры, низкоуровневое встроенное программное обеспечение, скорость, в которой скорость является критической частью цели программного обеспечения, и язык более низкого уровня, который обеспечивает более близкий доступ к фактическим данным, которые необходимо обработать, является ключом к этой производительности. , Однако для большинства программистов это не так, и незнание указателей не наносит вреда. Тем не менее, я верю, что каждый может извлечь пользу из изучения C и указателей, а также ручных передач.

5 голосов
/ 18 сентября 2008

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

Вы получаете Объект A создаете экземпляр Объекта B и передаете его в качестве параметра метода Объекту C. Объект C изменяет некоторые значения в Объекте B. Когда вы возвращаетесь к коду Объекта A, вы можете увидеть измененное значение в Объект Б. Почему это так?

Поскольку вы передали ссылку Объекта B на Объект C, не сделали еще одну копию Объекта B. Таким образом, Объект A и Объект C оба хранят ссылки на один и тот же Объект B в памяти. Меняется с одного места и будет видно в другом. Это называется ссылкой.

Теперь, если вы вместо этого используете примитивные типы, такие как int или float, и передаете их в качестве параметров метода, изменения в Объекте C не могут быть видны Объектом A, поскольку Объект A просто передал копию вместо ссылка на собственную копию переменной. Это называется по значению.

Вы, наверное, уже знали это.

Возвращаясь к языку C, функция A передает функции B некоторые переменные. Эти параметры функции являются собственно копиями по значению. Чтобы функция B могла управлять копией, принадлежащей функции A, функция A должна передать указатель в переменную, чтобы она стала передачей по ссылке.

«Эй, вот адрес памяти для моей целочисленной переменной. Поместите новое значение в это место адреса, и я заберу позже».

Обратите внимание, что концепция похожа, но не на 100% аналогична. Указатели могут сделать гораздо больше, чем просто передать «по ссылке». Указатели позволяют функциям манипулировать произвольными ячейками памяти до любого требуемого значения. Указатели также используются для указания новых адресов кода выполнения для динамического выполнения произвольной логики, а не только переменных данных. Указатели могут даже указывать на другие указатели (двойной указатель). Это мощно, но также довольно легко внедрить трудно обнаруживаемые ошибки и уязвимости безопасности.

4 голосов
/ 18 сентября 2008

В повседневной работе я интенсивно использую указатели и ссылки ... в управляемом коде (C #, Java) и неуправляемом (C ++, C). Я узнал о том, как обращаться с указателями и чем они являются самим мастером ... [Бинки !!] [1] Больше ничего не нужно говорить;)

Разница между указателем и ссылкой заключается в следующем. Указатель - это адрес какого-то блока памяти. Его можно переписать или, другими словами, переназначить в другой блок памяти. Ссылка - это просто переименование какого-либо объекта. Это может быть назначено только один раз! Как только он назначен объекту, он не может быть назначен другому. Ссылка - это не адрес, это другое имя переменной. Ознакомьтесь с C ++ FAQ , чтобы узнать больше.

Link1

LINK2

4 голосов
/ 18 сентября 2008

Если вы раньше не видели указателей, вы наверняка пропустите этот мини-камень:

void strcpy(char *dest, char *src)
{    
        while(*dest++ = *src++);
}
4 голосов
/ 18 сентября 2008

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

Указатели возникли из-за осознания того, что области памяти могут также содержать адреса других областей памяти, что дает нам косвенность. Без указателей (на низком уровне) самые сложные структуры данных были бы невозможны. Нет связанных списков, бинарных деревьев или хеш-таблиц. Нет передачи по ссылке, только по значению. Поскольку указатели могут указывать на код, без них у нас также не было бы виртуальных функций или таблиц поиска функций.

2 голосов
/ 28 мая 2015

Ссылки на C ++ принципиально отличаются от ссылок на языках Java или .NET; Языки .NET имеют специальные типы, называемые «byrefs», которые во многом похожи на «ссылки» C ++.

Ссылка C ++ или .NET byref (я буду использовать последний термин, чтобы отличать ссылки .NET) - это специальный тип, который не содержит переменную, а скорее содержит информацию, достаточную для идентификации переменной (или чего-то еще). который может вести себя как единое целое (например, слот массива), хранящийся в другом месте. Byrefs обычно используются только в качестве параметров / аргументов функции и предназначены для эфемерности. Код, который передает byref функции, гарантирует, что идентифицируемая переменная будет существовать, по крайней мере, до тех пор, пока эта функция не будет возвращена, а функции обычно гарантируют, что не сохранят ни одной копии byref после своего возврата (обратите внимание, что в C ++ последнее ограничение не исполнение). Таким образом, byrefs не может пережить идентифицированные им переменные.

В языках Java и .NET ссылка - это тип, который идентифицирует объект кучи; Каждый объект кучи имеет связанный класс, и код в классе объекта кучи может получить доступ к данным, хранящимся в объекте. Объекты кучи могут предоставлять внешнему коду ограниченный или полный доступ к хранящимся в нем данным и / или разрешать внешнему коду вызывать определенные методы в своем классе. Использование ссылки на вызов метода своего класса приведет к тому, что эта ссылка станет доступной для этого метода, который затем может использовать ее для доступа к данным (даже частным данным) в объекте кучи.

Что делает ссылки особенными в языках Java и .NET, так это то, что они поддерживают в качестве абсолютного инварианта, что каждая ненулевая ссылка будет продолжать идентифицировать один и тот же объект кучи, пока эта ссылка существует. Как только нигде во вселенной не существует ссылки на объект кучи, объект кучи просто перестанет существовать, но нет никакого способа, которым объект кучи может прекратить существовать, пока существует какая-либо ссылка на него, и нет никакого способа для «нормального» msgstr "ссылка на объект кучи, чтобы самопроизвольно стать чем-либо, кроме ссылки на этот объект. И Java, и .NET имеют специальные типы «слабых ссылок», но даже они поддерживают инвариант. Если в юниверсе не существует неслабых ссылок на объект, то любые существующие слабые ссылки будут признаны недействительными; как только это произойдет, не будет никаких ссылок на объект, и поэтому он может быть признан недействительным.

Указатели, такие как ссылки C ++ и ссылки Java / .NET, идентифицируют объекты, но в отличие от вышеупомянутых типов ссылок они могут переживать идентифицируемые ими объекты. Если объект, идентифицированный указателем, перестает существовать, но сам указатель не существует, любая попытка использовать указатель приведет к неопределенному поведению. Если указатель не известен как null или для идентификации объекта, который существует в настоящее время, не существует стандартного способа сделать что-либо с этим указателем, кроме как перезаписать его чем-то другим. Вполне допустимо, чтобы указатель продолжал существовать после того, как идентифицированный таким образом объект перестал это делать, при условии, что ничто никогда не использует указатель, но необходимо, чтобы что-то вне указателя указывало, безопасно ли его использовать, потому что нет способа спроси сам указатель.

Ключевое различие между указателями и ссылками (любого типа) заключается в том, что ссылки всегда можно спросить, являются ли они действительными (они будут либо действительными, либо идентифицируемыми как нулевые), и если они будут считаться действительными, они останутся таковыми Пока они существуют. Указатели нельзя спрашивать, действительны ли они, и система не будет делать ничего, чтобы гарантировать, что указатели не станут недействительными, и не позволит указателям, которые стали недействительными, быть признанными как таковые.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...