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

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

Таблица:

CREATE TABLE test
  (
     id   INT NOT NULL PRIMARY KEY IDENTITY, --made up key
     col1 INT, --first column to add, wasn't sure if this was nullable or not
     col2 INT, --second column to add, wasn't sure if this was nullable or not
     col3 INT NOT NULL --this is the column to optionally insert into
  ) 

определение триггера:

CREATE TRIGGER demo
ON test
INSTEAD OF INSERT
AS
    INSERT INTO test (col1,col2,col3)
    SELECT inserted.col1,
           inserted.col2,
           CASE
             WHEN inserted.col3 IS NULL THEN COALESCE(inserted.col1, 0) + COALESCE(inserted.col2, 0)
             ELSE inserted.col3
           END
    FROM   inserted

В основном он заменяет любую инструкцию insert, выполненную в таблице, с помощью клавиши в триггере, поэтому я проверяю с помощью временной таблицы inserted, чтобы увидеть, быть вставлен в наш необязательный столбец с нулевым значением, col3, является NULL. Если это так, я заменяю его добавлением col1 и col2 (я коалесцирую с нулем, поскольку вы не упомянули, являются ли два столбца источника нулевыми или нет).

Вы затем могут запускать вставные операторы, которые либо включают это, либо нет, несмотря на то, что col3 не является нулевым:

INSERT INTO test(col1,col2)
SELECT 12, 31
GO

INSERT INTO test(col1, col2, col3)
SELECT 1, 2, 89
GO

Результаты:

ID  COL1 COL2 COL3
------------------  
1   12   31    43
2   1    2     89

Если триггер wasn ' t там, вы могли бы получить ошибку, пытающуюся запустить эту первую инструкцию insert, сообщая вам, что она не может вставить NULL в col3.

Обратите внимание, что второй оператор insert, который указывает значение, не имеет было заменено добавлением в соответствии с запросом.

Вот рабочий SQL Fiddle .

3028
задан metamorphosis 17 June 2018 в 05:34
поделиться

16 ответов

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

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

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

10
ответ дан Don Wakefield 17 June 2018 в 05:34
поделиться
  • 1
    Да все еще имеет значение. Что, если они оставляют его открытым в течение длительного периода времени. За выходные? В то время как они едут в отпуск? – stimms 12 November 2009 в 01:32
  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), тогда как ссылка совместно использует тот же адрес памяти (с исходной переменной), но также и занимает некоторое место на стеке. Так как ссылка имеет тот же адрес как сама исходная переменная, безопасно думать о ссылке как о другом названии той же переменной.Примечание: Какой указатель указывает на, может быть на стеке или "куче". Так же ссылка. Мое требование в этом операторе не состоит в том, что указатель должен указать на стек. Указатель является просто переменной, которая содержит адрес памяти. Эта переменная находится на стеке. Так как ссылка имеет свое собственное пространство на стеке, и так как адрес совпадает с переменной, на которую это ссылается. Больше на [1 115] стек по сравнению с "кучей" . Это подразумевает, что существует действительный адрес ссылки, которую компилятор не скажет Вам.

    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. указатель А должен быть разыменован с [1 111] для доступа к ячейке памяти, на которую он указывает, тогда как ссылка может использоваться непосредственно. Указатель на класс/структуру использует -> для доступа, это - участники, тогда как ссылка использует ..

  7. указатель А является переменной, которая содержит адрес памяти. Независимо от того, как реализована ссылка, ссылка имеет тот же адрес памяти как объект, на который это ссылается.

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

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

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

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

1573
ответ дан 21 revs, 9 users 57% 17 June 2018 в 05:34
поделиться
  • 1
    I' m мнения, что определение как, туда, где стоимость / преимущество прибывает в эту проблему (фиксируют по сравнению с отпуском) обычно сводится к управлению; я обычно пытаюсь не попытаться сказать управление это I' ve, генерируя код, который пропускает память (пишущий его). – Paul Sonier 12 November 2009 в 01:38

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

173
ответ дан Peter Mortensen 17 June 2018 в 05:34
поделиться

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

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

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

цель ссылки А не может быть заменена всегда за исключением UB.

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

115
ответ дан Arkadiy 17 June 2018 в 05:34
поделиться
  • 1
    Для меня это было android:noHistory="true", что я удалил. – Poutrathor 20 August 2013 в 14:05

Вопреки популярному мнению возможно иметь ссылку, которая является ПУСТОЙ.

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

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

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

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

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

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)..., но компилятор мог бы оптимизировать оператор из существования! Действительная ссылка никогда не может быть ПУСТОЙ так от представления компилятора, сравнение всегда является ложью, и это свободно устранить 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));

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

Для другого примера опасностей разыменовать нулевого указателя см. Представлять неопределенное поведение при попытке портировать код на другую платформу Raymond Chen.

117
ответ дан Mark Ransom 17 June 2018 в 05:34
поделиться

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

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

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

108
ответ дан Peter Mortensen 17 June 2018 в 05:34
поделиться
  • 1
    +1, но, что при использовании SearchView и необходимо установить launchmode на SingleInstante; там какой-либо путь состоит в том, чтобы прекратить открывать новое действие для searchView?? – mohammad jannesary 23 May 2014 в 14:12

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

39
ответ дан Cole Johnson 17 June 2018 в 05:34
поделиться

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

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

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

15
ответ дан Peter Mortensen 17 June 2018 в 05:34
поделиться

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

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

, Например:

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 работать.

18
ответ дан Tim Cooper 17 June 2018 в 05:34
поделиться
  • 1
    Это решило мою проблему. Но, могли Вы делать объяснение почему это doesn' t работают, когда Вы делаете putExtra ()? – andrii 20 November 2011 в 16:13

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

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

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

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

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

int a = 0;
int& b = a;

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

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

int a;
increment(a);

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

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

63
ответ дан Peter Mortensen 17 June 2018 в 05:34
поделиться

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

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

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

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

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

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

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

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

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

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

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

354
ответ дан Peter Mortensen 17 June 2018 в 15:34
поделиться

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

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

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

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

12
ответ дан 22 November 2019 в 19:47
поделиться

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

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

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

13
ответ дан 22 November 2019 в 19:47
поделиться

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

Массивы, Указатели и Ссылки Под Капотом Всесторонняя Статья

Некоторые ключевые подходящие точки Указателей статьи

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

Ссылки

  • , Ссылки должны быть инициализированы, когда они объявляются.
  • Ссылки объявляются с помощью унарный суффиксный оператор оператора объявления &.
  • При инициализации ссылки, каждый использует название объекта, к которому они обратятся непосредственно без потребности в унарный префиксный оператор &
  • Когда-то инициализированный, на ссылки не могут указать на что-то еще присвоение или арифметическое управление
  • нет никакой потребности разыменовать ссылку, чтобы получить или установить содержание объекта, который это отсылает к [1 121]
  • , операции Присвоения на ссылке управляют содержанием объекта, на который это указывает на (после инициализации), не сама ссылка (не изменяется, где это указывает на)
  • , Арифметические операции на ссылке управляют содержанием объекта, на который это указывает, не сама ссылка (не изменяется, где это указывает на)
  • В в значительной степени всех реализациях, ссылка на самом деле хранится как адрес в память об отнесенном в объект. Следовательно, это поднимает устройство хранения данных в памяти, равной размеру адреса для целевой архитектуры машины точно так же, как объект указателя

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

1
ответ дан 22 November 2019 в 19:47
поделиться

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

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

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

int x = 1;
int *y = &x;
int &z = x;

Y является международным указателем, указывая на местоположение, где x хранится. X и Z относятся к тому же месту хранения (стек или "куча").

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

1) "Указателю можно повторно присвоить любое количество раз, в то время как ссылка не может быть повторно присвоена после привязки". - указатель является типом адресных сведений, который указывает на данные. Ссылка является другим названием данных. Таким образом, Вы можете 'повторно присваивать' ссылку. Вы просто не можете повторно присвоить местоположение данных, к которому это относится. Точно так же, как Вы не можете изменить местоположение данных, к которому относится 'x', Вы не можете сделать этого к 'z'.

x = 2;
*y = 2;
z = 2;

то же. Это - переназначение.

2) "Указатели нигде не могут указать (ПУСТОЙ УКАЗАТЕЛЬ), тогда как ссылка всегда относится к объекту" - снова с беспорядком. Ссылка является просто другим названием объекта. Нулевой указатель означает (семантически), что ни к чему не относится, тогда как ссылка была создана путем высказывания, что это было другое название 'x'. С тех пор

3) "Вы не можете взять адрес ссылки как Вы, может с указателями" - Да Вы можете. Снова с беспорядком. При попытке найти адрес указателя, который используется в качестве ссылки, которая является проблемой - ссылки причины не являются указателями на объект. Они - объект. Таким образом, можно получить адрес объекта, и можно получить адрес указателя. Причина они оба получают адрес данных (один местоположение объекта в памяти, другой указатель на местоположение объектов в памяти).

int *yz = &z; -- legal
int **yy = &y; -- legal

int *yx = &x; -- legal; notice how this looks like the z example.  x and z are equivalent.

4) "нет никакой "ссылочной арифметики"" - снова с беспорядком - так как пример выше имеет z быть ссылкой на x, и оба - поэтому целые числа, 'ссылочные' средние арифметические, например, добавляющие 1 к значению, упомянутому x.

x++;
z++;

*y++;  // what people assume is happening behind the scenes, but isn't. it would produce the same results in this example.
*(y++);  // this one adds to the pointer, and then dereferences it.  It makes sense that a pointer datatype (an address) can be incremented.  Just like an int can be incremented. 
0
ответ дан 22 November 2019 в 19:47
поделиться

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

0
ответ дан 22 November 2019 в 19:47
поделиться
Другие вопросы по тегам:

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