Заагч (Pointer)
Pointer бол Go-ийн хамгийн чухал ойлголтуудын нэг. Энэ нь эхлэгчдэд арай хүнд санагдаж болох ч аажим аажмаар ойлгоно. Pointer гэдэг нь утгыг шууд хадгалдаггүй — харин тэр утга санах ойн хаана байгааг хадгалдаг.
Компьютерийн санах ой нь дугаарлагдсан нүднүүдээс бүрддэг. Хувьсагч үүсгэхэд тэр хувьсагчийн утга эдгээр нүднүүдийн нэгэнд хадгалагдана. Pointer нь тэр нүдний дугаар (хаяг) юм.
& ба * операторууд
Go-д pointer-тай ажиллах хоёр үндсэн оператор байдаг:
&— хувьсагчийн хаягийг авна*— pointer-ийн заасан утгыг авна (дереференс)
package main
import "fmt"
func main() {
x := 42
p := &x // p нь x-ийн хаягийг хадгална
fmt.Println("x-ийн утга:", x) // 42
fmt.Println("x-ийн хаяг:", p) // 0x... (санах ойн хаяг)
fmt.Println("p-ийн заасан утга:", *p) // 42
}
Дэлгэцэнд ойролцоогоор:
x-ийн утга: 42
x-ийн хаяг: 0xc000018090
p-ийн заасан утга: 42
Санах ойн хаяг нь 0x тэмдэгтээр эхэлдэг арван зургаатын тоо юм. Таны компьютер дээр өөр тоо гарна — энэ нь ажиллах болгонд өөрчлөгдөнө.
Pointer ашиглан утга өөрчлөх
Pointer-г ашиглан эх хувьсагчийн утгыг өөрчлөх боломжтой:
package main
import "fmt"
func main() {
x := 10
p := &x
fmt.Println("Өмнө:", x) // 10
*p = 99 // p-ийн заасан хаягт 99 гэж бична
fmt.Println("Дараа:", x) // 99 — x өөрчлөгдсөн!
}
Дэлгэцэнд:
Өмнө: 10
Дараа: 99
*p = 99 гэдэг нь "pointer заасан хаягт буй утгыг 99 болго" гэсэн үг. x болон p нь санах ойн нэг нүдийг заасан учир x шууд өөрчлөгдөнө.
Функц дотор pointer дамжуулах
Go-д функцт хувьсагч дамжуулахад хуулбар үүснэ. Функц дотор өөрчилсөн ч эх хувьсагч өөрчлөгдөхгүй. Гэхдээ pointer дамжуулбал эх хувьсагчийг шууд өөрчилж болно:
package main
import "fmt"
// Pointer дамжуулаагүй — хуулбар өөрчлөгдөнө, эх нь үгүй
func нэмэхБуруу(n int) {
n = n + 1
}
// Pointer дамжуулсан — эх хувьсагч өөрчлөгдөнө
func нэмэхЗөв(n *int) {
*n = *n + 1
}
func main() {
тоо := 5
нэмэхБуруу(тоо)
fmt.Println("Буруу функцийн дараа:", тоо) // 5 — өөрчлөгдөөгүй
нэмэхЗөв(&тоо)
fmt.Println("Зөв функцийн дараа:", тоо) // 6 — өөрчлөгдсөн
}
Дэлгэцэнд:
Буруу функцийн дараа: 5
Зөв функцийн дараа: 6
Энэ ялгааг ойлгох нь маш чухал. Жишээлбэл, том struct-г функцт дамжуулахад хуулбар хийхийн оронд pointer дамжуулбал хурдтай, санах ой хэмнэлттэй байдаг.
new функц
Go-д new() функцийг ашиглан шинэ pointer үүсгэж болно:
package main
import "fmt"
func main() {
// new(int) нь тэгтэй тэнцүү int-ийн pointer үүсгэнэ
p := new(int)
fmt.Println("Утга:", *p) // 0
*p = 77
fmt.Println("Өөрчилсөн дараа:", *p) // 77
}
Дэлгэцэнд:
Утга: 0
Өөрчилсөн дараа: 77
new(int) нь санах ойд зай гаргаж, тэр зайн хаягийг буцаана. Эхний утга нь тухайн төрлийн default утга байна — int бол 0, bool бол false, string бол "".
Дараагийн хичээлд:
Массив (array) буюу нэг төрлийн утгуудын жагсаалтыг хэрхэн үүсгэж ашиглахыг судална. Массив нь Go-ийн slice-ийн суурь болдог тул энэ ойлголт маш чухал.