linkedin facebook linkedin facebook nod32

C++ 5-darslik

Muallif: Umarzoda Sh

Qo`shilgan sana: 2014-06-12

C++ 5-darslik
 POINTER (ko'rsatkich) VA SATRLAR
 C++ da ikki ko'rinishdagi ko'rsatkichlar - &-ko'rsatkichlar va pointerlar mavjuddir. Aslida bularning farqi faqat qo'llanilishi va ko'rinishida desak ham bo'ladi. Bu qismda biz C dan meros qolgan pointerlar bilan yaqindan tanishamiz. &-ko'rsatkichlarni biz o'tgan qismda ko'rgan edik.

 Pointerlar C/C++ dasturlash tillarining eng kuchli qurollaridandir. Lekin pointer tushunchasini anglash ham oson emas. Pointerlar yordamida funksiyalarning ko'rsatkich bo'yicha chaqirish mehanizmini amalga oshirish mumkin. Undan tashqari pointerlar yordamida dinamik strukturalar – stek (stack), ro'yhat (list), navbat (queue) va darahtlar (tree) tuzish mumkin.
 Undan tashqari pointer, satr va massivlar orasida yaqin aloqa bordir. Satr va massivlarni pointerlar yordamida berish bizga C dan meros bo'lob qoldi. Keyingi boblarda biz satr va massivlarni to'la qonli ob'ekt sifatida qo'lga olamiz.
 Pointerlar qiymat sifatida hotira adreslarini oladilar. Oddiy o'zgaruvchilar ma'lum bir qiymatga ega bo'lgan bir paytda, pointerlar boshqa bir o'zgaruvchining adresini o'z ichlariga oladilar. Shunda o'zgaruvchi bevosita qiymatga ko'rsatib tursa, pointer qiymatga bilvosita ko'rsatadi. 
 Pointer e'lonida, pointer ismidan oldin '*' (yulduzcha, ko'paytiruv) belgisi qo'yilishi kerak. Misolda ko'raylik:
 char *charPtr, c = 8, *pc, ff[] = "IcyCool";
 Bu yerda  charPtr va pc lar char tipidagi ko'rsatkichlardir. Yani, charPtr ni "char tipidagi oz'garuvchiga ga ko'rsatkich" deb o'qisak bo'ladi. Ko'rsatkich sifatida e'lon qilinayatgan har bir o'zgaruvchi ismi oldida '*' bo'lishi shartdir.
 Pointerlarni boshqa o'zgaruvchilar kabi e'lon davrida, yoki dastur ichida qiymat berish yordamida initsalizatsiya qilish mumkin. Pointerlar qiymat sifatida faqat o'zgaruvchi yoki ob'ektlarning adreslarini va NULL yoki 0 ni oladilar. NULL simvolik konstantasi <iostream.h> va boshqa bir necha standart e'lon fayllarida aniqlangan. Pointerga NULL qiymatini berish pointerni 0 ga tenglashtirish bilan tengdir, ammo C++ pointerga to'g'ridan-to'g'ri 0 qiymatini berish afzalroqdir, chunki agar NULL emas, 0 qiymati berilsa, ushbu qiymat avtomatik ravishda pointerning tipiga keltiriladi. Butun sonlardan faqat 0 qiymati pointerga keltirilishsiz berilishi mumkin. Agar ma'lum bir hotira adresini pointerga bermoqchi bo'lsak, quyidagicha yozishimiz mumkin: 
 int *iPtr, address = 0x45ad7 ;
 iPtr = (int *) address;             // C   uslubida tiplarni keltirish
iPtr = static_cast<int *>(address); // C++    "        "         "

Lekin, albatta, yuqoridagi yozganimizni kamdam-kam qo'llashga to'g'ri kelsa kerak, chunki adres olishning soddaroq yo'llari bordir. Aslida, pointer e'lon qilinsa-yu lekin hali qo'llanilmayatgan bo'lsa, unga 0 qiymatini berish tavsiya etiladi. Chunki, agar biz bu 0 qiymatli pointerni bilmasdan qo'llamoqchi bo'lsak, kompilyator hato beradi, bunga sabab odatda operatsiyon sistemalar 0 adresli hotira maydoni bilan ishlashga ruhsat bermaydilar. Shu tariqa qiymatsiz pointer qo'llanilganidan ogoh bo'lamiz.  
 POINTER OPERATORLARI
 O'zgaruvchilarning (yani harqanday ob'ektning) adresini olishda biz & operatorini - adres olish operatorini qo'llaymiz. Bu kontekstda & operatori bir dona argument oladi. Undan tashqari & ikkili operatori bitli qo'shishda qo'llaniladi. Adres olishga misol keltiraylik.
 int *iPtr, var = 44;
iPtr = &var;
 double d = 77.0, *dPtr = &d;
 Bu yerda bir narsani o'tib ketishimiz kerak. C++ da identefikatorlar (o'zgaruvchi va ob'ektlar) ikki turda bo'ladi. Birinchisi chap identefikatorlar (lvalue - left value: chap qiymat), ikkinchi tur esa o'ng identefikatorlardir (rvalue - right value: o'ng qiymat). Yani  chap identefikatorlar '=' (qiymat berish operatori) belgisining chap argumenti sifatida qo'llanilishi mumkin. O'ng identifikatorlar esa '=' ning o'ngida qo'yilishlari kerak. Bunda o'ng identefikatorlar har doim ham chap identefikator o'rnida qo'llanila olomaydilar. Yani chap identefikatorlarning qiymatlari '=' operatori yordamida o'zgartirilishi mumkin. Agar o'zgaruvchi const sifati bilan e'lon qilingan bo'lsa, u normal sharoitda faqat o'ng identefikatordir. Bu ma'lumotlarni keltirganimizning sababi, & adres olish operatori faqat chap identefikator bo'la oladigan o'zgaruvchilarga nisbatan qo'llanilishi mumkin. Agar o'zgaruvchi const sifatli konstantalarga, register sifatli o'zgaruvchilarga va ko'rsatkich qaytarmaydigan (adres qaytarmaydigan) ob'ektlarga nisbatan qo'llanilishi ta'qiqlanadi.  
 Faraz qilaylik, biz pointerimizga boshqa bir o'zgaruvchining adresini berdik. Endi bizning pointerimiz ikki qiymatni ifoda etmoqda, biri bu o'zining qiymati, yani boshqa o'zgaruvchining adresi, ikkinchi qiymat esa, bu boshqa o'zgaruvchining asl qiymatidir. Agar biz pointerning o'zi bilan ishlasak, biz hotiradagi adres bilan ishlagan bo'lamiz. Ko'p hollarda esa buning keragi yo'q. Pointer ko'rsatayotgan o'zgaruvchining qiymati bilan ushbu pointer yordamida ishlash uchun biz '*' belgisini, boshqacha qilib etganda, ko'rsatish operatorini (indirection, dereferencing operator) qo'llashimiz kerak bo'ladi. Bunga misol beraylik.
 ...
int k = 74;
int *kPtr = &k;
...
cout << k << " --- " << *kPtr << endl; // Ekranda:
                                       // 74 --- 74
cin >> *kPtr;                          // 290 qiymatini kiritaylik...
cout << k << " === " << *kPtr << endl; // Ekranda:
                                       // 290 === 290
*kPtr = 555;
cout << k << " ... "<< *Ptr << endl;   // Ekranda:
                                       // 555 ... 555
...
 Ko'rib turganimizdek, biz kPtr ko'rsatkichi orqali k o'zgaruvchining ham qiymatlarini o'zgartira oldik. Demak *kPtr chap identefikatordir, chunki uning qiymatini qiymat berish operatori yordamida o'zgartira olar ekanmiz. 
 Agar pointerimiz 0 ga teng bo'lsa, uni ko'rsatish operatori - '*' bilan qo'llash ko'p hollarda dastur ijrosi hatolariga olib keladi. Undan tashqari, boshlangich qiymatlari aniqlanmagan pointerni qiymatiga ko'rsatish eng kamida mantiqiy hatolarga olib keladi, bunung sababi, pointer ko'rsatayotgan hotira qismida oldingi ishlagan dasturlar kodlari qolgan bo'lishi mumkin. Bu esa bizga hech keragi yo'q. Pointer bo'lmagan o'zgaruvchilarga ko'rsatish operatorini qo'llash ta'qiqlanadi.
 Aslini olganda, & adres olish operatori va * ko'rsatish operatorlari, bir-birining teskarisidir. Bir misol kertiraylik. 
 // Adres olish va ko'rsatish operatorlari
# include <iostream.h>
 char c = 44;     // char tipidagi o'zgaruvchi      
char *pc = &c;   // char tipidagi pointer
 int main ()
{
   cout << "&*pc ning qiymati: " << &*pc << endl;
   cout << "*&pc ning qiymati: " << *&pc << endl;
   cout << "c ning hotiradagi adresi: " << &c << endl;
   cout << "pc pointerning qiymati: " << pc << endl;
   cout << "c ning qiymati: " << c << endl;
   cout << "*pc ning qiymati: " << *pc << endl;
      return (0);             
}
 Ekranda:
 &*pc ning qiymati: 0x4573da55
*&pc ning qiymati: 0x4573da55
c ning hotiradagi adresi: 0x4573da55
pc pointerning qiymati: 0x4573da55
c ning qiymati: 44
*pc ning qiymati: 44
 Demak, &*pc va *&pc ayni ishni bajarar ekan, yani * va & operatorlari  bir-birining teskarisidir. Hotiradagi adres ekranga boshqa ko'rinishda chiqishi mumkin. Bu mashina va kompilyatorga bog'liqdir.
 POINTER ARGUMENTLI FUNKSIYALAR
 Funksiylar ikki argumentlariga qarab ikki turga bo'linadi degan edik. Argumentlar qiymat bo'yicha, yoki ko'rsatkich bo'yicha berilishi mumkin edi. Qiymat bo'yicha berilgan argumentning funksiya chaqirig'iga nushasi beriladi. Ko'rsatkich bo'yicha argument chaqirig'ida, funksiyaga kerakli argumentga ko'rsatkich beriladi. Ko'rsatkich bo'yicha chaqiriqni ikki usulda bajarish mumkin, birinchi usul &-ko'rsatkichlar orqali amalga oshiriladi. Ikkinchi usulda esa pointerlar qo'llaniladi. 
Pointerlar bilan chaqishning afzalligi (qiymat bo'yicha chaqiriq bilan solishtirganda) shundagi, agar ob'ektlar katta bo'lsa, ulardan nusha olishga vaqt ketqizilmaydi. Undan tashqari funksiya ob'ektning asl nushasi bilan ishlaydi, yani ob'ektni o'zgartura oladi. Funksiya faqat bitta ob'ektni yoki o'zgaruvchini return ifodasi yordamida qiytara olgani uchun, oddiy yol bilan, qiymat bo'yicha chaqiriqda funksiya faqat bitta o'zgaruvchining qiymatini o'zgartira oladi. Agar pointerlarni qo'llasak, bittadan ko'p ob'ektlarni o'zgartirishimiz mumkin, huddi &-ko'rsatkichli chaqiriqdagi kabi.  
Funksiya chaqirig'ida esa, biz o'zgaruvchilarning adresini qo'llashimiz kerak. Buni & adres olish operatori yordamida bajaramiz. Massivni berayatganda esa adresni olish kerak emas, chunki massivning ismining o'zi massiv birinchi elementiga pointerdir.
 Pointerlarni qo'llab bir dastur yozaylik.
// Pointer argumentli funksiyalar
 # include <iostream.h>
 int foo1(int k) {return (k * k);}
void foo2(int *iPtr) {*iPtr = (*iPtr) * (*iPtr);}
int main()
{
   int qiymat = 9; 
   int javob = 0;
      javob = foo1(qiymat);  // javob = 81
   cout << "javob = " << javob << endl;
      foo2(&qiymat);         // qiymat = 81
   cout << "qiymat = " << qiymat << endl;
   return (0);
}
Ekranda:
 javob = 81
qiymat = 81
 Yuqoridagi dasturimizda foo2() funksiya chaqirig'ida qiymat nomli o'zgaruvchimizning adresini oldik (& operatori) va funksiya berdik. foo2() funksiyamiz iPtr pointer argumentining qiymatini * operatori yordamida o'zgartiryapti.Funksiya e'lonida pointer tipidagi parametrlardan keyin o'zgaruvchi ismlarini berish shart emas. Masalan: 
int func(int * , char * ); // funksiya e'loni
int func(int *arg1, char *arg2); // funksiya e'loni
 Yuqoridagi ikki e'lon aynidir.
 Aytib o'tkanimizdek, massivlarning ismlari birinchi elementlariga ko'rsatkichdir. Hatto, agar massiv bir indeksli bo'lsa, biz massivlar bilan ishlash uchun pointer sintaksisini qo'llashimiz mumkin. Kompilyator   
foo(int m[]);
 e'lonini
foo(int * const m);
 e'loniga almashtiradi. Yuqoridagi m pointerini "int tipiga o'zgarmas pointer" deb o'qiymiz. const bilan pointerlarning qo'llanilishini alohida ko'rib chiqamiz.
 const SIFATLI POINTERLAR
 const ifodasi yordamida sifatlantirilgan o'zgaruvchining qiymatini normal sharoitda o'zgartira olmaymiz. const ni qo'llash dasturning hatolardan holi bo'lishiga yordam beradi. Aslida ko'p dasturchilar const ni qo'llashga o'rganishmagan. Shu sababli ular katta imkoniyatlarni boy beradilar. Bir qarashda const ning keragi yo'qdek tuyuladi. Chunki const ni qo'llash dasturning hech qaysi bir yerida majburiy emas. Masalan konstantalarni belgilash uchun # define ifodasini qo'llasak bo'ladi, kiruvchi argumentlarni ham const sifatisiz e'lon qilsak, dastur mantig'i o'zgarishsiz qoladi. Lekin const kerak-kerakmas joyda o'zgaruvchi va ob'ektlarning holat-qiymatlarini o'zgartirilishidan himoyalaydi. Yani ob'ekt qiymatini faqat cheklangan funksiyalar va boshqa dastur bloklari o'zgartira oladilar. Bu kabi dasturlash uslubi esa, yani ma'lumotni berkitish va uni himoya qilish ob'ektli dasturlash falsafasiga kiradi. 
 Ko'rsatkich qo'llanilgan funksiyalarda, agar argumentlar funksiya tanasida o'zgartirilmasa, kirish parametrlari const deb e'lon qilinishlari kerak. Masalan, bir massiv elementlarini ekranga bosib chiqaradigan funksiya massiv elementlarini o'zgartirishiga hojat yo'q. Shu sababli argumentdagi massiv const sifatiga ega bo'ladi. Endi, agar dasturchi adashib, funksiya tanasida ushbu massivni o'zgartiradigan kod yozsa, kompilyator hato beradi. Yani bizning o'zgaruvchimiz himoyalangan bo'ladi. Bu mulohazalar boshqa tipdagi const sifatli funksiya kirish parametrlariga ham tegishlidir. Pointerlar bilan const ni to'rt hil turli kombinatsiya qo'llashimiz mumkin.
 1. Oddiy pointer va oddiy o'zgaruvchi (pointer ko'rsatayatgan o'zgaruvchi).
2. const pointer va oddiy o'zgaruvchi.
3. Oddiy pointer va const o'zgaruvchi.
4. const pointer va const o'zgaruvchi.
 Yuqoridagilarni tushuntirib beraylik. Birinchi kombinatsiyada o'zgaruvchini hech bir narsa himoya qilmаyapti. Ikkinchi holda esa o'zgaruchining qiymatini o'zgartirsa bo'ladi, lekin pointer ko'rsatayоtgan adresni o'zgartirish ta'qiqlanadi. Masalan massiv ismi ham const pointerdir. Va u ko'rsatayatgan massiv birinchi elementi-ni o'zgartirishimiz mumkin. Endi uchinchi holda pointerimiz oddiy, lekin u ko'rsatayatgan o'zgaruvchi himoyalangandir. Va nihoyat, to'rtinchi variantda eng yuqori darajadagi o'zgaruvchi himoyasita'minlanadi.
 Yuqoridagi tushunchalarga misol berib o'taylik.
 // const ifodasi va pointerlar
 # include <iostream.h>
# include <ctype.h>

int countDigits(const char *);  // oddiy pointer va const o'zgaruvchi
void changeToLowerCase(char *); // oddiy pointer va oddiy o'zgaruvchi
 int main()
{
   char m[] = "Sizni 2006 yil bilan tabriklaymiz!";
   char n[] = "TOSHKENT SHAHRI...";
    cout << m << endl <<"Yuqoridagi satrimizda " << countDigits(m)
        << " dona son bor." << endl << endl;
   cout << n << endl << "Hammasi kichik harfda:" << endl;
   changeToLowerCase(n);
   cout << n << endl;
   return (0);
}
 int countDigits(const char * cpc) { // satrdagi sonlar (0..9) miqdorini
                                    // hisoblaydi
   int k = 0;
   for ( ; *cpc != '\0' ; cpc++){ // satrlarni elementma-element
                                  // ko'rib chiqishning birinchi yo'li.
      if ( isdigit(*cpc) )  // <ctype.h> kutubhona funksiyasi       
         k++;  
   }
      return (k);
}
void changeToLowerCase(char *pc) { // katta harflarni kichik harflarga
                                   // almashtiruvchi funksiya
     while( *pc != '\0'){           // satrlarni elementma-element
                                  // ko'rib chiqishning ikkinchi yo'li.
      *pc = tolower( *pc );  // <ctype.h> kutubhona funksiyasi    
      ++pc; // pc keyingi harfga siljitildi
   }
   return;
}
Ekranda:
 Sizni 2006 yil bilan tabriklaymiz! Yuqoridagi satrimizda 4 dona son bor.
 TOSHKENT SHAHRI...
Hammasi kichik harfda:
toshkent shahri...
 Yuqoridagi dasturda ikki funksiya aniqlangan. Change ToLowerCase()funksiyasining parametri juda oddiydir. Oddiy char tipidagi pointer. Ushbu pointer ko'rsatayotgan ma'lumot ham oddiydir. Ikkinchi funksiyamizda esa (countDigits()), pointerimiz oddiy, yani uning qiymati o'zgarishi mumkin, u hotiraning turli adreslariga ko'rsatishi mumkin, lekin u ko'rsatayotgan o'zgaruvchi const deb e'lon qilindi. Yani pointerimiz ko'rsatayotgan ma'lumot ayni ushbu pointer  yordamida o'zgartirilishi ta'qiqlanadi.    Bizda yana ikki hol qoldi, ular quyida berilgan:
 const pointer va const o'zgaruvchi
const pointer va oddiy o'zgaruvchi
 Birinchi holga misol beraylik.
 ...
int m = 88, j =77;
const int * const pi = &m; // const pointer e'lon paytida
                           // initsalizatsiya qilinishi shartdir
              ...
m = 44;   // To'g'ri amal
*pi = 33; // Hato! O'zgaruvchi const deb belgilandi; birinchi const
pi = &j;  // Hato! Pointer const deb belgilandi; int * dan keyingi const
...
j = *pi;  // To'g'ri. const o'zgaruvchilarning
          // qiymatlari ishlatilinishi mumkin.
...

Yuqoridagi parchada const pointer va const o'zgaruvchili kombinatsiyani ko'rib chiqdik. Eng asosiysi, const pointer e'lon qilinganda initsalizatsiya bo'lishi shart. Bu qonun boshqa tipdagi const o'zgaruvchilarga ham tegishli.
 Ko'rib turganimizdek, 
 *pi = 33; 
 ifodasi bilan m ning qiymatini o'zgartirishga intilish bo'ldi. Lekin bu hatodir. Chunki biz pi pointeri ko'rsatib turgan hotira adresini o'zgarmas deb pi ning e'lonida birinchi const so'zi bilan belgilagan edik. Lekin biz o'zgaruvchining haqiqiy nomi bilan - m bilan o'zgaruvchi qiymatini o'zgartira olamiz. Albatta, agar m ham o'z navbatida const sifatiga ega bo'lmasa. Yani hotira adresidagi qiymatga ikkita yetishish yo'li mavjud bo'lsa, bular o'zgaruvchining asl nomi - m, va pi pointeri, bulardan biri orqali ushbu qiymatni o'zgartirsa bo'ladi, boshqasi o'rqali esa bu amal ta'qiqlanadi.
 Keyin,
 pi = &j; 
 ifoda bilan esa pi ga yangi adres bermoqchi bo'ldik. Lekin pointerimiz o'zgarmas bo'lgani uchun biz biz bu amalni bajara olmaymiz. Pointerlar va const ifodasining birga qo'llanilishining to'rtinchi holiday const pointer va oddiy hotira adresi birga ishlatilinadi. Bunga bir misol:
 int j = 84, d = 0;
int * const Ptr = &j; // e'lon davrida initsalizatsiya shartdir
*Ptr = 100;           // to'g'ri amal
Ptr = &d;             // Hato! Ptr ko'rsatayatgan
                      // hotira adresi o'zgartilishi ta'qiqlanadi
 Yuqorida Ptr ko'ratayatgan adresni o'zgartirsak, kompilyator hato beradi.Aslida, massiv ismlari ham ayni shu holga misol bo'la oladilar. Massiv ismi massivning birinchi elementiga const pointerdir. Lekin u ko'rsatayotgan massiv birinchi elementning qiymati o'zgartilishi mumkin. 
 POINTER VA ODDIY O'ZGARUVCHILARNING EGALLAGAN ADRES KATTALIGI
   Pointerlar istalgan ichki asos tipga (char, int, double ...) yoki qollanuvchi belgilagan tipga (class, struct ...) ko'rsatishi mumkin. Albatta, bu turli tiplar hotirada turlicha yer egallaydilar. char bir bayt bo'lsa, double 8. Lekin bu tiplarga ko'rsatuvchi pointerlarning kattaligi bir hil 4 bayt. Bu kattalik turli adreslash turlariga qarab o'zgarishi mumkin, lekin bitta sistemada turli tipdagi ko'rsatkichlar bir hil kattalikga egadirlar. Buning sababi shuki, pointerlar faqat hotiraning adresini saqlaydilar. Hozirgi sistemalarda esa 32 bit bilan istalgan adresni aniqlash mumkin. Pointerlar
oz'garuvchining faqat boshlangich baytiga ko'rsatadilar. Masalan, bizda double tipidagi o'zgaruvchi d bor bo'lsin. Va unga ko'rsatuchi pd pointerimiz ham e'lon qilingan bo'lsin. Pointerimiz d o'zgaruvchisining faqat birinchi baytiga ko'rsatadi. Lekin bizning d o'zgaruvchimizda pointerimiz ko'rsatayotgan birinchi baytidan tashqari yana 7 dona qo'shimcha bayti mavjud.
Mana shunda pointerning tipi o'yinga kiradi. Kompilyator double tipi hotirada qancha joy egallishi bilgani uchun, pointer ko'rsatayotgan adresdan boshlab qancha baytni olishni biladi. Shuning uchun pointerlar hotirada bir hil joy egallasa ham, biz ularga tip beramiz. void * tipidagi pointerni ham e'lon qilish mumkin. Bu pointer tipsizdir. Kompilyator bu pointer bilan * ko'rsatish operatori qo'llanilganda necha bayt joy bilan ishlashni bilmaydi. Shu sababli bu amal tayqiqlangandir. Lekin biz void * pointerini boshqa tipdagi pointerga keltirishimiz mumkin, va o'sha yangi tip bilan ishlay olamiz.  
      Masalan:
 ...
int i = 1024;
int *pi = &i, *iPtr;
void *pv;
 pv = (void *) pi;
cout << *pv; // Hato!

iPtr = (int *) pv;
cout << *iPtr; // Ekranda: 1024
...
   Tiplarning hotiradagi kattaligini kopsatadigan, bir parametr oladigan sizeof() (sizeof - ning kattaligi) operatori mavjuddir. Uning yordamida tiplarning, o'zgaruvchilarning yoki massivlarning kattaliklarini aniqlash mumkin. Agar o'zgaruvchi nomi berilsa, () qavslar berilishi shart emas, tip, massiv va pointer nomlari esa () qavslar ichida beriladi. Bir misol beraylik.
 // sizeof() operatori
# include <iostream.h>
 int k;
int *pk;
 char ch;
char *pch;
 double dArray[20];
 int main()
{
   cout << sizeof (int) << " - " << sizeof k << " - " << sizeof (pk) << endl;
      //      tip nomi            o'zgaruvchi              pointer      
   cout <<sizeof (char) << " - " <<sizeof ch << " - " <<sizeof (pch) << endl;
      cout << "\nMassiv hotirada egallagan umumiy joy (baytlarda): "
        << sizeof (dArray) << endl;
   cout << "Massivning alohida elementi egallagan joy: "
        << sizeof (double) << endl;
   cout << "Massivdagi elementlar soni: "
        << sizeof (dArray) / sizeof (double) << endl;
 return (0);

Ekranda:
4 - 4 - 4
1 - 1 - 4
Massiv hotirada egallagan umumiy joy (baytlarda): 160
Massivning alohida elementi egallagan joy: 8
Massivdagi elementlar soni: 20

3228 marta o`qildi.

Parol:
Eslab qolish.


Ro`yhatdan o`tish


Рейтинг@Mail.ru
Рейтинг@Mail.ru

Besucherzahler
счетчик посещений