Что есть указатель? Указатель - это переменная, которая содержит адрес в памяти, по которому хранится некоторое значение. Используются указатели для работы с данными по адресу а не по значению. Что по существу следует знать об указателях в языке Go? Во-первых это самый факт их существования и использования. Во-вторых, это факт отсутствия в Go арифметики указателей. Пожалуй, в этом будет состоять самая краткая выжимка по данной теме.
Синтаксис аналогичен таковому в С/С++. Точно как и в С, присутствует 2 оператора: астериск * - так называемое разыменование указателя, и амперсанд & - взятие адреса. Проиллюстрируем это.
package main
import (
"fmt"
)
func main() {
var i int = 0 // переменная типа int
var p *int // объявление указателя на int
p = &i // p присваивается адрес i
fmt.Println("Адрес:", p) //выведем адрес
fmt.Println("Значение:", *p) //выведем значение (разыменование указателя)
*p = 5 // присвоение нового значения по данному адресу
fmt.Println("i =", i) // исходная переменная i изменилась
}
//Результат:
//Адрес: 0x40e020
//Значение: 0
//i = 5
Приведём более полезный пример - чтение пользовательского ввода:
package main
import (
"fmt"
)
func main() {
input := ""
for {
fmt.Println("Введите текст (q для выхода) :")
fmt.Scan(&input) //используем взятие адреса!
if input == "q" {
fmt.Println("Пока!..")
break
}
fmt.Println("Вы ввели: ", input)
}
}
//Результат:
//Введите текст (q для выхода) :
//Привет!
//Вы ввели: Привет!
//Введите текст (q для выхода) :
//q
//Пока!..
Таким образом можно организовать взаимодействие с пользователем в консоли. К слову, при работе с SQL запросами, также используются указатели. Но об SQL мы поговорим отдельно.
Оператор new
Что в языке Go делает оператор new? Ответ лаконичен - он создаёт указатели. Опробуем его в деле.
package main
import (
"fmt"
)
type T struct {
a, b int
}
func main() {
i := new(int) // указатель на int
fmt.Println(i) // в нём адрес
fmt.Printf("Type of i is %T\n", i) // прочитаем тип
fmt.Println(*i) // разыменование указателя, то есть взятие значения
t := new(T) // указатель на структуру T
fmt.Println(t)
fmt.Printf("Type of t is %T\n", t)
fmt.Println(*t)
}
//Результат:
//0xc00000a0e0
//Type of i is *int
//0
//&{0 0}
//Type of t is *main.T
//{0 0}
Как видите, всё достаточно прозрачно. Но на самом деле, оператор new применяется нечасто. Впрактике языка Go, обычно прибегают к явному объявлению указателей. Отчего чаще поступают так или иначе - на этот вопрос у автора нет точного ответа. Так или иначе - оператор new - этополноправный инструмент, которым можно и нужно пользоваться.
Напоследок, сообщу о том, что в языке Go вам, возможно, встретится такое явление, как указатель на указатель! Попытаюсь привести пример приемлемой сложности.
package main
import (
"github.com/gorilla/websocket"
"net/http"
)
/* глобальный дескриптор WebSocket соединения - указатель на указатель
так как сама импортируемая переменная типа websocket.Conn
является указателем! */
var wSconn **websocket.Conn // указатель на указатель!
// Создаётся WebSocket upgrader
var upgrader = websocket.Upgrader{
ReadBufferSize: 4096,
WriteBufferSize: 4096,
CheckOrigin: func(r *http.Request) bool { return true },
}
func MainHandler(w http.ResponseWriter, r *http.Request) {
conn, err := upgrader.Upgrade(w, r, nil)
if err != nil {
log.Print("upgrade:", err)
return
}
defer conn.Close()
//После установки соединения устанавливаем глобальный дескриптор
wSconn = &conn
// далее продолжение работы функции ===================
}
/*Теперь глобальный дескриптор может быть использован в любом месте кода
для отправки сообщений в вебсокет*/
buf := []byte("Hello, world!")
conn := *wSconn
err := conn.WriteMessage(websocket.TextMessage, buf)
if err != nil {
log.Println("Error : ", err)
}
На этом рассмотрение предмета указателей можно считать достаточно полным. Позже мы ещё встретимся с ними, изучая функции и методы, работу с SQL, сетевое программирование. Дальше - больше.