6-hafta · Foundation

Xotira va pointerlar

C ning eng kuchli va eng nozik qismiga keldik. Bu darsda o'zgaruvchilar xotirada qayerda turishini, ularning manzilini qanday olishni va pointer (ko'rsatkich) orqali bevosita boshqarishni o'rganamiz. Har bir g'oyani jonli xotira diagrammasida, manzillangan kataklar va ko'rsatkich yorliqlari bilan ko'ramiz.

Bu darsda nimani o'rganasiz

Xotira manzillar ketma-ketligi ekanini tushunasiz
& operatori bilan o'zgaruvchining manzilini olasiz
Pointer yaratib, boshqa o'zgaruvchiga ko'rsatasiz
*p bilan ko'rsatilgan qiymatni o'qiysiz va o'zgartirasiz
Massiv va pointer bog'liqligini hamda pointer arifmetikasini ko'rasiz
malloc va free bilan dinamik xotira bilan ishlaysiz

6.1 Xotira nima?

Kompyuter xotirasi (memory yoki RAM) deganda ma'lumot saqlanadigan, navbatma-navbat raqamlangan kataklar ketma-ketligi tushuniladi. Har bir katakning o'z tartib raqami bor, va bu raqam manzil (address) deyiladi, xuddi ko'chadagi uylar raqami kabi.

Dasturda o'zgaruvchi yaratganingizda, masalan int yosh = 19;, C aynan shu kataklardan biriga (yoki bir nechtasiga) yosh degan nom beradi va u yerga 19 ni yozadi. O'tgan haftalarda biz faqat qiymat (19) bilan ishladik. Endi esa o'sha qiymat qayerda turishini, ya'ni manzilini ham ko'ramiz.

Manzillar odatda o'n oltilik (hex) ko'rinishda yoziladi, masalan 0x100. Bizga aniq raqam muhim emas, muhimi: har bir o'zgaruvchi xotirada aniq bir joyda yashaydi.

Pointerlar avvaliga qiyin tuyuladi, bu normal. Asosiy g'oyani yodda tuting: qiymat bu katakdagi narsa, manzil esa katakning raqami. Pointer aynan manzil bilan ishlaydi.

6.2 Manzil operatori (&)

O'zgaruvchining manzilini olish uchun uning oldiga & (ampersand) belgisini qo'yamiz. Masalan &yosh degani: yosh o'zgaruvchisi qaysi manzilda turibdi. Quyida uchta o'zgaruvchi xotirada. Katakni bosib, uning manzilini ko'ring:

Manzil ko'ruvchi katakni bosing
main.c
1int yosh = 19;
2int narx = 500;
3int soni = 3;
Yuqoridagi kataklardan birini bosing: uning manzilini (&) ko'rasiz.
Eslang, o'tgan hafta scanf("%d", &yosh) da ham aynan shu & bor edi. Sababi: scanf ga qiymatni emas, balki qayerga yozishni, ya'ni manzilni berish kerak.

6.3 Pointer nima?

Pointer (ko'rsatkich) deganda boshqa o'zgaruvchining manzilini saqlovchi o'zgaruvchi tushuniladi. Ya'ni pointerning ichida qiymat emas, balki manzil turadi. Uni yaratish uchun tur oldiga yulduzcha qo'yamiz: int *p degani, p bu int ga ko'rsatuvchi pointer.

pointer.c
1int yosh = 19;
2int *p = &yosh; // p yosh ga koʻrsatadi

Pastda pointer qaysi o'zgaruvchiga ko'rsatishini tanlang. Yashil yorliq p ko'rsatkichni, u turgan katak esa ko'rsatilayotgan o'zgaruvchini bildiradi:

Pointer diagrammasi ko'rsatkichni tanlang
p =
Pointer o'zi ham xotirada turadi va o'z manziliga ega. Lekin uning qiymati boshqa o'zgaruvchining manzili. Aynan shu sabab biz pointerni "ko'rsatadi" deymiz, u biror joyga ishora qiladi.

6.4 Dereferencing (*p)

Endi eng qiziq qismi. Pointer orqali ko'rsatilgan qiymatni o'qish yoki o'zgartirish uchun yana yulduzcha ishlatamiz, lekin endi u dereferencing (ko'rsatkichga ergashish) ma'nosini bildiradi. *p degani: p ko'rsatgan joydagi qiymat.

Agar *p = 25; deb yozsak, bu "p ko'rsatgan katakka 25 ni yoz" degani. p yosh ga ko'rsatayotgani uchun, yosh ning o'zi 25 ga aylanadi. Sinab ko'ring:

Dereferencing maydoni qiymat yozing
*p =
Diqqat qiling: p bu manzil, *p esa o'sha manzildagi qiymat. Ikkalasi ikki xil narsa. Pointerlar shu sabab kuchli: bitta funksiyaga pointer berib, u boshqa joydagi o'zgaruvchini o'zgartira oladi.

6.5 Massiv va pointer

Endi 4-haftadagi sirning javobi. C da massiv nomi aslida pointerdir: u massivning birinchi elementi manzilini bildiradi. Ya'ni arr bilan &arr[0] bir xil narsa. Shuning uchun int *p = arr; deb yozish mumkin, p endi massiv boshiga ko'rsatadi.

massiv.c
1int arr[4] = {10, 20, 30, 40};
2int *p = arr; // p = &arr[0]

Pastda massiv xotirada. Yashil p yorlig'i massiv boshiga ko'rsatib turibdi. Katakni bosib, har bir elementning manzilini ko'ring:

Massiv va pointer katakni bosing
Massiv kataklaridan birini bosing: indeks va manzilini ko'rasiz.

6.6 Pointer arifmetikasi

Massiv nomi pointer bo'lgani uchun, pointer ustida arifmetika ham qila olamiz. p + 1 degani "keyingi elementga o't". Lekin nozik joyi shu: u 1 bayt emas, balki bitta element miqdorida (int uchun 4 bayt) siljiydi. Demak *(p + 1) bu arr[1] bilan bir xil.

Pastda pointerni massiv bo'ylab yuriting. Yashil p yorlig'i siljiganini, manzil va *p qiymati o'zgarganini kuzating:

Pointer arifmetikasi p ni siljiting

Bashorat qiling

Bu dastur terminalga nima chiqaradi?

main.c
1int arr[] = {5, 10, 15};
2int *p = arr;
3printf("%d", *(p + 2));
10
15
5

6.7 Satrlar va pointerlar

Endi satrlar haqidagi haqiqat ham ochiladi. 4-haftada satr char massivi ekanini ko'rgandik. Massiv esa pointer. Demak satr aslida birinchi harfga ko'rsatuvchi char pointer (char *).

satr.c
1char *ism = "Ali";
2printf("%c\n", *ism); // A (birinchi harf)
3printf("%s\n", ism); // Ali (butun satr)

Bu yerda ism birinchi harf A ga ko'rsatadi. *ism bizga A ni beradi (dereferencing), ism + 1 esa keyingi harf l ga o'tadi. Mana shu sabab satrlarni pointer bilan ham aylanib chiqsa bo'ladi, oxiridagi \0 gacha.

Demak printf("%s", ism) da printf ga butun satr emas, faqat boshlang'ich manzil beriladi. printf esa o'sha manzildan boshlab, \0 ga yetguncha harflarni o'qiydi. Hammasi pointerlar ustida qurilgan.

6.8 Dinamik xotira (malloc)

Hozirgacha o'zgaruvchilar hajmi oldindan ma'lum edi. Lekin ba'zan dastur ishlayotgan vaqtda qancha xotira kerakligini biladi, masalan foydalanuvchi nechta son kiritishini. Bunda dinamik xotira ishlatamiz: malloc funksiyasi kerakli o'lchamda xotira ajratib, uning manzilini (pointer) qaytaradi.

dinamik.c
1int *p = malloc(3 * sizeof(int));
2p[0] = 10; // xuddi massivdek ishlatamiz
3free(p); // ishlatib boʻlgach, boʻshatamiz

Pastda o'lchamni tanlang, malloc bilan xotira so'rang va ishlatib bo'lgach free bilan bo'shating:

malloc va free xotira so'rang
o'lcham:
Eng muhim qoida: malloc bilan olingan har bir xotirani free bilan bo'shatish shart. Aks holda u band bo'lib qolaveradi. Bu muammo xotira oqishi (memory leak) deyiladi.

6.9 Stack va heap

Dastur xotirasi ikki asosiy qismga bo'linadi. Oddiy o'zgaruvchilaringiz bir joyda, malloc bilan olingan xotira esa boshqa joyda yashaydi. Bularni stack va heap deyishadi:

Stack
  • Oddiy, lokal o'zgaruvchilar shu yerda
  • Avtomatik: funksiya tugasa, o'zi tozalanadi
  • Tez, lekin hajmi cheklangan
  • Masalan: int yosh = 19;
Heap
  • Dinamik xotira (malloc) shu yerdan olinadi
  • Qo'lda boshqariladi: free bilan o'zingiz bo'shatasiz
  • Katta va moslashuvchan, lekin ehtiyot kerak
  • Masalan: malloc(...)

Asosiy farq: stack o'zini o'zi tozalaydi, heap esa sizning mas'uliyatingizda. Aynan shuning uchun har bir malloc ga bitta free mos kelishi kerak.

6.10 Chuqurroq advanced

Pointerlar kuchli, lekin ehtiyotkorlik talab qiladi. Mana eng ko'p uchraydigan tushuncha va xatolar.

NULL pointer

NULL bu maxsus qiymat bo'lib, "hech qayerga ko'rsatmaydi" degani. Pointerni hali ishlatishga tayyor bo'lmaganda unga NULL beriladi. NULL ga ko'rsatuvchi pointerni dereferensiya qilish (*p) dasturni qulatadi, shuning uchun avval tekshirish kerak.

Osilgan pointer va xotira oqishi

Ikkita keng tarqalgan xato bor. Osilgan pointer (dangling pointer): free qilingan xotiraga ko'rsatib turgan pointer, undan foydalanish xavfli. Xotira oqishi (memory leak): malloc qilib, free qilishni unutish, xotira behuda band bo'lib qolaveradi.

Yaxshi amaliyot: free qilgandan keyin pointerga darhol p = NULL; bering. Shunda u osilgan holatda qolmaydi, va keyinroq tekshirib ko'rish oson bo'ladi.

Atamalar lug'ati

manzilo'zgaruvchining xotiradagi joyi (address), & bilan olinadi.
pointerboshqa o'zgaruvchining manzilini saqlovchi o'zgaruvchi.
*pdereferencing: pointer ko'rsatgan joydagi qiymat.
mallocish vaqtida heap'dan dinamik xotira ajratadi.
freemalloc bilan olingan xotirani bo'shatadi.
NULLhech qayerga ko'rsatmaydigan maxsus pointer qiymati.

6.11 Bilim testi

16 ta savol. Haftani yakunlash uchun kamida 11 tasiga to'g'ri javob bering.

Tabriklaymiz! 6-hafta yakunlandi

Endi siz C ning eng nozik mavzusini, xotira va pointerlarni tushunasiz: manzil, pointer, dereferencing, pointer arifmetikasi va dinamik xotira. Bu ko'pchilik to'xtab qoladigan joy, siz esa o'tdingiz.

Keyingi hafta: Ma'lumotlar tuzilmalari (struct, bog'langan ro'yxat, stek va navbat).

Keyingi modulga o'tish