8-bo‘lim
Ko'rsatkichlar
& va * amallari, qiymat bo'yicha uzatish, funksiyada o'zgartirish, nil ko'rsatkich, new va Go da ko'rsatkich arifmetikasi yo'qligi.
Ushbu bo‘lim mundarijasi
Ko'rsatkich - bu qiymatning manzili. Go da ular bor, lekin C dagiga qaraganda ancha xavfsiz.
Muammo: hamma narsa nusxalanadi #
package main
import "fmt"
func oshir(son int) {
son = son + 10
}
func main() {
ball := 50
oshir(ball)
fmt.Println(ball)
}
50
Hech narsa o'zgarmadi. oshir funksiyasi ball ning nusxasini
oldi va o'sha nusxani o'zgartirdi.
Go da hamma narsa qiymat bo'yicha uzatiladi. Istisno yo'q.
Yechim: manzilni uzatish #
package main
import "fmt"
func oshir(son *int) {
*son = *son + 10
}
func main() {
ball := 50
oshir(&ball)
fmt.Println(ball)
}
60
Ikkita yangi belgi bor:
| Belgi | Nomi | Nima qiladi |
|---|---|---|
&ball | Manzil olish | O'zgaruvchining manzilini beradi |
*int | Ko'rsatkich turi | "int ga ko'rsatkich" |
*son | Manzildan o'qish | Manzildagi qiymatga boradi |
* belgisi ikki xil ma'noda ishlatiladi: tur yozilishida (*int) va
qiymatga murojaatda (*son).
Ko'rsatkichni ko'rish #
package main
import "fmt"
func main() {
ball := 50
manzil := &ball
fmt.Println(ball)
fmt.Println(*manzil)
fmt.Println(manzil == &ball)
*manzil = 75
fmt.Println(ball)
}
50
50
true
75
manzil va &ball - bir xil narsa. *manzil orqali yozganingizda,
ball ning o'zi o'zgaradi.
Yuqoridagi rasmda 0xc000018030 ko'rsatilgan, lekin bu son har bir
ishga tushirishda boshqacha bo'ladi.
Shuning uchun bu darslikdagi misollarda manzilning o'zi hech qachon
chiqarilmaydi - faqat *manzil orqali olingan qiymat yoki
taqqoslash natijasi chiqariladi. Aks holda kutilgan natija sizda mos
kelmasdi.
nil ko'rsatkich #
package main
import "fmt"
func main() {
var manzil *int
fmt.Println(manzil == nil)
ball := 42
manzil = &ball
fmt.Println(manzil == nil, *manzil)
}
true
false 42
Ko'rsatkichning nol qiymati - nil, ya'ni "hech qayerga
ko'rsatmaydi".
Agar manzil nil bo'lsa va siz *manzil deb yozsangiz, dastur
vahima (panic) bilan to'xtaydi:
panic: runtime error: invalid memory address or nil pointer dereference
Bu Go da eng ko'p uchraydigan ishlash vaqti xatolaridan biri.
Himoya usuli oddiy - ishlatishdan oldin tekshiring:
if manzil != nil {
fmt.Println(*manzil)
}
Vahima va undan qutulishni 15-bo'limda ko'ramiz.
Funksiyada tuzilmani o'zgartirish #
package main
import "fmt"
type Talaba struct {
Ism string
Ball int
}
func oshirNusxa(t Talaba) {
t.Ball += 10
}
func oshirManzil(t *Talaba) {
t.Ball += 10
}
func main() {
talaba := Talaba{Ism: "Aziza", Ball: 80}
oshirNusxa(talaba)
fmt.Println("Nusxadan keyin:", talaba.Ball)
oshirManzil(&talaba)
fmt.Println("Manzildan keyin:", talaba.Ball)
}
Nusxadan keyin: 80
Manzildan keyin: 90
Bu ko'rsatkichlarning eng ko'p uchraydigan ishlatilishi. Strukturalarni 11-bo'limda batafsil o'rganamiz.
E'tibor bering: oshirManzil ichida (*t).Ball emas, oddiy
t.Ball yozilgan.
Go ko'rsatkich orqali maydonga murojaat qilishni o'zi hal qiladi.
C da t->Ball deb alohida belgi yozish kerak edi, Go da esa nuqta
ikkala holatda ham ishlaydi.
Xuddi shunday, &talaba o'rniga metodlar bilan ishlaganda Go ko'pincha
& ni ham o'zi qo'yadi - buni 12-bo'limda ko'ramiz.
Ko'rsatkich qaytaruvchi funksiya #
package main
import "fmt"
type Talaba struct {
Ism string
Ball int
}
func yangiTalaba(ism string) *Talaba {
t := Talaba{Ism: ism, Ball: 0}
return &t
}
func main() {
a := yangiTalaba("Kamola")
a.Ball = 95
fmt.Println(a.Ism, a.Ball)
}
Kamola 95
C da mahalliy o'zgaruvchining manzilini qaytarish jiddiy xato edi: funksiya tugagach, o'sha xotira yo'qolardi.
Go da bu mutlaqo xavfsiz. Kompilyator t ning manzili tashqariga
chiqayotganini ko'radi va uni avtomatik ravishda uyumda (heap)
joylashtiradi.
Bu tahlil qochib chiqish tahlili (escape analysis) deb ataladi. Siz hech narsa yozmaysiz - Go o'zi qaror qiladi va xotirani kerak bo'lmay qolganda avtomatik tozalaydi.
new funksiyasi #
package main
import "fmt"
func main() {
manzil := new(int)
fmt.Println(*manzil)
*manzil = 42
fmt.Println(*manzil)
ball := 42
boshqa := &ball
fmt.Println(*boshqa)
}
0
42
42
new(int) - yangi int yaratib, uning manzilini qaytaradi. Qiymat
nol bo'ladi.
Amalda new kam ishlatiladi: &Talaba{...} shakli ancha qulayroq,
chunki qiymatlarni darhol berish mumkin.
Ko'rsatkich arifmetikasi yo'q #
C da ko'rsatkichga son qo'shib, qo'shni xotiraga o'tish mumkin edi:
p++; // C da: keyingi elementga o'tadi
*(p + 3) // C da: uchinchi qo'shnini o'qiydi
Go da bu umuman yo'q. manzil++ deb yozib bo'lmaydi.
Sabab - xavfsizlik. Ko'rsatkich arifmetikasi C dagi eng jiddiy xavfsizlik zaifliklarining manbai: chegaradan chiqib, begona xotirani o'qish yoki yozish.
Go da massiv bo'ylab yurish uchun slice ishlatiladi va u har safar chegarani tekshiradi. Buni keyingi bo'limda ko'ramiz.
Qachon ko'rsatkich ishlatish kerak #
| Holat | Tavsiya |
|---|---|
| Funksiyada qiymatni o'zgartirish kerak | Ko'rsatkich |
| Katta struktura uzatilmoqda | Ko'rsatkich |
| "Qiymat yo'q" holatini ko'rsatish kerak | Ko'rsatkich (nil) |
| Kichik struktura, faqat o'qiladi | Qiymat |
int, string, bool | Qiymat |
Yangi boshlaganlar ko'pincha ko'rsatkichlarni haddan tashqari ko'p ishlatadi.
Go da kichik qiymatni nusxalash juda arzon - ko'pincha ko'rsatkich orqali murojaatdan tezroq, chunki protsessor keshiga yaxshiroq tushadi.
Qoida: o'zgartirish kerak bo'lmasa, oddiy qiymat yozing. Keyinroq
kerak bo'lsa, * qo'shish oson.
Slice va map - alohida holat #
package main
import "fmt"
func ozgartir(royxat []int) {
royxat[0] = 999
}
func main() {
sonlar := []int{1, 2, 3}
ozgartir(sonlar)
fmt.Println(sonlar)
}
[999 2 3]
Bu yerda & yozilmagan, lekin o'zgarish saqlanib qoldi.
Sababi: slice ichida haqiqiy ma'lumotga ko'rsatkich saqlanadi.
Slice ning o'zi nusxalandi, lekin nusxa ham o'sha ma'lumotga
ko'rsatadi.
Xuddi shunday map va kanal ham. Buni keyingi ikki bo'limda
batafsil ko'ramiz.
- Funksiyaga son berib, uni o'zgartirishga urinib ko'ring.
- Xuddi shuni ko'rsatkich bilan qayta yozing.
&va*amallarini bitta misolda ishlating.nilko'rsatkichni tekshirib ko'ring.nildan o'qib, vahima matnini ko'ring.- Strukturani funksiyada nusxa va manzil orqali o'zgartiring.
- Ko'rsatkich qaytaruvchi funksiya yozing.
new(int)va&sonfarqini solishtiring.manzil++yozib, xato matnini o'qing.- Slice ni funksiyada o'zgartirib, natijani tekshiring.
Xulosa #
- Go da hamma narsa qiymat bo'yicha uzatiladi - funksiya nusxa oladi.
&qiymatmanzilni beradi,*manzilesa manzildagi qiymatga boradi.- Ko'rsatkichning nol qiymati -
nil; undan o'qish vahima keltiradi. - Go ko'rsatkich orqali maydonga murojaatni o'zi hal qiladi:
t.Ballyetarli. - Mahalliy o'zgaruvchining manzilini qaytarish xavfsiz - Go kerak bo'lsa uni uyumga ko'chiradi.
- Ko'rsatkich arifmetikasi yo'q - bu ataylab, xavfsizlik uchun.
- Shubhalansangiz qiymat ishlating; ko'rsatkich - o'zgartirish yoki katta struktura uchun.
slice,mapva kanal ichida allaqachon ko'rsatkich bor.
Keyingi bo'limda massivlar va slicelarni o'rganamiz.
O‘qish tarixini saqlamoqchimisiz?
Tizimga kirsangiz, tugatgan bo‘limlaringiz saqlanadi va qoldirgan joyingizdan davom etasiz.
Xatolik topdingizmi?
Imlo xatosi, ishlamaydigan kod yoki noto‘g‘ri ma‘lumotni ko‘rsangiz - bizga xabar bering. Har bir xabar administrator tomonidan ko‘rib chiqiladi.