Какова реальная разница между указателями и ссылками?

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

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

Ответ 1

Все это просто косвенное: способность не иметь дело с данными, но сказать: "Я буду направлять вас к некоторым данным, там". У вас есть одна и та же концепция в Java и С#, но только в ссылочном формате.

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

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

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

Ответ 2

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

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

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

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

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

Ответ 3

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

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

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

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

Ответ 4

Это похоже на вопрос: "Какая эта навязчивая идея с инструкциями CPU? Я пропустил что-то, не разбрызгивая инструкции x86 MOV по всему месту?"

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

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

Ответ 5

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

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

Ответ 6

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

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

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

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

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

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

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

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

Ответ 7

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

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

Link1

LInk2

Ответ 8

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

void strcpy(char *dest, char *src)
{    
        while(*dest++ = *src++);
}

Ответ 9

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

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

Ответ 10

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

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

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

В Java вы постоянно используете указатели. Большинство переменных являются указателями на объекты - вот почему:

StringBuffer x = new StringBuffer("Hello");
StringBuffer y = x;
x.append(" boys");
System.out.println(y);

... печатает "Hello boys", а не "Hello".

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

Ответ 11

Строки являются фундаментальными для C (и других родственных языков). При программировании на C, вы должны управлять своей памятью. Вы не просто говорите: "ладно, мне понадобится куча струн"; вам нужно подумать о структуре данных. Сколько памяти вам нужно? Когда вы его выделите? Когда вы освободите его? Скажем, вам нужно 10 строк, каждая из которых не более 80 символов.

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

char dict[10][81];

Обратите внимание, кстати, что dict не является "строкой" или "массивом", либо "char". Это указатель. Когда вы пытаетесь распечатать одну из этих строк, все, что вы делаете, это передать адрес одного символа; C предполагает, что если он только начинает печатать символы, он в конечном итоге ударит нуль. И он предполагает, что если вы находитесь в начале одной строки, и вы переходите на 81 байт, вы будете в начале следующей строки. И, фактически, взяв ваш указатель и добавив к нему 81 байт, это единственный возможный способ перейти к следующей строке.

Итак, почему важны указатели? Потому что вы не можете ничего сделать без них. Вы даже не можете сделать что-то простое, как распечатать кучу строк; вы , безусловно, не можете делать ничего интересного, например, реализовать связанные списки, хеши или очереди или деревья или файловую систему, или какой-либо код управления памятью, или ядро ​​или... что угодно. Вам НЕОБХОДИМО понять их, потому что C просто передает вам блок памяти и позволяет вам делать все остальное, и для выполнения чего-либо с блоком необработанной памяти требуются указатели.

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

Теперь я свободно признаю, что программирование с указателями не требуется в 90% кода, написанного сегодня, и на самом деле это совершенно опасно в производственном коде. ОК. Это здорово. И функциональное программирование на практике просто не используется. Согласовано.

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

От здесь. Отличная статья.

Ответ 12

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

Ответ 13

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

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

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

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

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

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

Ответ 14

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

SomeObject store[100];
int a_ptr = 20;
SomeObject A = store[a_ptr];

Одна из проблем с этим подходом заключается в том, что после того, как я изменил "A", мне пришлось бы переназначить его в массив "store", чтобы изменения были постоянными:

store[a_ptr] = A;

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

После того, как я научился понимать указатели, я отказался от реализации подхода адресации массива. Аналогия все еще довольно актуальна. Просто подумайте, что массив "store" управляется временем выполнения языка программирования.

SomeObject A;
SomeObject* a_ptr = &A;
// Any changes to a_ptr contents hereafter will affect
// the one-true-object that it addresses. No need to reassign.

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

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

Ответ 15

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

Ответ 16

Указатели важны! Они "указывают" на адрес памяти, а многие внутренние структуры представлены как указатели, IE. Массив строк фактически представляет собой список указателей на указатели! Указатели также могут использоваться для обновления переменных, передаваемых в функции.

Ответ 17

Вам нужны они, если вы хотите генерировать "объекты" во время выполнения без предварительной выделения памяти в стеке

Ответ 18

Эффективность параметров - передача указателя (Int - 4 байта) в отличие от копирования целого (достаточно большого) объекта.

Java-классы передаются через ссылку (в основном, указатель) и btw, а именно в java, которая скрыта от программиста.

Ответ 19

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

С указателями есть две вещи:

  • Указатели на указатели, адресация и т.д. могут быть очень загадочными. Это приводит к ошибкам, и их трудно прочитать.
  • Память, на которую указывают указатели, часто выделяется из кучи, что означает, что вы несете ответственность за освобождение этой памяти. Чем больше ваше приложение, тем сложнее выполнить это требование, и вы получите утечки памяти, которые трудно отследить.

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

Ответ 20

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

Насколько я знаю, Java и ActionScript не поддерживают небезопасные коды и указатели.

Ответ 21

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

Рассмотрим даже простую функцию подкачки. Если у вас есть

void swap (int и a, int и b)

или

процедура Swap (var a, b: integer)

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

То же самое с объектами --- не думайте об объектных идентификаторах как указатели или ссылки на "материал". Вместо этого просто подумайте о них, как о объектах OBJECTS, на которые вы можете отправлять сообщения. Даже в таких примитивных языках, как С++, вы можете намного быстрее продвигаться, думая (и записывая) на максимально высоком уровне.

Ответ 22

Напишите более двух строк c или С++, и вы узнаете.

Они являются "указателями" на расположение памяти переменной. Это похоже на передачу переменной по ссылочному виду.