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

2802

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

Но каковы различия?


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

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

Чтобы прояснить заблуждение:

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

int &ri = i;

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

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

Как общее правило,

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

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

  • 1
    Локальная ссылка (т. Е. Не в структуре или классе) не обязательно выделяет память. Вы можете сказать это из-за разницы в размерах sizeof (int &) и sizeof (struct {int & x;}).
  • 81
    Я думаю, что пункт 2 должен быть «Указатель может быть NULL, но ссылка - нет. Только искаженный код может создать ссылку NULL, и его поведение не определено».
Показать ещё 31 комментарий
Теги:
pointers
reference
c++-faq

37 ответов

1396
  • Указатель можно переназначить:

    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;
    
  • Указатель имеет свой собственный адрес и размер памяти в стеке (4 байта на x86), тогда как ссылка имеет один и тот же адрес памяти (с исходной переменной), но также занимает некоторое место в стеке. Поскольку ссылка имеет тот же адрес, что и исходная переменная, безопасно думать о ссылке как о другом имени для той же переменной. Примечание. То, на что указывает указатель, может быть в стеке или в куче. То же ссылка. Моя претензия в этом утверждении заключается не в том, что указатель должен указывать на стек. Указатель - это просто переменная, содержащая адрес памяти. Эта переменная находится в стеке. Поскольку ссылка имеет свое собственное пространство в стеке, а так как адрес совпадает с переменной, которую он ссылается. Подробнее о стек против кучи. Это означает, что существует реальный адрес ссылки, который компилятор вам не скажет.

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

    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);
    
  • Указателю можно назначить 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
    
  • Указатели могут выполнять итерацию по массиву, вы можете использовать ++ для перехода к следующему элементу, на который указывает указатель, и + 4, чтобы перейти к 5-му элементу. Это независимо от того, какой размер указывает объект, на который указывает указатель.

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

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

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

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

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

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

  • 2
    Ссылка может быть нулевой. В некоторых случаях вы передаете параметры ссылки следующим образом: function (* ptr); Если ptr равен NULL, то ссылка тоже будет.
  • 18
    ... но разыменование NULL не определено. Например, вы не можете проверить, имеет ли ссылка значение NULL (например, & ref == NULL).
Показать ещё 40 комментариев
324

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

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

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

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

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

Рассмотрим следующий оператор из Часто задаваемые вопросы по С++:

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

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

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

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

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

  • 1
    @Christoph: ну ссылки могут быть висят, только если вы получили его где-то путем разыменования некоторого указателя.
  • 16
    @kriss: Нет, вы также можете получить висячую ссылку, вернув автоматическую переменную по ссылке.
Показать ещё 13 комментариев
159

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

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, он не должен компилироваться. Вы не можете привязывать неконстантную ссылку к временному объекту, и вы не можете принять его адрес в этом отношении.

  • 5
    но какой вариант использования для этого?
  • 17
    Что ж, s3_copy создаст временный файл, а затем скопирует его в s3_copy, тогда как s3_reference напрямую использует временный. Затем, чтобы быть по-настоящему педантичным, вам нужно взглянуть на оптимизацию возвращаемого значения, при которой компилятору разрешается исключать конструкцию копирования в первом случае.
Показать ещё 6 комментариев
114

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

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

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

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

Фактическая ошибка заключается в разыменовании указателя NULL перед назначением ссылки. Но я не знаю каких-либо компиляторов, которые будут генерировать любые ошибки при этом условии - ошибка распространяется до точки далее в коде. Это делает эту проблему настолько коварной. В большинстве случаев, если вы разыгрываете указатель 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);

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

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

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));

Для более старого взгляда на эту проблему у кого-то, у кого лучшие навыки написания, см. Null References от Jim Hyslop и Herb Sutter.

Для другого примера опасности разыменования нулевого указателя см. Отображение поведения undefined при попытке переноса кода на другую платформу Раймонда Чен.

  • 4
    В качестве придирки я бы сказал, что ссылка на самом деле не нулевая - она ссылается на плохую память. Несмотря на то, что это правильное утверждение, то, что оно является справочным, не означает, что оно относится к чему-то действительному
  • 58
    Данный код содержит неопределенное поведение. Технически, вы ничего не можете сделать с нулевым указателем, кроме как установить его и сравнить. Как только ваша программа вызывает неопределенное поведение, она может делать все что угодно, в том числе работать правильно, пока вы не дадите демонстрацию большому боссу.
Показать ещё 16 комментариев
106

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

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

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

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

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

  • 5
    Я думаю, что это лучший ответ на сегодняшний день. Другие говорят о ссылках и указателях, как будто они разные звери, а затем рассказывают, как они отличаются по поведению. Это не облегчает имхо. Я всегда понимал ссылки как T* const с разным синтаксическим сахаром (это исключает много * и & из вашего кода).
  • 2
    «Цель константного указателя может быть заменена путем взятия его адреса и использования константного приведения». Это неопределенное поведение. См. Stackoverflow.com/questions/25209838/… для получения подробной информации.
Показать ещё 3 комментария
105

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

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

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

  • 4
    @Orion Edwards> членский доступ с использованием указателей ->> членский доступ с использованием ссылок. Это не на 100% верно. Вы можете иметь ссылку на указатель. В этом случае вы получите доступ к элементам указателя, на который нет ссылок, используя -> struct Node {Node * next; }; Узел * первый; // p - ссылка на указатель void foo (Node * & p) {p-> next = first; } Node * bar = новый узел; Foo (бар); - ОП: Вы знакомы с понятиями rvalues и lvalues?
  • 3
    Умные указатели имеют оба. (методы в классе интеллектуальных указателей) и -> (методы в базовом типе).
Показать ещё 4 комментария
60

Собственно, ссылка не похожа на указатель.

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

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

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

Теперь некоторое объяснение реального кода:

int a = 0;
int& b = a;

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

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

int a;
increment(a);

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

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

  • 2
    Ссылка - это ссылка на l-значение, необязательно на переменную. Из-за этого он намного ближе к указателю, чем к реальному псевдониму (конструкции времени компиляции). Примеры выражений, на которые можно ссылаться: * p или даже * p ++
  • 5
    Правильно, я просто указал на тот факт, что ссылка не всегда помещает новую переменную в стек, как это делает новый указатель.
Показать ещё 3 комментария
59

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

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

В качестве примера:

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 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]. Это довольно легко сделать. я не меньше 2, поэтому array [i] никогда не может ссылаться на массив [1]. maybeModify() присваивается a0 как ссылка (aliasing array [0]). Поскольку нет "ссылочной" арифметики, компилятор просто должен доказать, что возможноModify никогда не получает адрес 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];
    }
}

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

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

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

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

F createF(int argument);

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

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

  • 0
    Правда, тело должно быть видимым. Однако определить, что maybeModify не берет адрес чего-либо, связанного с x , существенно проще, чем доказать, что куча арифметики указателей не происходит.
  • 0
    Я полагаю, что оптимизатор уже делает то, что «куча арифметики указателей не происходит» проверяет множество других причин.
Показать ещё 2 комментария
36

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

  • 9
    См. Ответ Марка Рэнсома на контрпример. Это наиболее часто утверждаемый миф о ссылках, но это миф. Единственная гарантия, которую вы имеете по стандарту, состоит в том, что у вас сразу же появляется UB, когда у вас есть ссылка NULL. Но это все равно, что сказать: «Эта машина безопасна, она никогда не сможет сойти с дороги. (Мы не несем никакой ответственности за то, что может случиться, если вы все равно уведите ее с дороги. Она может просто взорваться»).
  • 16
    @cmaster: в действующей программе ссылка не может быть нулевой. Но указатель может. Это не миф, это факт.
Показать ещё 8 комментариев
34

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

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

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, а не сама ссылка. После назначения две ссылки по-прежнему относятся к их исходным объектам, и значение этих объектов теперь тоже самое.

  • 0
    «ссылка всегда ссылается на объект» просто полностью ложна
27

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

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

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

23

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

    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. 
18

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

>>> 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.

  • 1
    И если у класса есть ссылочная переменная, он должен быть инициализирован либо nullptr, либо допустимым объектом в списке инициализации.
  • 1
    Формулировка в этом ответе слишком запутанна, чтобы иметь реальный смысл. Кроме того, @Misgevolution, вы серьезно рекомендуете читателям инициализировать ссылку с nullptr ? Вы на самом деле читали какую-либо другую часть этой темы, или ...?
Показать ещё 1 комментарий
18

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

int& j = i;

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

int* const j = &i;
  • 13
    Это не то, что говорит стандарт C ++, и компилятору не нужно реализовывать ссылки так, как описано в вашем ответе.
  • 0
    @jogojapan: Любой способ, который допустим для компилятора C ++ для реализации ссылки, также является допустимым способом для реализации const указателя. Эта гибкость не доказывает, что существует различие между ссылкой и указателем.
Показать ещё 2 комментария
18

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

С другой стороны, одно существенное различие между ссылками и указателями заключается в том, что временные ссылки, назначенные для ссылок на константу, живут до тех пор, пока ссылка на 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 работать.

  • 1
    Вы не можете взять адрес ссылки, но это не значит, что они физически не занимают места. Запретить оптимизацию они наверняка могут.
  • 2
    Влияние, несмотря на то, что «ссылка на стек не занимает места вообще» является явно ложным.
Показать ещё 2 комментария
15

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

#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;
}
14

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

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

  • 1
    Ссылки и указатели являются ручками. Они оба дают вам семантику, где ваш объект передается по ссылке, но дескриптор копируется. Нет разницы. (Есть и другие способы иметь дескрипторы, такие как ключ для поиска в словаре)
  • 0
    Я тоже раньше так думал. Но посмотрите связанную статью, объясняющую, почему это не так.
Показать ещё 4 комментария
14

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

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

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

  • 3
    Вы можете написать &r + offset где r был объявлен как ссылка
13

Рискуя добавить в замешательство, я хочу добавить некоторые данные, я уверен, что это в основном зависит от того, как компилятор реализует ссылки, но в случае 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 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? Ну, очевидно, что он компилируется отлично, но вызывает ошибку сегментации во время выполнения, поскольку он больше не указывает на допустимую переменную, мы по существу имеем сломанную ссылку, которая все еще существует (пока она не выпадает из области), но бесполезна.

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

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

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

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

12

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

Ссылки менее мощные, чем указатели

1) После создания ссылки впоследствии нельзя ссылаться на другой объект; он не может быть пересмотрен. Это часто делается с указателями.

2) Ссылки не могут быть NULL. Указатели часто делают NULL, чтобы указать, что они не указывают на какую-либо действительную вещь.

3) При объявлении эта ссылка должна быть инициализирована. Нет такого ограничения с указателями

Из-за вышеуказанных ограничений ссылки на С++ не могут использоваться для реализации структур данных, таких как Linked List, Tree и т.д. В Java ссылки не имеют ограничений и могут использоваться для реализации всех структур данных. Ссылки, более мощные в Java, являются основной причиной, по которой Java не нуждается в указателях.

Ссылки более безопасны и просты в использовании:

1) Безопаснее: поскольку ссылки должны быть инициализированы, дикие ссылки, такие как дикие указатели, вряд ли будут существовать. По-прежнему возможно иметь ссылки, которые не относятся к допустимому местоположению

2) Прост в использовании: для доступа к значению не требуется оператор разыменования. Они могут использоваться как обычные переменные. "& Амп; оператор нужен только во время объявления. Кроме того, к элементам ссылки объекта можно обращаться с помощью оператора точки ('.), В отличие от указателей, в которых для доступа к элементам необходим оператор стрелки (- > ).

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

12

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

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

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

10

Эта программа может помочь в понимании ответа на вопрос. Это простая программа ссылки "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

10

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

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

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

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

9

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

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

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

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

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

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

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

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

  • 0
    « неявное приведение » приведение - это синтаксическая конструкция, она существует в грамматике; бросок всегда явный
9

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

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;
}

По умолчанию используется ссылка "привязка константы к временному" аспекту ссылок.

8

Как указатель, ссылка является псевдонимом для объекта, обычно реализован для хранения машинного адреса объекта и не налагать накладные расходы по сравнению с указателями, но он отличается от указатель в том, что:

• Вы получаете доступ к ссылке с точно таким же синтаксисом, что и имя объект.

• Ссылка всегда ссылается на объект, для которого он был инициализирован.

• Нет нулевой ссылки, и мы можем предположить, что ссылка относится к объекту.

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

Например:

template<class T>
class vector {
T∗ elem;
// ...
public:
T& operator[](int i) { return elem[i]; } // return reference to element
const T& operator[](int i) const { return elem[i]; } // return reference to const element
void push_back(const T& a); // pass element to be added by reference
// ...
};
void f(const vector<double>& v)
{
double d1 = v[1]; // copy the value of the double referred to by v.operator[](1) into d1
v[2] = 7; // place 7 in the double referred to by the result of v.operator[](2)
v.push_back(d1); // give push_back() a reference to d1 to wor k with
}
8

Возможно, некоторые метафоры помогут; В контексте экрана рабочего стола -

  • Ссылка требует указания фактического окна.
  • Указатель требует расположения части пространства на экране, которое, как вы уверяете, будет содержать ноль или более экземпляров этого типа окна.
7

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

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 указателя.)

Некоторые утверждают, что это такая полезная функция при чтении кода, что параметры указателя всегда следует использовать для изменяемых параметров, а не non- const ссылки, даже если функция не ожидает nullptr. То есть эти люди утверждают, что подписи функций, такие как fn3() выше, не должны быть разрешены. Руководящие принципы стиля Google C++ являются примером этого.

6

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

Указатель может быть инициализирован равным 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 "="
  • 1
    У нас все еще может быть const int& i = 0 .
  • 1
    В этом случае ссылка будет использоваться только в режиме чтения. Мы не можем изменить эту ссылку const, даже используя const_cast, потому что const_cast принимает только указатель, а не ссылку.
Показать ещё 2 комментария
5

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

2

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

// 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 ok
}
2

Я всегда решаю это правило из основных принципов С++:

Предпочитают T * по сравнению с T & когда "no argument" является допустимым вариантом

  • 1
    Использование перегруженных функций, которые не принимают указатели вместо разрешения nullptr , или использование терминальных объектов, является спорным гораздо лучшим решением, вместо использования nullptr качестве аргументов.
  • 1
    @Clearer Возможно, он чище, но иногда вам просто нужно быстро передать указатели, и могут быть случаи, когда вам все равно, является ли указатель нулевым или нет.
1

Вы можете использовать разницу между ссылками и указателями, если следовать правилам аргументов, переданных функции. Константные ссылки предназначены для данных, передаваемых в функцию, а указатели - для данных, переданных из функции. В других языках, вы можете четко фиксировать это с помощью ключевых слов, таких, как in и out. В C++ вы можете объявить (по соглашению) эквивалент. Например,

void DoSomething(const Foo& thisIsAnInput, Foo* thisIsAnOutput)
{
   if (thisIsAnOuput)
      *thisIsAnOutput = thisIsAnInput;
}

Использование ссылок в качестве входов и указателей в качестве выходов является частью руководства по стилю Google.

  • 2
    Нет ничего по сути входного или выходного в отношении указателей по сравнению со ссылками. Важным отличием является наличие или отсутствие const .
  • 1
    Я согласен, нет ничего изначально входного или выходного. То, что я упоминал, было просто соглашением (которое я не изобрел). Соглашение является частью руководства по стилю Google . Есть одно преимущество указателей в качестве вывода, и они могут быть обнуляемыми, если вы не хотите выводить.
Показать ещё 1 комментарий
0

Помимо всех ответов здесь,

Вы можете реализовать перегрузку операторов, используя ссылки:

my_point operator+(const my_point& a, const my_point& b)
{
  return { a.x + b.x, a.y + b.y };
}

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

  • 1
    Дело не в том, что он не будет вызван: вы даже не можете объявить такой оператор с двумя параметрами указателя; Согласно черновому варианту « Операторская функция должна быть нестатической функцией-членом или не-функцией-членом, имеющей как минимум один параметр, тип которого является классом, ссылкой на класс, перечислением или ссылкой на перечисление . "([over.oper] / 6)
0

Тарын сказал:

Вы не можете взять адрес ссылки, как вы можете с указателями.

На самом деле вы можете.

Я цитирую ответ на другой вопрос:

C++ FAQ гласит:

В отличие от указателя, когда ссылка привязана к объекту, она не может быть "переустановлена" для другого объекта. Сама ссылка не является объектом (она не имеет идентификатора; взятие адреса ссылки дает вам адрес референта; помните: ссылка является его референтом).

  • 5
    Цитата, которую вы даете, противоречит вашей собственной точке зрения. Это довольно ясно говорит, что сама ссылка не имеет адреса.
  • 0
    При работе с выражением, которое называет ссылку, вы имеете дело с референтом.
0

Если вы берете указатель в стандарте C99 и указываете его на NULL, а затем ссылаетесь на указатель, он смертельно падает с компьютера. Если бы вы установили эту программу в качестве начальной загрузки в BIOS, и если бы вы могли распространять вирус достаточно далеко, вы бы фатально нарушили глобальную сеть. Каково решение этого фундаментального недостатка C?

Это довольно дорогостоящий, если бы владелец в глобальной сети, владевший ПК, подавал иск по искам класса или даже государство могло пойти за вами, если ущерб будет большим и достаточно заметным, так что это не весело. Кстати, мой учитель C и UNIX преподал мне первый параграф в Райерсоне. Ну, если NULL в конечном итоге ничего не значит, а не указывая ни на что, вы должны сразу указывать на все, но если вы указываете на все, это не занимает много времени и энергии? Это проблема для решения, и теперь можно сделать настоящую компьютерную науку.

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

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

-3

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

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

Ещё вопросы

Сообщество Overcoder
Наверх
Меню