В чем разница между C # Reference и Pointer? - PullRequest
74 голосов
/ 10 января 2009

Я не совсем понимаю разницу между ссылкой на C # и указателем. Они оба указывают на место в памяти, не так ли? Единственное отличие, которое я могу выяснить, состоит в том, что указатели не так умны, не могут указывать на что-либо в куче, освобождены от сборки мусора и могут ссылаться только на структуры или базовые типы.

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

Я просто не понимаю, что такого сложного в указателе? Это в основном просто ссылка на место в памяти, не так ли? Он может вернуть свое местоположение и напрямую взаимодействовать с объектом в этом месте?

Я пропустил массивное очко?

Ответы [ 10 ]

116 голосов
/ 10 января 2009

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

Возьмем, к примеру, следующий код

int* p1 = GetAPointer();

Это типобезопасный в том смысле, что GetAPointer должен возвращать тип, совместимый с int *. Тем не менее, еще нет гарантии, что * p1 будет фактически указывать на int. Это может быть символ, двойник или просто указатель на случайную память.

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

string str = GetAString();

В этом случае str имеет одно из двух состояний: 1) указывает на отсутствие объекта и, следовательно, имеет значение null или 2) указывает на допустимую строку. Вот и все. CLR гарантирует, что это так. Это не может и не будет для указателя.

42 голосов
/ 10 января 2009

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

РЕДАКТИРОВАТЬ: Концептуально, да. Они более или менее одинаковы.

12 голосов
/ 10 января 2009

Ссылка - это «абстрактный» указатель: вы не можете выполнять арифметику со ссылкой и не можете выполнять какие-либо низкоуровневые трюки с ее значением.

6 голосов
/ 10 января 2009

Сначала я думаю, что вам нужно определить «указатель» в вашей семантике. Вы имеете в виду указатель, который вы можете создать в небезопасном коде с fixed ? Вы имеете в виду IntPtr , который вы получаете, возможно, от родного звонка или Marshal.AllocHGlobal ? Вы имеете в виду GCHandle ? Все это по сути одно и то же - представление адреса в памяти, где что-то хранится - будь то класс, число, структура, что угодно. И, к сведению, они, безусловно, могут быть в куче.

Указатель (все вышеперечисленные версии) является фиксированным элементом. GC не знает, что находится по этому адресу, и поэтому не может управлять памятью или жизнью объекта. Это означает, что вы теряете все преимущества системы сбора мусора. Вы должны вручную управлять памятью объекта, и у вас есть вероятность утечек.

Ссылка, с другой стороны, в значительной степени является "управляемым указателем", о котором GC знает. Это по-прежнему адрес объекта, но теперь сборщик мусора знает подробности цели, поэтому он может перемещать ее, выполнять уплотнения, финализировать, удалять и все другие полезные вещи, которые делает управляемая среда.

Основное отличие, действительно, в том, как и почему вы будете их использовать. В подавляющем большинстве случаев на управляемом языке вы будете использовать ссылку на объект. Указатели становятся удобными для выполнения взаимодействия и редкой необходимости в действительно быстрой работе.

Редактировать: На самом деле вот хороший пример , когда вы можете использовать «указатель» в управляемом коде - в данном случае это GCHandle, но точно такая же вещь могла бы иметь было сделано с AllocHGlobal или с помощью фиксированного байтового массива или структуры. Я предпочитаю GCHandle, потому что он чувствует себя более ".NET" для меня.

5 голосов
/ 10 января 2009

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

Кроме того, указатели можно использовать только в небезопасном контексте.

4 голосов
/ 13 ноября 2013

Основное различие между ссылкой и указателем заключается в том, что указатель представляет собой набор битов, содержание которых имеет значение только в том случае, если он активно используется в качестве указателя, тогда как указатель инкапсулирует не только набор битов, но и некоторые метаданные. который держит основную структуру в курсе ее существования. Если указатель существует на некоторый объект в памяти, и этот объект удален, но указатель не удален, продолжающееся существование указателя не будет причинять никакого вреда, пока не будет предпринята попытка доступа к памяти, на которую он указывает. Если не будет предпринята попытка использовать указатель, ничто не будет заботиться о его существовании. Напротив, основанные на ссылках инфраструктуры, такие как .NET или JVM, требуют, чтобы система всегда могла идентифицировать каждую ссылку на объект в существовании, и каждая ссылка на объект в существовании всегда должна быть либо null, либо идентифицировать объект своего объекта. правильный тип.

Обратите внимание, что каждая ссылка на объект фактически содержит два вида информации: (1) содержимое поля идентифицируемого объекта и (2) набор других ссылок на тот же объект. Хотя нет никакого механизма, с помощью которого система может быстро идентифицировать все ссылки, которые существуют на объект, набор других ссылок, которые существуют на объект, часто может быть самой важной вещью, инкапсулированной ссылкой (это особенно верно, когда вещи типа Object используются как вещи, такие как токены блокировки). Хотя система хранит несколько бит данных для каждого объекта для использования в GetHashCode, объекты не имеют реальной идентичности за пределами набора ссылок, которые существуют для них. Если X содержит единственную существующую ссылку на объект, замена X ссылкой на новый объект с тем же содержимым поля не будет иметь никакого идентифицируемого эффекта, за исключением изменения битов, возвращаемых GetHashCode(), и даже этот эффект не не гарантировано.

4 голосов
/ 10 января 2009

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

4 голосов
/ 10 января 2009

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

То есть переменная, содержащая ссылку, - это просто блок памяти размером с указатель, содержащий указатель на объект. Однако эту переменную нельзя использовать так же, как переменную указателя. В C # (и C, и C ++, ...) указатель может быть проиндексирован как массив, но ссылка не может. В C # ссылка отслеживается сборщиком мусора, указатель не может быть. В C ++ указатель может быть переназначен, а ссылка - нет. Синтаксически и семантически указатели и ссылки совершенно разные, но механически они одинаковы.

1 голос
/ 10 января 2009

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

0 голосов
/ 03 марта 2012

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

Указателей часто критикуют за «уродливость».

class* myClass = new class();

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

myClass->Method() or (*myClass).Method()

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

Для меня вот почему ссылки «родились» в первую очередь, чтобы обеспечить то же преимущество, что и указатели, но без всего этого синтаксиса указателей. Теперь вы можете передать фактический объект (а не только его значение) И у вас есть более читабельный, нормальный способ взаимодействия с объектом.

MyMethod(&type parameter)
{
   parameter.DoThis()
   parameter.DoThat()
}

Ссылки на C ++ отличались от ссылок на C # / Java тем, что как только вы присваивали ему значение, которое было им, вы не можете переназначить его (и оно должно быть назначено, когда оно было объявлено). Это было то же самое, что использование константного указателя (указатель, который нельзя перенаправить на другой объект).

Java и C # - это высокоуровневые современные языки, которые убрали много путаницы, накопившейся в C / C ++ за прошедшие годы, и указатели, безусловно, были одной из тех вещей, которые нужно было «очистить».

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

В этом конкретном примере, что знание о указателях поможет вам со ссылками? Понимание того, что ссылка на C # НЕ является самим объектом, а указывает на объект, является очень важной концепцией.

# 1: Вы НЕ передаете по значению Для начала, когда вы используете указатель, вы знаете, что указатель содержит только адрес, вот и все. Сама переменная почти пуста, и поэтому так приятно передавать в качестве аргументов. В дополнение к повышению производительности вы работаете с реальным объектом, поэтому любые сделанные вами изменения не являются временными

# 2: Полиморфизм / Интерфейсы Если у вас есть ссылка, которая является типом интерфейса, и она указывает на объект, вы можете вызывать только методы этого интерфейса, даже если у объекта может быть гораздо больше возможностей. Объекты могут также реализовывать одни и те же методы по-разному.

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

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

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