Каковы различия между переменной-указателем и ссылочной переменной в C ++?

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

Но в чем различия?


Резюме из ответов и ссылок ниже:

  1. Указатель может быть переназначен любое количество раз, в то время как ссылка не может быть переназначена после привязки.
  2. Указатели не могут указывать нигде (NULL), тогда как ссылка всегда ссылается на объект.
  3. Вы не можете использовать адрес ссылки, как вы можете использовать указатели.
  4. Там нет "ссылочной арифметики" (но вы можете взять адрес объекта, на который указывает ссылка, и сделать арифметику указателя на нем, как в &obj + 5

Чтобы уточнить неправильное представление:

  

Стандарт C ++ очень осторожен, чтобы не диктовать, как компилятор может   реализовать ссылки, но каждый компилятор C ++ реализует   ссылки как указатели. То есть объявление, такое как:

int &ri = i;
     

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

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

Как правило,

  • Используйте ссылки в параметрах функций и возвращаемых типах для предоставления полезных и самодокументируемых интерфейсов.
  • Используйте указатели для реализации алгоритмов и структур данных.

Интересное чтение:

2794 голоса | спросил prakash 12 thEurope/Moscowp30Europe/Moscow09bEurope/MoscowFri, 12 Sep 2008 00:03:57 +0400 2008, 00:03:57

29 ответов


0
  1. Указатель можно переназначить:

    int x = 5;
    int y = 6;
    int *p;
    p =  &x;
    p = &y;
    *p = 10;
    assert(x == 5);
    assert(y == 10);
    

    Ссылка не может и должна быть назначена при инициализации:

    int x = 5;
    int y = 6;
    int &r = x;
    
  2. Указатель имеет свой собственный адрес памяти и размер в стеке (4 байта в x86), тогда как ссылка разделяет тот же адрес памяти (с исходной переменной), но также занимает некоторое место в стеке. Поскольку ссылка имеет тот же адрес, что и сама исходная переменная, можно с уверенностью рассматривать ссылку как другое имя для той же переменной. Примечание. То, на что указывает указатель, может быть в стеке или куче. Так же ссылка. Мое утверждение в этом утверждении не в том, что указатель должен указывать на стек. Указатель - это просто переменная, которая содержит адрес памяти. Эта переменная находится в стеке. Поскольку ссылка имеет свое собственное пространство в стеке, а адрес совпадает с переменной, на которую она ссылается. Подробнее о стеке против кучи . Это означает, что существует реальный адрес ссылки, которую компилятор вам не скажет.

    int x = 0;
    int &r = x;
    int *p = &x;
    int *p2 = &r;
    assert(p == p2);
    
  3. У вас могут быть указатели на указатели, указывающие на дополнительные уровни косвенности. Принимая во внимание, что ссылки предлагают только один уровень косвенности.

    int x = 0;
    int y = 0;
    int *p = &x;
    int *q = &y;
    int **pp = &p;
    pp = &q;//*pp = q
    **pp = 4;
    assert(y == 4);
    assert(x == 0);
    
  4. Указатель может быть назначен nullptr напрямую, а ссылка - нет. Если вы достаточно стараетесь и знаете, как, вы можете указать адрес ссылки nullptr. Аналогичным образом, если вы попытаетесь сделать это достаточно усердно, у вас может быть ссылка на указатель, и тогда эта ссылка может содержать nullptr.

    int *p = nullptr;
    int &r = nullptr; <--- compiling error
    int &r = *p;  <--- likely no compiling error, especially if the nullptr is hidden behind a function call, yet it refers to a non-existent int at address 0
    
  5. Указатели могут перебирать массив, вы можете использовать ++, чтобы перейти к следующему элементу, на который указывает указатель и + 4 для перехода к 5-му элементу. Это не имеет значения, на какой размер объекта указывает указатель.

  6. На указатель необходимо разыменовать с помощью *, чтобы получить доступ к области памяти, на которую он указывает, тогда как ссылку можно использовать непосредственно. Указатель на класс /структуру использует -> для доступа к его членам, тогда как ссылка использует .

  7. Указатель - это переменная, которая содержит адрес памяти. Независимо от того, как реализована ссылка, ссылка имеет тот же адрес памяти, что и элемент, на который она ссылается.

  8. Ссылки не могут быть вставлены в массив, тогда как указатели могут быть (упомянуты пользователем @litb)

  9. Const ссылки могут быть связаны с временными. Указатели не могут (не без некоторой косвенности):

    const int &x = int(12); //legal C++
    int *y = &int(12); //illegal to dereference a temporary.
    

    Это делает const& более безопасным для использования в списках аргументов и т. д.

ответил H S Progr 1 J000000Sunday18 2018, 21:52:37
0

Что такое ссылка на C ++ ( для программистов на C )

ссылка может рассматриваться как указатель на константу (не путать с указателем на постоянное значение!) с автоматическим перенаправлением, то есть компилятор будет применять оператор * для вас.

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

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

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

Рассмотрим следующее утверждение из C ++ FAQ :

  

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

Но если ссылка действительно была объектом, как могли быть висячие ссылки? В неуправляемых языках невозможно, чтобы ссылки были более «безопасными», чем указатели - как правило, просто не существует способа надежного псевдонима значений через границы области действия!

Почему я считаю ссылки на C ++ полезными

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

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

ответил Christoph 28 FebruaryEurope/MoscowbSat, 28 Feb 2009 00:26:44 +0300000000amSat, 28 Feb 2009 00:26:44 +030009 2009, 00:26:44
0

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

std::string s1 = "123";
std::string s2 = "456";

std::string s3_copy = s1 + s2;
const std::string& s3_reference = s1 + s2;

В этом примере s3_copy копирует временный объект, который является результатом конкатенации. Тогда как s3_reference по сути становится временным объектом. Это действительно ссылка на временный объект, который теперь имеет то же время жизни, что и ссылка.

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

ответил Matt Price 12 thEurope/Moscowp30Europe/Moscow09bEurope/MoscowFri, 12 Sep 2008 01:43:16 +0400 2008, 01:43:16
0

Вопреки распространенному мнению, существует ссылка на NULL.

int * p = NULL;
int & r = *p;
r = 1;  // crash! (if you're lucky)

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

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

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

Мой пример выше короткий и надуманный. Вот более реальный пример.

class MyClass
{
    ...
    virtual void DoSomething(int,int,int,int,int);
};

void Foo(const MyClass & bar)
{
    ...
    bar.DoSomething(i1,i2,i3,i4,i5);  // crash occurs here due to memory access violation - obvious why?
}

MyClass * GetInstance()
{
    if (somecondition)
        return NULL;
    ...
}

MyClass * p = GetInstance();
Foo(*p);

Я хочу повторить, что единственный способ получить нулевую ссылку - это искаженный код, и как только вы его получите, вы получите неопределенное поведение. никогда имеет смысл проверять нулевую ссылку; Например, вы можете попробовать if(&bar==NULL)..., но компилятор может оптимизировать оператор из-за отсутствия! Допустимая ссылка никогда не может быть NULL, поэтому, с точки зрения компилятора, сравнение всегда ложно, и можно свободно исключать предложение if как мертвое. Код - это сущность неопределенного поведения.

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

template<typename T>
T& deref(T* p)
{
    if (p == NULL)
        throw std::invalid_argument(std::string("NULL reference"));
    return *p;
}

MyClass * p = GetInstance();
Foo(deref(p));

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

Еще один пример опасности разыменования нулевого указателя см. в разоблачении неопределенное поведение при попытке переноса кода на другую платформу от Raymond Chen.

ответил Mark Ransom 12 thEurope/Moscowp30Europe/Moscow09bEurope/MoscowFri, 12 Sep 2008 01:06:49 +0400 2008, 01:06:49
0

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

Обновление: теперь, когда я еще немного об этом подумаю, появилась важная разница.

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

Цель ссылки не может быть заменена каким-либо образом, кроме UB.

Это должно позволить компилятору оптимизировать ссылки.

ответил Arkadiy 12 thEurope/Moscowp30Europe/Moscow09bEurope/MoscowFri, 12 Sep 2008 00:07:29 +0400 2008, 00:07:29
0

Вы забыли самую важную часть:

членский доступ с указателями использует ->
членский доступ со ссылками использует .

foo.bar значительно явно превосходит foo->bar точно так же, как vi является явным превосходит Emacs : -)

ответил Orion Edwards 12 thEurope/Moscowp30Europe/Moscow09bEurope/MoscowFri, 12 Sep 2008 02:10:49 +0400 2008, 02:10:49
0

На самом деле ссылка не очень похожа на указатель.

Компилятор хранит «ссылки» на переменные, связывая имя с адресом памяти; это его работа по преобразованию любого имени переменной в адрес памяти при компиляции.

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

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

Теперь немного пояснений к реальному коду:

int a = 0;
int& b = a;

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

void increment(int& n)
{
    n = n + 1;
}

int a;
increment(a);

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

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

ответил Vincent Robert 19 thEurope/Moscowp30Europe/Moscow09bEurope/MoscowFri, 19 Sep 2008 16:23:19 +0400 2008, 16:23:19
0

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

  • Ссылки составлены таким образом, что компилятору существенно легче отслеживать, какие ссылочные псевдонимы какие переменные. Две важные особенности очень важны: нет «ссылочной арифметики» и нет переназначения ссылок. Это позволяет компилятору выяснить, какие ссылки ссылаются на какие переменные во время компиляции.
  • Ссылки могут ссылаться на переменные, которые не имеют адресов памяти, например, те, которые компилятор выбирает для включения в регистры. Если вы берете адрес локальной переменной, компилятору будет очень трудно поместить его в регистр.

Как пример:

void maybeModify(int& x); // may modify x in some way

void hurtTheCompilersOptimizer(short size, int array[])
{
    // This function is designed to do something particularly troublesome
    // for optimizers. It will constantly call maybeModify on array[0] while
    // adding array[1] to array[2]..array[size-1]. There's no real reason to
    // do this, other than to demonstrate the power of references.
    for (int i = 2; i < (int)size; i++) {
        maybeModify(array[0]);
        array[i] += array[1];
    }
}

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

void hurtTheCompilersOptimizer(short size, int array[])
{
    // Do the same thing as above, but instead of accessing array[1]
    // all the time, access it once and store the result in a register,
    // which is much faster to do arithmetic with.
    register int a0 = a[0];
    register int a1 = a[1]; // access a[1] once
    for (int i = 2; i < (int)size; i++) {
        maybeModify(a0); // Give maybeModify a reference to a register
        array[i] += a1;  // Use the saved register value over and over
    }
    a[0] = a0; // Store the modified a[0] back into the array
}

Для такой оптимизации необходимо доказать, что ничто не может изменить массив [1] во время вызова. Это довольно легко сделать. i никогда не меньше 2, поэтому массив [i] никогда не может ссылаться на массив [1]. MaybeModify () получает a0 в качестве ссылки (псевдоним массива [0]). Поскольку здесь нет «ссылочной» арифметики, компилятору нужно только доказать, что MaybeModify никогда не получает адрес x, и он доказал, что ничего не меняет массив [1].

Это также должно доказать, что нет никаких способов, которыми будущий вызов мог бы прочитать /записать [0], пока у нас есть временная регистровая копия этого в a0. Это часто бывает тривиально доказать, потому что во многих случаях очевидно, что ссылка никогда не сохраняется в постоянной структуре, такой как экземпляр класса.

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

void maybeModify(int* x); // May modify x in some way

void hurtTheCompilersOptimizer(short size, int array[])
{
    // Same operation, only now with pointers, making the
    // optimization trickier.
    for (int i = 2; i < (int)size; i++) {
        maybeModify(&(array[0]));
        array[i] += array[1];
    }
}

Поведение такое же; только теперь гораздо сложнее доказать, что MaybeModify никогда не модифицирует массив [1], потому что мы уже дали ему указатель; кошка вышла из сумки. Теперь он должен сделать гораздо более сложное доказательство: статический анализ MaybeModify, чтобы доказать, что он никогда не записывает в & x + 1. Он также должен доказать, что он никогда не сохраняет указатель, который может ссылаться на массив [0], который это так же сложно.

Современные компиляторы становятся все лучше и лучше в статическом анализе, но всегда приятно помогать им и использовать ссылки.

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

РЕДАКТИРОВАТЬ: Через пять лет после публикации этого ответа я обнаружил реальное техническое различие, в котором ссылки отличаются от простого взгляда на одну и ту же концепцию адресации. Ссылки могут изменить продолжительность жизни временных объектов так, как указатели не могут.

F createF(int argument);

void extending()
{
    const F& ref = createF(5);
    std::cout << ref.getArgument() << std::endl;
};

Обычно временные объекты, такие как объект, созданный при вызове createF(5), уничтожаются в конце выражения. Однако, связывая этот объект со ссылкой, ref, C ++ продлит срок службы этого временного объекта до ref выходит за рамки.

ответил Cort Ammon 1 stEurope/Moscowp30Europe/Moscow09bEurope/MoscowSun, 01 Sep 2013 07:44:41 +0400 2013, 07:44:41
0

Ссылка никогда не может быть NULL.

ответил 12 thEurope/Moscowp30Europe/Moscow09bEurope/MoscowFri, 12 Sep 2008 00:12:35 +0400 2008, 00:12:35
0

Хотя ссылки и указатели используются для косвенного доступа к другому значению, между ссылками и указателями есть два важных различия. Во-первых, ссылка всегда ссылается на объект: ошибка в определении ссылки без ее инициализации. Поведение присваивания является вторым важным отличием: назначение ссылки изменяет объект, к которому привязана ссылка; он не перепривязывает ссылку на другой объект. После инициализации ссылка всегда ссылается на один и тот же базовый объект.

Рассмотрим эти два фрагмента программы. В первом мы присваиваем один указатель другому:

int ival = 1024, ival2 = 2048;
int *pi = &ival, *pi2 = &ival2;
pi = pi2;    // pi now points to ival2

После назначения ival объект, адресуемый pi, остается неизменным. Присвоение изменяет значение pi, заставляя его указывать на другой объект. Теперь рассмотрим похожую программу, которая назначает две ссылки:

int &ri = ival, &ri2 = ival2;
ri = ri2;    // assigns ival2 to ival

Это назначение изменяет ival, значение, на которое ссылается ri, а не саму ссылку. После назначения две ссылки по-прежнему ссылаются на свои исходные объекты, и значение этих объектов теперь также остается неизменным.

ответил Kunal Vyas 20 Maypm11 2011, 23:26:37
0

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

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

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

ответил Lightness Races in Orbit 29 +03002014-10-29T20:17:23+03:00312014bEurope/MoscowWed, 29 Oct 2014 20:17:23 +0300 2014, 20:17:23
0

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

    void fun(int &a, int &b); // A common usage of references.
    int a = 0;
    int &b = a; // b is an alias for a. Not so common to use. 
ответил fatma.ekici 1 Jpm1000000pmTue, 01 Jan 2013 21:45:46 +040013 2013, 21:45:46
0

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

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

Например:

class scope_test
{
public:
    ~scope_test() { printf("scope_test done!\n"); }
};

...

{
    const scope_test &test= scope_test();
    printf("in scope\n");
}

напечатает:

in scope
scope_test done!

Это языковой механизм, который позволяет ScopeGuard работать.

ответил MSN 13 thEurope/Moscowp30Europe/Moscow09bEurope/MoscowSat, 13 Sep 2008 03:27:06 +0400 2008, 03:27:06
0

Это основано на учебнике . То, что написано, делает это более ясным:

>>> The address that locates a variable within memory is
    what we call a reference to that variable. (5th paragraph at page 63)

>>> The variable that stores the reference to another
    variable is what we call a pointer. (3rd paragraph at page 64)

Просто чтобы запомнить это,

>>> reference stands for memory location
>>> pointer is a reference container (Maybe because we will use it for
several times, it is better to remember that reference.)

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

Посмотрите на следующее утверждение,

int Tom(0);
int & alias_Tom = Tom;

alias_Tom можно понимать как alias of a variable (отличается от typedef, что alias of a type) Tom. Также можно забыть, что терминология такого утверждения заключается в создании ссылки на Tom.

ответил Life 13 Jpm1000000pmMon, 13 Jan 2014 17:14:16 +040014 2014, 17:14:16
0

Ссылка не является другим именем, данным некоторой памяти. Это неизменный указатель, который автоматически разыменовывается при использовании. В основном это сводится к:

int& j = i;

Это внутренне становится

int* const j = &i;
ответил tanweer alam 26 FebruaryEurope/MoscowbTue, 26 Feb 2013 09:20:32 +0400000000amTue, 26 Feb 2013 09:20:32 +040013 2013, 09:20:32
0

Ссылка на указатель возможна в C ++, но обратное невозможно, это означает, что указатель на ссылку невозможен. Ссылка на указатель обеспечивает более чистый синтаксис для изменения указателя. Посмотрите на этот пример:

#include<iostream>
using namespace std;

void swap(char * &str1, char * &str2)
{
  char *temp = str1;
  str1 = str2;
  str2 = temp;
}

int main()
{
  char *str1 = "Hi";
  char *str2 = "Hello";
  swap(str1, str2);
  cout<<"str1 is "<<str1<<endl;
  cout<<"str2 is "<<str2<<endl;
  return 0;
}

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

#include<stdio.h>
/* Swaps strings by swapping pointers */
void swap1(char **str1_ptr, char **str2_ptr)
{
  char *temp = *str1_ptr;
  *str1_ptr = *str2_ptr;
  *str2_ptr = temp;
}

int main()
{
  char *str1 = "Hi";
  char *str2 = "Hello";
  swap1(&str1, &str2);
  printf("str1 is %s, str2 is %s", str1, str2);
  return 0;
}

Посетите следующую страницу для получения дополнительной информации о ссылке на указатель:

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

#include <iostream>
using namespace std;

int main()
{
   int x = 10;
   int *ptr = &x;
   int &*ptr1 = ptr;
}
ответил Destructor 9 FebruaryEurope/MoscowbMon, 09 Feb 2015 16:17:16 +0300000000pmMon, 09 Feb 2015 16:17:16 +030015 2015, 16:17:16
0

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

С уважением, & Амп; rzej

ответил Andrzej 6 FebruaryEurope/MoscowbMon, 06 Feb 2012 12:59:29 +0400000000pmMon, 06 Feb 2012 12:59:29 +040012 2012, 12:59:29
0

Я использую ссылки, если мне не нужен ни один из них:

  • Нулевые указатели могут использоваться как ценность дозорного, часто дешевый способ Избегайте перегрузки функций или использования bool.

  • Вы можете выполнять арифметику с указателем. Например, p += offset;

ответил Aardvark 12 thEurope/Moscowp30Europe/Moscow09bEurope/MoscowFri, 12 Sep 2008 17:41:58 +0400 2008, 17:41:58
0

Риск добавления к путанице, я хочу добавить некоторые входные данные, я уверен, что это в основном зависит от того, как компилятор реализует ссылки, но в случае с gcc идея, что ссылка может указывать только на переменную в стеке на самом деле не правильно, возьмем это, например:

#include <iostream>
int main(int argc, char** argv) {
    // Create a string on the heap
    std::string *str_ptr = new std::string("THIS IS A STRING");
    // Dereference the string on the heap, and assign it to the reference
    std::string &str_ref = *str_ptr;
    // Not even a compiler warning! At least with gcc
    // Now lets try to print it's value!
    std::cout << str_ref << std::endl;
    // It works! Now lets print and compare actual memory addresses
    std::cout << str_ptr << " : " << &str_ref << std::endl;
    // Exactly the same, now remember to free the memory on the heap
    delete str_ptr;
}

Что выводит это:

THIS IS A STRING
0xbb2070 : 0xbb2070

Если вы заметили, что даже адреса памяти совпадают, это означает, что ссылка успешно указывает на переменную в куче! Теперь, если вы действительно хотите стать причудливым, это тоже работает:

int main(int argc, char** argv) {
    // In the actual new declaration let immediately de-reference and assign it to the reference
    std::string &str_ref = *(new std::string("THIS IS A STRING"));
    // Once again, it works! (at least in gcc)
    std::cout << str_ref;
    // Once again it prints fine, however we have no pointer to the heap allocation, right? So how do we free the space we just ignorantly created?
    delete &str_ref;
    /*And, it works, because we are taking the memory address that the reference is
    storing, and deleting it, which is all a pointer is doing, just we have to specify
    the address with '&' whereas a pointer does that implicitly, this is sort of like
    calling delete &(*str_ptr); (which also compiles and runs fine).*/
}

Что выводит это:

THIS IS A STRING

Поэтому ссылка - это указатель изнутри, они оба просто хранят адрес памяти, где адрес, на который указывает адрес, не имеет значения, как вы думаете, что произойдет, если я вызову std :: cout <<str_ref; ПОСЛЕ вызова delete & str_ref? Что ж, очевидно, что он хорошо компилируется, но вызывает ошибку сегментации во время выполнения, потому что он больше не указывает на допустимую переменную, у нас по существу есть неработающая ссылка, которая все еще существует (пока она не выпадает из области видимости), но бесполезна.

Другими словами, ссылка - это не что иное, как указатель, у которого абстрагирована механика указателя, что делает его более безопасным и простым в использовании (без случайной математики указателя, без смешения '.' и '->' и т. д. ), при условии, что вы не пробуете чепуху, как мои примеры выше;)

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

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

Помните, мои примеры выше, просто примеры, демонстрирующие, что такое ссылка, вы никогда не захотите использовать ссылку таким образом! Для правильного использования ссылки здесь уже есть множество ответов, которые бьют по голове

ответил Tory 15 +04002014-10-15T01:38:00+04:00312014bEurope/MoscowWed, 15 Oct 2014 01:38:00 +0400 2014, 01:38:00
0

Другое отличие состоит в том, что вы можете иметь указатели на тип void (а это означает указатель на что-либо), но ссылки на void запрещены.

int a;
void * p = &a; // ok
void & p = a;  //  forbidden

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

ответил kriss 29 Jpm1000000pmFri, 29 Jan 2010 18:15:18 +030010 2010, 18:15:18
0

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

void increment(int *ptrint) { (*ptrint)++; }
void increment(int &refint) { refint++; }
void incptrtest()
{
    int testptr=0;
    increment(&testptr);
}
void increftest()
{
    int testref=0;
    increment(testref);
}

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

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

ответил Adisak 15 +04002009-10-15T05:57:37+04:00312009bEurope/MoscowThu, 15 Oct 2009 05:57:37 +0400 2009, 05:57:37
0

Эта программа может помочь понять ответ на вопрос. Это простая программа со ссылкой «j» и указателем «ptr», указывающим на переменную «x».

#include<iostream>

using namespace std;

int main()
{
int *ptr=0, x=9; // pointer and variable declaration
ptr=&x; // pointer to variable "x"
int & j=x; // reference declaration; reference to variable "x"

cout << "x=" << x << endl;

cout << "&x=" << &x << endl;

cout << "j=" << j << endl;

cout << "&j=" << &j << endl;

cout << "*ptr=" << *ptr << endl;

cout << "ptr=" << ptr << endl;

cout << "&ptr=" << &ptr << endl;
    getch();
}

Запустите программу, посмотрите на вывод, и вы поймете.

Кроме того, уделите 10 минут и посмотрите это видео: https://www.youtube.com/watch?v=rlJrrGV0iOg

ответил Arlene Batada 15 MaramFri, 15 Mar 2013 07:03:08 +04002013-03-15T07:03:08+04:0007 2013, 07:03:08
0

Другое интересное использование ссылок - предоставление аргумента по умолчанию определенного пользователем типа:

class UDT
{
public:
   UDT() : val_d(33) {};
   UDT(int val) : val_d(val) {};
   virtual ~UDT() {};
private:
   int val_d;
};

class UDT_Derived : public UDT
{
public:
   UDT_Derived() : UDT() {};
   virtual ~UDT_Derived() {};
};

class Behavior
{
public:
   Behavior(
      const UDT &udt = UDT()
   )  {};
};

int main()
{
   Behavior b; // take default

   UDT u(88);
   Behavior c(u);

   UDT_Derived ud;
   Behavior d(ud);

   return 1;
}

Вариант по умолчанию использует ссылку 'bind const reference на временную' ссылку.

ответил Don Wakefield 12 thEurope/Moscowp30Europe/Moscow09bEurope/MoscowFri, 12 Sep 2008 21:59:56 +0400 2008, 21:59:56
0

Я чувствую, что есть еще один момент, который здесь не освещался.

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

Хотя это может показаться поверхностным, я считаю, что это свойство имеет решающее значение для ряда функций C ++, например:

  • Шаблоны . Поскольку параметры шаблона имеют тип утка, синтаксические свойства типа - это все, что имеет значение, поэтому часто один и тот же шаблон может использоваться с обоими T и T&.
    (или std::reference_wrapper<T>, который все еще полагается на неявное приведение T&)
    Шаблоны, которые охватывают как T&, так и T&& даже чаще.

  • Lvalues ​​. Рассмотрим выражение str[0] = 'X'; Без ссылок это будет работать только для c-строк (char* str). Возвращая символ по ссылке, пользовательские классы могут иметь одинаковые обозначения.

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

  • Операторские перегрузки . С помощью ссылок можно ввести косвенное обращение к вызову оператора - скажем, operator+(const T& a, const T& b), сохранив ту же запись инфикса. Это также работает для обычных перегруженных функций.

Эти пункты дают возможность значительной части C ++ и стандартной библиотеки, так что это довольно важное свойство ссылок.

ответил Ap31 6 J000000Thursday17 2017, 22:48:46
0

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

void fn1(std::string s);
void fn2(const std::string& s);
void fn3(std::string& s);
void fn4(std::string* s);

void bar() {
    std::string x;
    fn1(x);  // Cannot modify x
    fn2(x);  // Cannot modify x (without const_cast)
    fn3(x);  // CAN modify x!
    fn4(&x); // Can modify x (but is obvious about it)
}

Назад в C вызов, который выглядит как fn(x), может быть передан только по значению, поэтому он определенно не может изменить x; чтобы изменить аргумент, вам нужно передать указатель fn(&x). Таким образом, если аргументу не предшествовал &, вы знали, что он не будет изменен. (Обратное, & означает «изменено», не соответствует действительности, поскольку иногда вам придется передавать большие структуры только для чтения с помощью const указатель.)

Некоторые утверждают, что это такая полезная функция при чтении кода, что параметры указателя всегда следует использовать для изменяемых параметров, а не для const

ответил Arthur Tacca 2 42017vEurope/Moscow11bEurope/MoscowThu, 02 Nov 2017 14:16:02 +0300 2017, 14:16:02
0

Разница между указателем и ссылкой

Указатель может быть инициализирован на 0, а ссылка - нет. Фактически, ссылка должна также ссылаться на объект, но указатель может быть нулевым указателем:

int* p = 0;

Но у нас не может быть int& p = 0;, а также int& p=5 ;

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

Int x = 0;
Int y = 5;
Int& p = x;
Int& p1 = y;

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

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

Int a = 6, b = 5;
Int& rf = a;

Cout << rf << endl; // The result we will get is 6, because rf is referencing to the value of a.

rf = b;
cout << a << endl; // The result will be 5 because the value of b now will be stored into the address of a so the former value of a will be erased

Еще один момент: когда у нас есть шаблон, подобный шаблону STL, такой шаблон класса всегда будет возвращать ссылку, а не указатель, чтобы облегчить чтение или присвоение нового значения с помощью оператора []:

Std ::vector<int>v(10); // Initialize a vector with 10 elements
V[5] = 5; // Writing the value 5 into the 6 element of our vector, so if the returned type of operator [] was a pointer and not a reference we should write this *v[5]=5, by making a reference we overwrite the element by using the assignment "="
ответил dhokar.w 6 Jpm1000000pmFri, 06 Jan 2017 17:01:10 +030017 2017, 17:01:10
0

Разница заключается в том, что переменная-указатель с непостоянными значениями (не путать с указателем на константу) может быть изменена во время выполнения программы, требует использования семантики указателя (&, *), а ссылки быть установленным только при инициализации (поэтому вы можете установить их только в списке инициализатора конструктора, но не иначе) и использовать семантику доступа к обычным значениям. В основном, ссылки были введены для поддержки перегрузки операторов, как я читал в одной очень старой книге. Как кто-то заявил в этой теме - указатель может быть установлен в 0 или любое другое значение, которое вы хотите. 0 (NULL, nullptr) означает, что указатель инициализируется ничем. Ошибка разыменования нулевого указателя. Но на самом деле указатель может содержать значение, которое не указывает на какое-то правильное расположение в памяти. Ссылки, в свою очередь, стараются не позволять пользователю инициализировать ссылку на что-то, на что нельзя ссылаться из-за того, что вы всегда предоставляете ему правильное значение. Несмотря на то, что существует множество способов инициализации ссылочной переменной в неправильном месте в памяти, лучше не вдаваться в подробности. На уровне машины и указатель, и ссылка работают равномерно - с помощью указателей. Допустим, в основных ссылках приведены синтаксические сахара. Rvalue ссылки отличаются от этого - они, естественно, объекты стека /кучи.

ответил Zorgiev 24 PMpSun, 24 Apr 2016 16:22:20 +030022Sunday 2016, 16:22:20
0

Я всегда решаю с помощью a-valid-option" rel =" nofollow noreferrer "> это правило из C ++ Core Guidelines:

  

Предпочитаю T *, а не T & когда «без аргумента» является допустимым параметром

ответил Hitokage 19 +03002017-10-19T14:50:17+03:00312017bEurope/MoscowThu, 19 Oct 2017 14:50:17 +0300 2017, 14:50:17
0

У меня есть аналогия для ссылок и указателей, думаю, что ссылки - это другое имя объекта, а указатели - адрес объекта.

// receives an alias of an int, an address of an int and an int value
public void my_function(int& a,int* b,int c){
    int d = 1; // declares an integer named d
    int &e = d; // declares that e is an alias of d
    // using either d or e will yield the same result as d and e name the same object
    int *f = e; // invalid, you are trying to place an object in an address
    // imagine writting your name in an address field 
    int *g = f; // writes an address to an address
    g = &d; // &d means get me the address of the object named d you could also
    // use &e as it is an alias of d and write it on g, which is an address so it's ok
}
ответил Immac 12 72017vEurope/Moscow11bEurope/MoscowSun, 12 Nov 2017 01:59:51 +0300 2017, 01:59:51

Похожие вопросы

Популярные теги

security × 330linux × 316macos × 2827 × 268performance × 244command-line × 241sql-server × 235joomla-3.x × 222java × 189c++ × 186windows × 180cisco × 168bash × 158c# × 142gmail × 139arduino-uno × 139javascript × 134ssh × 133seo × 132mysql × 132