Статьи

Указатели против ссылок

Некоторые языки, включая C, C ++, поддерживают указатели. Другие языки, включая C ++, Java, Python, Ruby, Perl и PHP, поддерживают ссылки. На первый взгляд и ссылки, и указатели очень похожи, обе используются для того, чтобы одна переменная обеспечивала доступ к другой. Поскольку оба предоставляют множество одинаковых возможностей, часто неясно, чем отличаются эти разные механизмы. В этой статье я проиллюстрирую разницу между указателями и ссылками.

Почему это важно

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

Остальная часть этой статьи написана с намерением широко рассказать о концепции ссылок, а не о конкретной реализации. Мы будем использовать Go в качестве эталонной реализации для указателей.

В чем разница?

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

Ссылка — это переменная, которая ссылается на другую переменную.

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

int i = 3;
int *ptr = &i;
int &ref = i;

Первая строка просто определяет переменную. Второй определяет указатель на адрес памяти этой переменной. Третий определяет ссылку на первую переменную.

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

Продолжая наш пример, обе следующие строки изменят значение i на 13.

*ptr = 13;
ref = 13;

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

Рассмотрим следующий пример в Go:

package main

import "fmt"

var ap *int

func main() {
    a := 1          // define int
    b := 2          // define int

    ap = &a
     // set ap to address of a (&a)
     //   ap address: 0x2101f1018
     //   ap value  : 1

    *ap = 3
     // change the value at address &a to 3
     //   ap address: 0x2101f1018
     //   ap value  : 3

    a = 4
     // change the value of a to 4
     //   ap address: 0x2101f1018
     //   ap value  : 4

    ap = &b
     // set ap to the address of b (&b)
     //   ap address: 0x2101f1020
     //   ap value  : 2
}

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

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

Расширение функции выше:

    ...

    ap2 := ap
     // set ap2 to the address in ap
     //   ap  address: 0x2101f1020
     //   ap  value  : 2
     //   ap2 address: 0x2101f1020
     //   ap2 value  : 2

    *ap = 5
     // change the value at the address &b to 5
     //   ap  address: 0x2101f1020
     //   ap  value  : 5
     //   ap2 address: 0x2101f1020
     //   ap2 value  : 5
    // If this was a reference ap & ap2 would now
    // have different values

    ap = &a
     // change ap to address of a (&a)
     //   ap  address: 0x2101f1018
     //   ap  value  : 4
     //   ap2 address: 0x2101f1020
     //   ap2 value  : 5
    // Since we've changed the address of ap, it now
    // has a different value then ap2
}

Вы можете поэкспериментировать и поиграть сами на ходу play: http://play.golang.org/p/XJtdLxFoeO

Ключ к пониманию разницы во втором примере.

Если бы мы работали со ссылками, мы бы не смогли изменить значение b на * ap и отразить это в * ap2. Это потому, что, как только вы сделаете копию ссылки, они теперь независимы. Хотя они могут ссылаться на одну и ту же переменную, при манипулировании ссылкой она изменит то, на что она ссылается, а не на значение ссылки.

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

Оставайтесь с нами … В следующем посте будет представлено другое свойство, доступное только указателям, указатель указателя.

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