Разница между указателем и ссылкой в ​​c? - PullRequest
11 голосов
/ 14 февраля 2011

В чем разница между указателем, ссылкой и разыменованием в c?

Ответы [ 9 ]

20 голосов
/ 14 февраля 2011

Вот карта памяти;представление памяти в виде последовательности блоков:

    address    01   02   03
             +----+----+----+...
data within  | 23 | 6f | 4a |
             +----+----+----+...

Теперь предположим, что мы создаем символ:

char c = 'z';  // 'z' is 7a in hex

Далее предположим, что c хранится по адресу 01, поэтому нашпамять выглядит так:

    address    01   02   03
             +----+----+----+...
data within  | 7a | 6f | 4a |
             +----+----+----+...

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

char* p = &c;  // point at c

p может храниться по адресу 02:

    address    01   02   03
             +----+----+----+...
data within  | 7a | 01 | 4a |
             +----+----+----+...

Здесь указатель p находится по адресу 02, а указывает по адресу 01.В этом смысл p = &c;.Когда мы разыменовываем указатель p (по адресу 02), мы смотрим на то, что в адресе, указанном p.То есть p указывает на адрес 01, поэтому разыменование p означает поиск внутри адреса 01.

Наконец, давайте создадим ссылку:

char& r = c;

Здесьрасположение памяти не меняется.То есть память не используется для хранения r.r является своего рода псевдонимом для c, поэтому, когда мы ссылаемся на r, мы практически ссылаемся на c.r и c концептуально едины.Изменение r означает изменение c, а изменение c означает изменение r.

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

Также связаны const ссылки .Это то же самое, что и ссылка, за исключением того, что они неизменны:

const char& r = c;
r = 'y';  // error; you may not change c through r
c = 'y'   // ok. and now r == 'y' as well

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

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

6 голосов
/ 14 февраля 2011

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

  • Указатель - это отдельный адрес памяти. Примерная диаграмма того, как это происходит в памяти:

    | Address  | Value          |
    |----------|----------------|
    |0x1111    |0x1112          | <-- Pointer!
    |0x1112    |42              | <-- Pointed value
    |0x1113    |42              | <-- Some other value
    

    Я использовал гораздо меньший размер адреса просто для простоты. По сути, 0x1111 является указателем, поскольку его содержимое является адресом другого значения.

  • Разыменование означает проверку значения адреса, содержащегося в значении указателя. Такой причудливый язык может сбивать с толку; в основном, если я разыменую 0x1111, я смотрю на 0x1112 и получаю значение из этого адреса. Зачем? Потому что это действительно полезно и потому что сборка позволяет нам делать это тоже,

    mov rax, [r8]
    

    Это синтаксис nasm / intel для "посмотрите в r8, найдите этот адрес памяти, следуйте по нему, найдите значение по этому адресу памяти и поместите его в rax".

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

    void add(int* x)
    {
        *x = *x + 7;
    }
    

    работа.

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

    void cppadd(int& x)
    {
        int a = 7;
        x = &a; // doesn't work.
    }
    

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

Википедия обобщает это довольно хорошо:

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

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

5 голосов
/ 14 февраля 2011

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

2 голосов
/ 20 августа 2015

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

int  c1;
int* p1;
c1 = 5;
p1 = &c1;
//p1 references c1

Разыменование указателя означает использование оператора * (символ звездочки) для доступа к значению, хранящемуся в указателе: ПРИМЕЧАНИЕ. Значение, хранящееся по адресу указателя, должно быть значением ОДНОГО ТИПА в качестве типа переменной, на которую «указывает» указатель, но нет никакой гарантии, что это такесли указатель не был установлен правильно.Тип переменной, на которую указывает указатель, - это тип, который меньше внешней звездочки.

int n1;
n1 = (*p1);

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

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

Ссылки:

http://www.codingunit.com/cplusplus-tutorial-pointers-reference-and-dereference-operators

& is the reference operator and can be read as “address of”.
* is the dereference operator and can be read as “value pointed by”.

http://www.cplusplus.com/doc/tutorial/pointers/

& is the reference operator    
* is the dereference operator

Вы также можете читать вики. Оператор разыменования * также называется оператором косвенного обращения.

Этот текст взят из этой ссылки, он дал тот же ответна тот же вопрос: значение слов "ссылка" и "разыменование"

2 голосов
/ 14 февраля 2011

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

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

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

1 голос
/ 14 февраля 2011

Значением указателя является адрес памяти.

int a;
int* b = &a;
// b holds the memory address of a, not the value of a.

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

int a;
int* b = &a;
// b is a reference to a.

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

int a;
int* b = &a;
int c = *b;
// c dereferences b, meaning that c will be set with the value stored in the address that b contains.

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

0 голосов
/ 06 апреля 2019

Согласно моему опыту, позвольте мне ответить.

В C ++ есть переменные (обычные переменные, переменные-указатели и т. Д.) И ссылки.

Компилятор назначит адрес длякаждая переменная.Очевидно, что адрес этой переменной не может выглядеть одинаково.Можно сказать, что каждая переменная на самом деле является адресом.Что такое ссылка &, ссылка не переменная, а тег, поэтому компилятор не назначит ему адрес, но это не означает, что он не имеет адреса, его адрес является адресом переменной или объекта, к которому он относится, поэтому он используется в качестве тега. Используется для установки его адреса на адрес переменной или объекта, на который он ссылается, когда его анализирует компилятор.Это создает ситуацию, когда адрес совпадает с адресом переменной или объекта, на который он ссылается, неверие Может скомпилировать и обдумать или попробовать GDB!

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

За последовательность и последовательность!например:

class box {
 private:
  int l;

 public:
  box(int length = 0) : l(length){};
  box operator+(const box& that) {
    box b;
    b.l = this->l + that.l;
    return b;
  }
  box operator+(const box* that) {
    box b;
    b.l = this->l + that->l;
    return b;
  }
};

int main() {
  box b1(2);
  box b2(4);

  box b3 = b1 + b2;
  box b4 = b1 + &b2;

  return 0;
}

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

0 голосов
/ 13 мая 2012

как в "C ++ ...":

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

0 голосов
/ 14 февраля 2011

Указатель - это адрес некоторых данных, например, int* a. Здесь a - это просто адрес, где хранится значение типа int. Ссылка, напротив, является другим именем для некоторой переменной, псевдонимом, например, int a; int &b = a Здесь b - это просто другое имя для a: b++ имеет тот же эффект, что и a++.

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