SlideShare une entreprise Scribd logo
1  sur  26
Заагч

Сэдвүүд:



·       Заагч гэж юу вэ?



·       Заагч хэмээх үгийн утга санаа



·       Заагчийг зарлах



·       Заагчийг төрөлжүүлэх нь



·       * тэмдгийн тухай



·       Заагчийг идэвхжүүлэх



·       Заагчийн ... заагч



·       Заагч дээр хийх үйлдлүүд



·       Заагч ба массив



·       Динамик массив



·       Олон хэмжээст динамик массив

Заагч гэж юу вэ?
Энэ удаагийн лекцээр бид програмчлалын хэлний нэгэн чухал ойлголт болох заагчийн тухай үзнэ.
Заагч нь Си хэл төдийгүй Си++ хэлэнд чухал үүргийг гүйцэтгэдэг юм.



Заагч гэж юу болох талаар ойлголт авахын тулд эхлээд хувьсагч гэж юу болох талаар санах хэрэгтэй
(бид хувьсагчийн тухай “Си хэлний үндсэн ойлголтууд” лекц дээр үзсэн).



Хувьсагч гэдэг бол утгыг нь өөрчлөх боломжтой, тодорхой төрөлд хамаарах, нэр бүхий өгөгдөл юм.
Хувьсагчийг ашиглахын тулд эхлээд түүнийг зарлана. Зарлагдсан хувьсагчтай түүний нэрээр
дамжуулан ажиллана. Ж.нь хувьсагчид утга оноохын тулд түүний нэрэнд утга оноож, хувьсагчийн
утгыг хэвлэхийн тулд түүний нэрийг гаралтанд өгдөг:



...



int i;



i=10;



printf (“%d”, i);



...



Ингээд хөрвүүлэгчээр эх кодыг боловсруулах үед хөрвүүлэгч нь зарлагдсан хувьсагчид зориулан
санах ойд “байр” гаргаж өгдөг. “Байрны” хэмжээ буюу нүдийн тоо хувьсагчийн төрлөөр (int, double
г.м.) тодорхойлогдоно. Түүнчлэн хөрвүүлэгч нь эх кодонд бичигдсэн хувьсагчийн нэрийг санах ой
дахь “байрны” хаягаар нь сольдог. Хаяг (address) гэдэг нь санах ойн нүдний дугаар бөгөөд эерэг
бүхэл тоо байдаг. Хэрэв хувьсагчид утга оноосон бол тэр нь харгалзах хаягаар орших “байранд”
очиж сууна.
Ө хөрвүүлсний дараагаар, хувьсагчийн нэр нь санах ойн “байрны” хаяг, харин хувьсагчийн утга
 .х.
нь тэр “байранд” байрлах ёстой утга болон хувирдаг байна. Үүнийг схемчлэн үзүүлвээс:




Эх код




Хувьсагч



Нэр

Утга




Санах ой дахь “байр”



Хаяг

Утга




Машины код
Хувьсагчийн нэр, утга ба санах ойн хаягийн хоорондын улдаа холбоог ойлгохын тулд дараах
жишээг харцгаая:



char ch=’G’;



unsigned int date=1979;



float niilber=2007.18;



Энд гурван өөр төрлийн хувьсагчийг дараалан зарлаж байна. Тэгвэл уг кодыг санах ой оролцуулан
схемчилж дүрсэлбэл:




Санах ойн хаягууд

1A2B16

1A2C16

1A2D16

1A2E16

1A2F16
1A3016

1A3116

1A3216



“Байрны” хэмжээ

байт

байт

байт

байт

байт

байт

байт

байт



Утга

‘G’

1979

2007.18




Хувьсагчийн нэр

ch

Date

Niilber
Энд үзүүлснээр бол зарлагдсан хувьсагчууд санах ойд 1A2B16 гэсэн хаяг бүхий нүдээс эхлэн
байрлажээ. Яг ямар хаяг бүхий нүдээс байрлуулж эхлэхийг хөрвүүлэгч өөрөө шийдэж байгаа.
Бүхэл char төрлийн ch хувьсагч 1 байт хэмжээтэй “байр” эзэлсэн бөгөөд хаяг нь 1A2B16, бүхэл
unsigned int төрлийн date хувьсагч 2 байтын хэмжээтэй “байр” эзэлсэн бөгөөд хаяг нь 1A2C16,
бодит float төрлийн niilber хувьсагч 4 байтын хэмжээтэй “байр” эзэлж, хаяг нь 1A2E16 болно.
Эндээс харахад хаяг бол өөрөө бас тоон утга байна. Тэгэхээр түүнийг ямар нэг хувьсагчид утга
болгон оноож болно гэсэн үг. Си хэлэнд хаягийг ихэвчлэн 16-тын тоогоор бичдэг.



Ингэж санах ойн хаягийг утга болгон оноох зориулалттай хувьсагчийг Си хэлэнд заагч төрлийн
хувьсагч (pointer-type variable) буюу товчоор зүгээр л заагч (pointer) гэж нэрлэдэг байна.

Заагч хэмээх үгийн утга санаа



Яагаад заагч хэмээн нэрийдсэн юм бол оо? Ямар нэг юмыг заадаг учраас л тэр байх. Чухам юуг
заадаг болохоор тэр вэ?



Заагчийн утга нь санах ойн дахь хаяг байдаг гэдгийг дахин хэлье. Үүнийг, заагч нь өгөгдсөн хаяг
бүхий санах ойн “байрыг” зааж байна хэмээн хэлж болох нь. Эсвэл, тэр “байранд” орших өгөгдлийг
(утгыг) зааж байгаа гэж хэлж бас болно.

Заагчийг зарлах



Заагч төрлийн хувьсагчийг ашиглахын тулд мэдээж эхлээд түүнийг зарлах хэрэгтэй. Гэхдээ энд нэг
онцлог бий. Юу гэвээс, заагч хувьсагчийг хамааруулах бие даасан өгөгдлийн төрөл гэж байхгүй
ажээ. Ө заагчийн төрлийг илэрхийлсэн тусгай албаны үг Си хэлэнд байдаггүй. Тэгвэл яаж зарлах
       .х.
вэ?



Заагчийг дараах загварын дагуу зарлана:
өгөгдлийн_төрөл        *заагчийн_нэр;



Энд:



·      өгөгдлийн_төрөл – заагчийн зааж буй өгөгдлийн төрөл (заагчийн төрөл биш). Ж.нь char,
short, int, long болон эдгээрийн unsigned хэлбэрүүд, float, double байж болно.



·    заагчийн_нэр – заагчийг нэрлэхийн тулд програм зохиогчийн сонгож авсан чөлөөт
идентификатор.



Заагчийг зарлахдаа түүний заах ёстой өгөгдлийн төрлийг эхэлж бичдэг болох нь харагдаж байна.
Тэгээд тусгаарлагч * тэмдгийг бичээд, ард нь заагчийн нэрийг бичиж байна. Жишээ авч үзье:



char *z;



int *k;



float *f;



double *ptr;



Энд z бол char төрлийн өгөгдлийг зааж буй заагч, k бол int төрлийн өгөгдлийг зааж буй заагч, f бол
float төрлийн өгөгдлийг зааж буй заагч, ptr бол double төрлийн өгөгдлийг зааж буй заагч.



Ө нэгэн жишээ:
 өр



int *k, *i;
Энд k болон i нь int төрлийн өгөгдлүүдийг зааж буй заагчууд юм. Ө нэгэн ижил өгөгдлийн
                                                                 .х.
төрлийг заасан хоёр заагчийг зэрэг зарлаж байна. * тэмдэг бол заагчийн нэртэй хамт явах ёстой
юм. Тиймээс олон заагчийг зэрэг зарлах тохиолдолд заагч бүрийн нэрний өмнө * тэмдгийг бичих
ёстой гэдэг нь дээрх жишээнээс харагдаж байна.

Заагчийг төрөлжүүлэх нь



Уг нь заагчийг илэрхийлэх бие даасан өгөгдлийн төрөл байхгүй гэдгийг өмнөх сэдвийн эхэнд
дурдсан байгаа. Тэгвэл бүгдээрээ дараах жишээг авч үзье:



char *z;



Си хэлний хөрвүүлэгч нь дээрх операторыг, char * гэсэн төрөлд хамаарах z гэсэн заагч-хувьсагчийг
зарлаж байна гэж үздэг байна. Тэгэхээр:



int *k;



float *f;



double *ptr;



г.м. операторууд бол харгалзан int *, float*, double * гэсэн төрлийн заагч хувьсагчуудыг зарлаж
байна гэсэн үг.



Эндээс ямар дүгнэлт хийж болох вэ? Хэдийгээр заагчийг илэрхийлэх бие даасан төрөл байхгүй ч,
стандарт өгөгдлийн төрлүүдийг ашиглан заагчийг төрөлжүүлж болдог байх нь ээ.

* тэмдгийн тухай
Заагч төрлийн хувьсагчийг зарлахад оролцож буй * тэмдгийг “хаягаар хандах үйлдэл” хэмээн
нэрийддэг.



Хаягаар хандах үйлдэл нь унар үйлдэл (оператор) байна. Учир нь түүний зүүн талд ямар нэг
операнд бичигдээгүй, харин баруун талд нэг операнд бичигдсэн байна. Тэр нь нөгөө зарлагдсан
заагч маань юм. Харин заагчийн утга бол хаяг байна. Ө.х.




Тиймээс хаягаар хандах үйлдлийн үр дүн нь тухайн хаягаар орших (ө.х. заагчийн зааж буй) “байр”
буюу эсвэл тэр “байранд” байрлах өгөгдөл байна. Түүний төрөл нь зарлах бичиглэлд буй
өгөгдлийн төрөл байна. Ж.нь:



char *z;



гэсэн операторт бол *z нь z-ийн зааж буй өгөгдөл юм. Түүний төрөл нь char байна. Зураглан
харуулваас:




Үүний адилаар:



int *k, *i;



гэсэн операторт бол *k ба *i нь харгалзан k, i-ийн зааж буй өгөгдлүүд юм. Тэдгээрийн төрөл нь int
байна.

Заагчийг идэвхжүүлэх
Зарлагдсан заагчийг ашиглахаасаа өмнө заагчаа идэвхжүүлэх нь зүй. Заагчийн авах утга бол
мэдээж санах ойн хаяг байж таарна.



Заагчид хэд хэдэн янзаар утга оноож болно. Эдгээрийн заримтай танилцъя.

Хувьсагчийн хаяг оноох



Заагчид ямар нэг хувьсагчийн хаягийг оноохын өмнө эхлээд тэр хаягаа олж тодорхойлох хэрэгтэй.
Үүний тулд хаяг олох үйлдлийг (reference operation) хийнэ. Энэ нь & гэсэн унар үйлдэл байдаг.
Үйлдэлд оролцох операнд нь ямагт хувьсагч байх ёстой. Ө  .х.:



&хувьсагч



Үйлдлийн үр дүн нь хувьсагчийн хаяг байх болно. Тиймээс:



заагч = &хувьсагч;



Ингэснээр хувьсагчийн санах ой дахь хаяг нь заагчийн утга болно. Ж.нь дараах кодыг харцгаая:



int a, *p;   /* a гэсэн бүхэл хувьсагч ба p гэсэн заагч зарлаж байна */



p = &a;      /* a-ийн хаягийг p-д оноож байна */



Энд p заагчид a хувьсагчийн хаягийг оноохын тулд эхлээд тэр хувьсагчаа зарлаж байна. Ингэснээр,
a хувьсагч маань санах ойд тодорхой хаяг бүхий “байртай” болно. Тиймээс a-ийн хаягийг p-д оноох
боломжтой болж байгаа юм.

Ил хаягийг оноох
Заагчид санах ойн хаягийг шууд тоон утга хэлбэрээр нь оноож болно. Ө.х.:



заагч = хаяг;



Ж.нь дараах кодыг харцгаая:



char *ptr;       /* бүхэл char төрлийн өгөгдлийг заасан ptr заагчийг зарлаж байна */



ptr = (char *) 0xB8000000;     /* түүнд 0xB8000000 гэсэн тоон утгыг оноож байна */



Энд 0xB8000000*1+ бол 16-тын бүхэл тоо (Си хэлэнд 16-тын тоог бичихдээ урд нь 0x гэсэн угтвар
залгадгийг сануулья). Ө ийм дугаар бүхий хаягийг заагчид оноож байна.
                       .х.



Гэхдээ Си хэлний хөрвүүлэгч нь заагчийг төрөлжсөн мэтээр ойлгодгийг санацгаая (“Заагчийг
төрөлжүүлэх нь” хэсгийг хар). Манай тохиолдолд ptr заагч char * гэсэн төрөлтэй байна. Гэтэл
0xB8000000 бол unsigned long int төрлийн тоо байна. Тийм учраас төрөл илээр хувиргах (unsigned
long int төрлийг char * төрөлд хувиргах) үйлдлийг дунд нь хийж өгсөн байна:



(char *) 0xB8000000

Хоосон хаяг оноох



Заримдаа заагчид санах ойн ямар ч нүдний хаягтай үл тэнцэх тийм утгыг оноож болно. Ийм утгыг
хоосон хаяг хэмээн нэрийддэг. Ө байхгүй хаяг гэсэн үг.
                               .х.



Хоосон хаягийг илэрхийлэхийн тулд stdio.h, stddef.h г.м. толгой файлуудад NULL хэмээх тусгай
тогтмол тодорхойлогдсон байдаг. Түүнийг тэг-заагч (null-pointer) гэнэ. ANSI-стандартад тэг-заагч нь
0 юм уу 0L гэсэн тоон утгатай байдаг. Ж.нь:
int *ea;



ea = NULL;



Програмд тэг-заагчийг, өгөгдсөн заагч зөв идэвхжсэн байна уу ө.х. санах ойн тодорхой “байрыг”
зааж байна уу үгүй юу гэдгийг тогтооход ашиглах боломжтой.

Заагчийг оноох



Заагчид бас өөр нэг заагчийг утга болгон оноож болно. Ингэхийн тулд утга болж буй заагч нь өөрөө
эхлээд идэвхжсэн байх ёстой. Ж.нь:



int a, *p;   /* a гэсэн бүхэл хувьсагч ба p гэсэн заагч зарлаж байна */



int* r;      /* бүхэл өгөгдлийг заасан r гэсэн заагч зарлаж байна */



p = &a;      /* p заагчийг идэвхжүүлээд */



r = p;       /* r заагчид утга болгон оноож байна */

Заагчийн ... заагч



Заагч нь өөрөө хувьсагч юм чинь хөрвүүлэлтийн дараа мөн л санах ойд тодорхой “байранд”
байрлаж таарна. Энэ байр нь мэдээж хаягтай байж таарна. Тэр хаягийг нь бид өөр нэг заагчид утга
болгон оноож болно.



Ингэж заагчийн хаягийг утга болгон авах зориулалттай заагчийг заагчийн заагч (pointer to pointer)
хэмээн нэрийддэг. Зарлахдаа:
өгөгдлийн_төрөл           **заагчийн_заагч;



гэж зарлана. Харин заагчийн хаяг бол хаяг олох оператороор (&) олдоно. Ж.нь:



char x, *ptr1, **ptr2;    /* энгийн хувьсагч, заагч, заагчийн заагч зарлаж байна */



x = ‘M’;                 /* энгийн хувьсагчид утга оноож байна */



ptr1 = &x;               /* хувьсагчийн хаягийг заагчид оноож байна */



ptr2 = &ptr1;             /* заагчийн хаягийг заагчийн заагчид оноож байна */



Энд ptr1 бол x-ийг зааж буй заагч, харин ptr2 бол ptr1-ийг зааж буй заагч буюу заагчийн заагч
байна:




Зарлагдаж байгаа хэлбэрийг нь харахад заагчийн заагчийг бас л төрөлжүүлж болох байна (char **,
int **, float **, double ** г.м.). Тухайлбал дээрх жишээний ptr2 бол char ** гэсэн төрөлтэй юм.



Заагчийн заагч нь өөрөө бас л хувьсагч байна. Тиймээс мэдээж санах ойд тодорхой хаягаар
байрлана. Тэр хаягийг нь олж өөр нэг заагчид оноож болно. Ингэж заагчийн заагчийн хаягийг авах
хувьсагчийг заагчийн заагчийн заагч (pointer to pointer to pointer) гэнэ. Зарлахдаа:



өгөгдлийн_төрөл           ***заагчийн_заагчийн_заагч;



гэж зарлана.
Гэх мэтээр бид заагчийн ... заагчийг тодорхойлж болно. Си хэлний хөрвүүлэгч заагчийн ... заагчийг
мөн л төрөлжүүлэн үздэг.

Заагч дээр хийх үйлдлүүд



Заагч дээр дараах үйлдлүүдийг хийж болно:



·     хаягаар хандах үйлдэл



·     утга оноох үйлдэл



·     төрөл хувиргах үйлдэл



·     заагчийн хаягийг олох үйлдэл



·     арифметик үйлдлүүд



·     жиших үйлдлүүд

Хаягаар хандах үйлдэл



Хаягаар хандах үйлдэл нь заагчийн зааж буй санах ойн “байрыг” илэрхийлдэг гэдгийг бид мэдэж
авсан (“* тэмдгийн тухай” хэсгийг хар). Тиймээс бид тэр “байр” луу утга “хийж” болно:



*заагч = утга;



Гэхдээ үүний тулд эхлээд заагч өөрөө идэвхжсэн байх ёстой. Идэвхжүүлэх утга нь зөвхөн
хувьсагчийн хаяг байх ёстой. Эс тэгвээс алдаа заана. Ж.нь:
char a, *ptr;          /* энгийн хувьсагч, заагч хоёр зарлаж байна */



ptr = &a;              /* энгийн хувьсагчийн хаягийг олоод заагчид оноож байна */



*ptr = ‘Y’;            /* хаягаар хандах үйлдлээр заагчийн заах “байранд” утга хийж байна */



printf (“%c”, *ptr);    /* заагчийн заах өгөгдлийг хэвлэж гаргая */



Энд *ptr нь char төрөлтэй учраас түүнд ‘Y’ гэсэн тэмдэгт утга оноож байгаа юм. Програмыг
биелүүлэх юм дэлгэцэн дээр Y гэж гарах болно.



Тэмдэглэж хэлэх зүйл байна. Дээрх жишээнд, *ptr бол ptr-ийн зааж буй санах ойн “байр” билээ.
Гэтэл:



ptr = &a;



гэсэн үйлдлийн дараа ptr нь a-гийн санах ой дахь “байрыг” заадаг болж байгаа. Ө ptr заагч
                                                                               .х.
идэвхжсэний дараагаар *ptr = a болж байгаа юм. Тиймээс:



*ptr = ‘Y’;



гэсэн үйлдэл бол үнэн хэрэгтээ:



a = ‘Y’;



гэсэнтэй адилхан ажээ. Гаралтанд *ptr-ийг биш харин a-г өгөөд үүнийг мэдэж болно:
printf (“%c”, a);



Энэ нь дэлгэцэн дээр мөн л Y гэж гаргах болно.



Заагчийн заагчийн хувьд хаягаар хандах үйлдэл нь юуг илэрхийлэх вэ? Тухайлбал:



char **z;



гэсэн операторыг авч үзье. p=*z гэсэн орлуулга хийвэл **z-ийг *p гэж бичнэ. Мэдээж p бол z-ийн
заах санах ойн “байрыг” илэрхийлнэ. Гэтэл z нь заагчийг заах ёстой учраас p нь заагч болж таарна.
Харин *p бол p-ийн заах санах ойн “байрыг” илэрхийлнэ. Эндээс, **z бол z-ийн заах заагчийн заах
санах ойн “байрыг” илэрхийлнэ гэдэг нь илт байна.



Тиймээс бид тэр “байр” луу утга “хийж” болно:



**заагчийн_заагч = утга;



Гэхдээ үүний тулд эхлээд заагчийн заагч өөрөө идэвхжсэн байх ёстой. Идэвхжүүлэх утга нь зөвхөн
ямар нэг заагчийн хаяг байх ёстой. Гэтэл үүний тулд тэр заагч нь бас идэвхжсэн байх ёстой.
Идэвхжүүлэх утга нь зөвхөн ямар нэг хувьсагчийн хаяг байх ёстой. Эс тэгвээс алдаа заана. Ж.нь:



char x, *p1, **p2;



p1 = &x;             /* заагч идэвхжүүлж байна */



p2 = &p1;            /* заагчийн заагч идэвхжүүлж байна */
**p2 = 'M';         /* хаягаар хандах үйлдлээр заагчийн заагчийн заах “байр” луу утга хийх */



printf ("%c", **p2); /* заагчийн заагчийн заах өгөгдлийг хэвлэж гаргая */



Энд байгаа:



**p2 = 'M';



гэсэн үйлдэл үнэн хэрэгтээ:



x = ‘M’;



гэсэнтэй адилхан гэдгийг сануулъя. Яагаад тэгж байгаа билээ?

Утга оноох үйлдэл



Мэдээж энэ бол заагчид ямар нэг хаягийг утга болгож оноох үйлдэл. Ингэхдээ утга оноох оператор
“=”-г ашиглаж байгаа.

Төрөл хувиргах үйлдэл



Хэрэв заагчид оноох гэж буй утга нь заагчийн төрлөөс өөр төрөлтэй байвал яах вэ? Мэдээж төрөл
хувиргах үйлдлийг хийдэг байна. Ж.нь:



char *z;         /* z заагчийн төрөл нь char * байна */



int *k;          /* k заагчийн төрөл нь int * байна */
z = (char *) k;   /* тийм учраас k-ийн төрлийг z-ийн төрөл рүү хувиргаж байна */



Ө нэг жишээг “Заагчийг идэвхжүүлэх” хэсгийн “Ил хаяг оноох” гэсэн дэд хэсгээс харж болно.
 өр

Заагчийн хаягийг олох



Хаяг олох оператороор заагчийн хаягийг олдог. Олсон утгыг заагчийн заагчид оноодог. Энэ тухай
дээр үзсэн (“Заагчийн ... заагч” хэсэгт болон энэ хэсгийн “Хаягаар хандах үйлдэл” дэд хэсэгт).

Арифметик үйлдлүүд



Заагч дээр хийх арифметик үйлдлүүд бол:



·     ++



·     --



·     +



·     -



гэсэн үйлдлүүд байна.



++ ба – гэсэн унар үйлдлүүд заагчийн утгыг “нэгж уртаар” өөрчилдөг. “Нэгж урт” гэдэг нь заагчийн
зааж буй өгөгдлийн төрлийн урт (байтаар) юм. Бичих загвар нь:



заагч++     /* постфикс хэлбэр*/
++заагч    /* префикс хэлбэр*/



заагч--        /* постфикс хэлбэр*/



--заагч        /* префикс хэлбэр*/



Ж.нь дараах кодыг харцгаая. Энд дандаа постфикс хэлбэрүүдийг ашигласан болно:



char *cp, c;



long *lp, L;



double *dp, d;



cp = &c;       /* cp заагчийг идэвхжүүлье */



cp++;          /* утга нь 1-ээр нэмэгдэнэ, учир нь түүний заах char төрлийн урт 1 байт байдаг */



lp = &L;       /* lp заагчийг идэвхжүүлье */



lp--;          /* утга нь 4-өөр багасна, учир нь түүний заах long төрлийн урт 4 байт байдаг*/



dp = &d;       /* dp заагчийг идэвхжүүлье */
dp++;       /* утга нь 8-аар нэмэгдэнэ, учир нь түүний заах double төрлийн урт 8 байт байдаг */



Энд хэрэв cp-г идэвхжүүлэхэд утга нь 235915510 болсон гэвэл cp++; гэсний дараа утга нь
235915610 болно гэсэн үг. Түүнчлэн lp=235914410 байсан гэвэл lp--; гэсний дараа lp=235914010,
dp=235912810 байсан гэвэл dp++; гэсний дараа dp=235913610 болох юм.




+ ба – гэсэн бинар үйлдлүүд нь заагч дээр бүхэл тоо нэмэх, хасах, мөн заагчаас заагчийг хасахад
хэрэглэгдэнэ. Заагч дээр заагчийг нэмэх үйлдэл гэж Си хэлэнд байдаггүй.



+ тэмдэг ашиглан заагч дээр бүхэл тоо нэмдэг. Ингэхэд гарах нийлбэр нь бүхэл тоог “нэгж уртаар”
үржсэн хэмжээгээр заагчийн утгаас их байна. Ж.нь:



float *fp1, *fp2, f;



fp1 = &f;          /* эхлээд fp1 заагчийг идэвхжүүлье */



fp2 = fp1 + 2;         /* харин fp2-ын утга fp1-ээс 8-аар их болно */



Энд хэрэв fp1=235914810 байсан гэвэл fp2=235915610 болох юм. Учир нь fp1-ийн зааж буй float
төрөл 4 байтын урттай байдаг. Тиймээс 4 байтыг 2-оор үржээд 8 байт гарах юм.




- тэмдэг ашиглан заагчаас бүхэл тоо хасахад гарах ялгавар бол бүхэл тоог “нэгж уртаар” үржсэн
хэмжээгээр заагчийн утгаас бага байна.
- тэмдэг ашиглан нэг заагчаас нөгөө заагчийг хасч болдог. Ингэхэд гарах ялгавар нь заагчуудын
утгын ялгаврыг “нэгж уртад” хуваасан утга байна. Ж.нь:



float *fp1, *fp2, f1, f2;



int d;



fp1 = &f1;         /* эхлээд fp1 заагчийг идэвхжүүлье */



fp2 = &f2;         /* дараа нь fp2 заагчийг идэвхжүүлье */



d = fp1 - fp2;      /* нэг заагчаас нөгөө заагчийг хасъя */



Энд хэрэв fp1=235914810, fp2=235914410 байсан гэвэл d=(2359148-2359144)/4=1 гэж гарах болно.

Үйлдлийн эрэмбэ тооцох



Дээр дурдсан үйлдлүүдийг заагч дээр хийхдээ үйлдлийн эрэмбийг сайтар анхаарах ёстой юм.



Унар үйлдлүүд болох хаягаар хандах * үйлдэл, хаяг олох & үйлдэл мөн ++ ба -- гэсэн үйлдлүүд
(префикс хэлбэрүүд нь) адил эрэмбэтэй байдаг. Тиймээс илэрхийлэл дотор зэрэгцэн байрласан
тохиолдолд баруунаасаа зүүн тийш хийгдэж эхлэнэ. Ж.нь:



int d, *p;



p = &d;
*++p = 10;



Хамгийн сүүлийн мөрийг нягтлан харъя. Энд хаягаар хандах * үйлдэл ба префикс ++ гэсэн унар
үйлдлүүд зэрэгцэн орсон байна. Ингээд баруун талаасаа эхлэн ++p хийгдээд p-ийн утга
өмнөхөөсөө “нэгж утгаар” нэмэгдэнэ. Дараа нь энэхүү шинэ утгаар (хаягаар) хандан p-ийн заах
байр луу 10 гэсэн утгыг хийж байна. Ө *p=10. Бид энэ мөрийг:
                                     .х.



++p;



*p = 10;



гэж задлан бичиж болох юм.



+ ба – гэсэн бинар үйлдлүүд хоорондоо адил эрэмбэтэй, харин унар үйлдлүүдээс бага эрэмбэтэй
байдаг. Тиймээс илэрхийлэл дотор бинар үйлдлүүд зэрэгцэн орсон тохиолдолд зүүнээсээ баруун
тийш хийгдэж эхлэнэ. Харин унар ба бинар үйлдүүд зэрэгцэн орсон тохиолдолд мэдээж унар
үйлдлүүд түрүүлж хийгдэнэ. Ж.нь:



int *p, a=2;



p = &a;



a = *p+1;



Хамгийн сүүлийн мөрийг нягтлан харъя. Эхлээд *p үйлдэл хийгдэнэ. p нь a-г заах учраас *p=2
гарна. Дараа нь түүн дээр 1-ийг нэмнэ. Ө *p+1=3 гарна. Эцэст нь энэ утгаа эргүүлээд a-д өгч
                                        .х.
байна. Тиймээс a=3 гарах юм.

Жиших үйлдлүүд
Заагч дээр:



·    == - тэнцүү



·    != - тэнцүү биш



·    < - бага



·    <= - бага буюу тэнцүү



·    > - их



·    >= - их буюу тэнцүү



гэсэн жиших үйлдлүүдийг хийх боломжтой. Ө заагч оролцсон нөхцөлт илэрхийлэл бичиж болно
                                         .х.
гэсэн үг.



Гэхдээ заагчийг зөвхөн түүнтэй адил төрлийн заагчтай л жишиж болно. Эсвэл тэг-заагчтай (NULL)
жишиж болно.

Заагч ба массив



Бид массивтай өмнө нь танилцаж байсан билээ. Одоо харин массив нь заагчтай ямар холбоотой
болохыг үзье.



Си хэлний дүрэм ёсоор бол индексгүй бичигдсэн массивын нэр нь массивын хамгийн эхний
элементийн хаягийг илэрхийлдэг байна.
Ж.нь:



char a[10];



гэсэн массив байна гэе. Энд:



·       a - массивын нэр



·       10 – массивын элементийн тоо буюу урт.



Зураглан үзүүлвээс:




Энэ массивын хамгийн эхний элемент нь a*0+ байна. Түүний хаяг нь &a*0+ болно. Тэгвэл дээр
дурдсан ёсоор a бол &a*0+-ыг илэрхийлдэг байна. Ө a=&a*0+ юм. Элементийн хаяг бол тогтмол
                                                 .х.
утга байх учраас a бол тогтмол хэмжигдхүүн байж таарна. Тиймээс a-ийн утгыг өөрчилж болохгүй.



Одоо харин a-ийн утга дээр “нэгж уртыг“ нэмээд үзье:



a+1



Манай тохиолдолд “нэгж урт” бол 1-тэй тэнцүү. Тиймээс энэ нь a-ийн заах нүдний дараагийн
нүдний хаяг болж таарна. Гэтэл a-ийн заах нүдний дараагийн нүдэнд a*1+ элемент байрлаж байгаа
биз дээ (дээрх зургийг хар). Тиймээс a+1=&a*1+ байх нь ээ. Тиймээс энэ хаягаар хандалт хийвэл a*1+
элементэд хүрч чадна. Ө *(a+1)=a*1+ байх нь. Дахиад цаашаа хөдөлвөл *(a+2)=a*2+ байна. Дахиад
                         .х.
цаашаа хөдөлвөл *(a+3)=a*3+ байна. Г.м.-ээр бид a массивын дурын i-р элементэд *(a+i) гэж
хандаж болно. Ө бид a массивын дурын элементэд хандахдаа:
                 .х.
a[i]



гэсэн индекстэй бичиглэлийн оронд заагч бүхий:



*(a+i)



гэсэн бичиглэл ашиглаж болох юм.



Жишээ болгоод ийм нэг бодлогыг авч үзье. Дээр зарлагдсан a массивыг индекс хэрэглэхгүйгээр 0-
ээс 9 хүртэлх тоогоор идэвхжүүл. Үүнийг гүйцэтгэх програмын код нь:



#include <stdio.h>



main ( )



{



    int a[10];



    int i;



    for (i = 0; i <= 9; i++) *(a+i) = i;              /* массиваа идэвхжүүлэх */



    for (i = 0; i <= 9; i++) printf ("%dn", a*i+);   /* гаралтын үйлдэл */
getchar ( );



}

Үргэлжлэл бий.




*1+ Энэ нь 3087007744 гэсэн 10-тын тоо болно

Contenu connexe

Tendances

U.cs101 алгоритм программчлал-3
U.cs101   алгоритм программчлал-3U.cs101   алгоритм программчлал-3
U.cs101 алгоритм программчлал-3Badral Khurelbaatar
 
U.cs101 алгоритм программчлал-5 zasvar badral(1)
U.cs101   алгоритм программчлал-5 zasvar badral(1)U.cs101   алгоритм программчлал-5 zasvar badral(1)
U.cs101 алгоритм программчлал-5 zasvar badral(1)Badral Khurelbaatar
 
алгоритмын бодлогууд
алгоритмын бодлогуудалгоритмын бодлогууд
алгоритмын бодлогуудRenchindorj Monkhzul
 
Олон хувьсагчтай функцийн нөхцөлт экстремум, интеграл
Олон хувьсагчтай функцийн нөхцөлт экстремум, интегралОлон хувьсагчтай функцийн нөхцөлт экстремум, интеграл
Олон хувьсагчтай функцийн нөхцөлт экстремум, интегралBattur
 
11-р ангийн мэдээлэл зүйн тест
11-р ангийн мэдээлэл зүйн тест11-р ангийн мэдээлэл зүйн тест
11-р ангийн мэдээлэл зүйн тестDagii Dagii
 
U.cs101 алгоритм программчлал-2
U.cs101   алгоритм программчлал-2U.cs101   алгоритм программчлал-2
U.cs101 алгоритм программчлал-2Badral Khurelbaatar
 
Lec4 хereglegchiinpunkts
Lec4 хereglegchiinpunktsLec4 хereglegchiinpunkts
Lec4 хereglegchiinpunktsTuruu Tsogt
 
U.cs101 алгоритм программчлал-7
U.cs101   алгоритм программчлал-7U.cs101   алгоритм программчлал-7
U.cs101 алгоритм программчлал-7Badral Khurelbaatar
 
Давталттай алгоритмын бодлогууд
Давталттай алгоритмын бодлогуудДавталттай алгоритмын бодлогууд
Давталттай алгоритмын бодлогуудБаярсайхан Л
 
01_6-р анги (Мэдээ, Мэдээлэл түүний шинж чанар)
01_6-р анги (Мэдээ, Мэдээлэл түүний шинж чанар)01_6-р анги (Мэдээ, Мэдээлэл түүний шинж чанар)
01_6-р анги (Мэдээ, Мэдээлэл түүний шинж чанар)Dagvarichin Amaraa
 
компьютерийн үндсэн төхөөрөмжүүд
компьютерийн үндсэн төхөөрөмжүүдкомпьютерийн үндсэн төхөөрөмжүүд
компьютерийн үндсэн төхөөрөмжүүдKhishighuu Myanganbuu
 

Tendances (20)

U.cs101 алгоритм программчлал-3
U.cs101   алгоритм программчлал-3U.cs101   алгоритм программчлал-3
U.cs101 алгоритм программчлал-3
 
U.cs101 алгоритм программчлал-5 zasvar badral(1)
U.cs101   алгоритм программчлал-5 zasvar badral(1)U.cs101   алгоритм программчлал-5 zasvar badral(1)
U.cs101 алгоритм программчлал-5 zasvar badral(1)
 
C++ vndsen oilgolt хичээл 1
C++ vndsen oilgolt хичээл 1C++ vndsen oilgolt хичээл 1
C++ vndsen oilgolt хичээл 1
 
Лекц 10: (Рекурс)
Лекц 10: (Рекурс)Лекц 10: (Рекурс)
Лекц 10: (Рекурс)
 
2
22
2
 
алгоритмын бодлогууд
алгоритмын бодлогуудалгоритмын бодлогууд
алгоритмын бодлогууд
 
Lection 1
Lection 1Lection 1
Lection 1
 
Олон хувьсагчтай функцийн нөхцөлт экстремум, интеграл
Олон хувьсагчтай функцийн нөхцөлт экстремум, интегралОлон хувьсагчтай функцийн нөхцөлт экстремум, интеграл
Олон хувьсагчтай функцийн нөхцөлт экстремум, интеграл
 
11-р ангийн мэдээлэл зүйн тест
11-р ангийн мэдээлэл зүйн тест11-р ангийн мэдээлэл зүйн тест
11-р ангийн мэдээлэл зүйн тест
 
U.cs101 алгоритм программчлал-2
U.cs101   алгоритм программчлал-2U.cs101   алгоритм программчлал-2
U.cs101 алгоритм программчлал-2
 
Lec4 хereglegchiinpunkts
Lec4 хereglegchiinpunktsLec4 хereglegchiinpunkts
Lec4 хereglegchiinpunkts
 
Өгөгдлийн бүтэц 10
Өгөгдлийн бүтэц 10Өгөгдлийн бүтэц 10
Өгөгдлийн бүтэц 10
 
Лекц 9(Заагч)
Лекц 9(Заагч)Лекц 9(Заагч)
Лекц 9(Заагч)
 
hicheel2
hicheel2hicheel2
hicheel2
 
MT101 Lecture 1(Mongolia)
MT101 Lecture 1(Mongolia)MT101 Lecture 1(Mongolia)
MT101 Lecture 1(Mongolia)
 
U.cs101 алгоритм программчлал-7
U.cs101   алгоритм программчлал-7U.cs101   алгоритм программчлал-7
U.cs101 алгоритм программчлал-7
 
Давталттай алгоритмын бодлогууд
Давталттай алгоритмын бодлогуудДавталттай алгоритмын бодлогууд
Давталттай алгоритмын бодлогууд
 
01_6-р анги (Мэдээ, Мэдээлэл түүний шинж чанар)
01_6-р анги (Мэдээ, Мэдээлэл түүний шинж чанар)01_6-р анги (Мэдээ, Мэдээлэл түүний шинж чанар)
01_6-р анги (Мэдээ, Мэдээлэл түүний шинж чанар)
 
C cons
C consC cons
C cons
 
компьютерийн үндсэн төхөөрөмжүүд
компьютерийн үндсэн төхөөрөмжүүдкомпьютерийн үндсэн төхөөрөмжүүд
компьютерийн үндсэн төхөөрөмжүүд
 

En vedette

En vedette (7)

6. computer opganization
6. computer opganization6. computer opganization
6. computer opganization
 
C lects (1)
C lects (1)C lects (1)
C lects (1)
 
Lecture 7, 8
Lecture 7, 8Lecture 7, 8
Lecture 7, 8
 
алгоритм
алгоритмалгоритм
алгоритм
 
оролт гаралтын төхөөрөмжүүд
оролт гаралтын төхөөрөмжүүдоролт гаралтын төхөөрөмжүүд
оролт гаралтын төхөөрөмжүүд
 
Ediin zasgiin matematic hicheeliin lekts
Ediin zasgiin matematic hicheeliin lektsEdiin zasgiin matematic hicheeliin lekts
Ediin zasgiin matematic hicheeliin lekts
 
алгоритм 8
алгоритм 8алгоритм 8
алгоритм 8
 

Similaire à Lesson 7 (20)

Lec3
Lec3Lec3
Lec3
 
Лекц-1
Лекц-1Лекц-1
Лекц-1
 
Lecture 1
Lecture 1Lecture 1
Lecture 1
 
Ci hel
Ci helCi hel
Ci hel
 
C
CC
C
 
си хэлний хичээлүүд 11 р анги
си хэлний хичээлүүд 11 р ангиси хэлний хичээлүүд 11 р анги
си хэлний хичээлүүд 11 р анги
 
Dynamic web 3
Dynamic web 3Dynamic web 3
Dynamic web 3
 
Лекц №6
Лекц №6Лекц №6
Лекц №6
 
Dynamic web 3-4
Dynamic web 3-4Dynamic web 3-4
Dynamic web 3-4
 
u.cs101 "Алгоритм ба програмчлал" Лекц №6
u.cs101 "Алгоритм ба програмчлал" Лекц №6u.cs101 "Алгоритм ба програмчлал" Лекц №6
u.cs101 "Алгоритм ба програмчлал" Лекц №6
 
Lekts1
Lekts1Lekts1
Lekts1
 
Lekts1
Lekts1Lekts1
Lekts1
 
Lekts1
Lekts1Lekts1
Lekts1
 
онол
онолонол
онол
 
Sw203 Lecture11 Casting
Sw203 Lecture11 CastingSw203 Lecture11 Casting
Sw203 Lecture11 Casting
 
visual programming lecture 2
visual programming lecture 2visual programming lecture 2
visual programming lecture 2
 
2
22
2
 
visual programming lecture 2
visual programming lecture 2visual programming lecture 2
visual programming lecture 2
 
visual programming lecture 2
visual programming lecture 2visual programming lecture 2
visual programming lecture 2
 
Microsoft excel-2007томъёонууд
Microsoft excel-2007томъёонуудMicrosoft excel-2007томъёонууд
Microsoft excel-2007томъёонууд
 

Plus de Lha Bolorerdene (20)

семинарын удирдамж 8
семинарын удирдамж 8семинарын удирдамж 8
семинарын удирдамж 8
 
Lessons
LessonsLessons
Lessons
 
Integral1
Integral1Integral1
Integral1
 
дифференциал тэгшитгэлийн хувилбар
дифференциал тэгшитгэлийн хувилбардифференциал тэгшитгэлийн хувилбар
дифференциал тэгшитгэлийн хувилбар
 
олон хувьсагч бие даалт
олон хувьсагч бие даалтолон хувьсагч бие даалт
олон хувьсагч бие даалт
 
семинар
семинарсеминар
семинар
 
семинар
семинарсеминар
семинар
 
Os l1
Os l1Os l1
Os l1
 
Plan 2011 2012-1
Plan 2011 2012-1Plan 2011 2012-1
Plan 2011 2012-1
 
лабораторийн ажилUud
лабораторийн ажилUudлабораторийн ажилUud
лабораторийн ажилUud
 
L 4
L 4L 4
L 4
 
L 3
L 3L 3
L 3
 
L 3
L 3L 3
L 3
 
Presentation1
Presentation1Presentation1
Presentation1
 
Presentation1
Presentation1Presentation1
Presentation1
 
блог хийх зааварчилгаа
блог хийх зааварчилгааблог хийх зааварчилгаа
блог хийх зааварчилгаа
 
10 best hotel chains îð÷óóëãà
10 best hotel chains îð÷óóëãà10 best hotel chains îð÷óóëãà
10 best hotel chains îð÷óóëãà
 
лекц 4
лекц 4лекц 4
лекц 4
 
лекц 4
лекц 4лекц 4
лекц 4
 
Lab 8
Lab 8Lab 8
Lab 8
 

Lesson 7

  • 1. Заагч Сэдвүүд: · Заагч гэж юу вэ? · Заагч хэмээх үгийн утга санаа · Заагчийг зарлах · Заагчийг төрөлжүүлэх нь · * тэмдгийн тухай · Заагчийг идэвхжүүлэх · Заагчийн ... заагч · Заагч дээр хийх үйлдлүүд · Заагч ба массив · Динамик массив · Олон хэмжээст динамик массив Заагч гэж юу вэ?
  • 2. Энэ удаагийн лекцээр бид програмчлалын хэлний нэгэн чухал ойлголт болох заагчийн тухай үзнэ. Заагч нь Си хэл төдийгүй Си++ хэлэнд чухал үүргийг гүйцэтгэдэг юм. Заагч гэж юу болох талаар ойлголт авахын тулд эхлээд хувьсагч гэж юу болох талаар санах хэрэгтэй (бид хувьсагчийн тухай “Си хэлний үндсэн ойлголтууд” лекц дээр үзсэн). Хувьсагч гэдэг бол утгыг нь өөрчлөх боломжтой, тодорхой төрөлд хамаарах, нэр бүхий өгөгдөл юм. Хувьсагчийг ашиглахын тулд эхлээд түүнийг зарлана. Зарлагдсан хувьсагчтай түүний нэрээр дамжуулан ажиллана. Ж.нь хувьсагчид утга оноохын тулд түүний нэрэнд утга оноож, хувьсагчийн утгыг хэвлэхийн тулд түүний нэрийг гаралтанд өгдөг: ... int i; i=10; printf (“%d”, i); ... Ингээд хөрвүүлэгчээр эх кодыг боловсруулах үед хөрвүүлэгч нь зарлагдсан хувьсагчид зориулан санах ойд “байр” гаргаж өгдөг. “Байрны” хэмжээ буюу нүдийн тоо хувьсагчийн төрлөөр (int, double г.м.) тодорхойлогдоно. Түүнчлэн хөрвүүлэгч нь эх кодонд бичигдсэн хувьсагчийн нэрийг санах ой дахь “байрны” хаягаар нь сольдог. Хаяг (address) гэдэг нь санах ойн нүдний дугаар бөгөөд эерэг бүхэл тоо байдаг. Хэрэв хувьсагчид утга оноосон бол тэр нь харгалзах хаягаар орших “байранд” очиж сууна.
  • 3. Ө хөрвүүлсний дараагаар, хувьсагчийн нэр нь санах ойн “байрны” хаяг, харин хувьсагчийн утга .х. нь тэр “байранд” байрлах ёстой утга болон хувирдаг байна. Үүнийг схемчлэн үзүүлвээс: Эх код Хувьсагч Нэр Утга Санах ой дахь “байр” Хаяг Утга Машины код
  • 4. Хувьсагчийн нэр, утга ба санах ойн хаягийн хоорондын улдаа холбоог ойлгохын тулд дараах жишээг харцгаая: char ch=’G’; unsigned int date=1979; float niilber=2007.18; Энд гурван өөр төрлийн хувьсагчийг дараалан зарлаж байна. Тэгвэл уг кодыг санах ой оролцуулан схемчилж дүрсэлбэл: Санах ойн хаягууд 1A2B16 1A2C16 1A2D16 1A2E16 1A2F16
  • 6. Энд үзүүлснээр бол зарлагдсан хувьсагчууд санах ойд 1A2B16 гэсэн хаяг бүхий нүдээс эхлэн байрлажээ. Яг ямар хаяг бүхий нүдээс байрлуулж эхлэхийг хөрвүүлэгч өөрөө шийдэж байгаа. Бүхэл char төрлийн ch хувьсагч 1 байт хэмжээтэй “байр” эзэлсэн бөгөөд хаяг нь 1A2B16, бүхэл unsigned int төрлийн date хувьсагч 2 байтын хэмжээтэй “байр” эзэлсэн бөгөөд хаяг нь 1A2C16, бодит float төрлийн niilber хувьсагч 4 байтын хэмжээтэй “байр” эзэлж, хаяг нь 1A2E16 болно. Эндээс харахад хаяг бол өөрөө бас тоон утга байна. Тэгэхээр түүнийг ямар нэг хувьсагчид утга болгон оноож болно гэсэн үг. Си хэлэнд хаягийг ихэвчлэн 16-тын тоогоор бичдэг. Ингэж санах ойн хаягийг утга болгон оноох зориулалттай хувьсагчийг Си хэлэнд заагч төрлийн хувьсагч (pointer-type variable) буюу товчоор зүгээр л заагч (pointer) гэж нэрлэдэг байна. Заагч хэмээх үгийн утга санаа Яагаад заагч хэмээн нэрийдсэн юм бол оо? Ямар нэг юмыг заадаг учраас л тэр байх. Чухам юуг заадаг болохоор тэр вэ? Заагчийн утга нь санах ойн дахь хаяг байдаг гэдгийг дахин хэлье. Үүнийг, заагч нь өгөгдсөн хаяг бүхий санах ойн “байрыг” зааж байна хэмээн хэлж болох нь. Эсвэл, тэр “байранд” орших өгөгдлийг (утгыг) зааж байгаа гэж хэлж бас болно. Заагчийг зарлах Заагч төрлийн хувьсагчийг ашиглахын тулд мэдээж эхлээд түүнийг зарлах хэрэгтэй. Гэхдээ энд нэг онцлог бий. Юу гэвээс, заагч хувьсагчийг хамааруулах бие даасан өгөгдлийн төрөл гэж байхгүй ажээ. Ө заагчийн төрлийг илэрхийлсэн тусгай албаны үг Си хэлэнд байдаггүй. Тэгвэл яаж зарлах .х. вэ? Заагчийг дараах загварын дагуу зарлана:
  • 7. өгөгдлийн_төрөл *заагчийн_нэр; Энд: · өгөгдлийн_төрөл – заагчийн зааж буй өгөгдлийн төрөл (заагчийн төрөл биш). Ж.нь char, short, int, long болон эдгээрийн unsigned хэлбэрүүд, float, double байж болно. · заагчийн_нэр – заагчийг нэрлэхийн тулд програм зохиогчийн сонгож авсан чөлөөт идентификатор. Заагчийг зарлахдаа түүний заах ёстой өгөгдлийн төрлийг эхэлж бичдэг болох нь харагдаж байна. Тэгээд тусгаарлагч * тэмдгийг бичээд, ард нь заагчийн нэрийг бичиж байна. Жишээ авч үзье: char *z; int *k; float *f; double *ptr; Энд z бол char төрлийн өгөгдлийг зааж буй заагч, k бол int төрлийн өгөгдлийг зааж буй заагч, f бол float төрлийн өгөгдлийг зааж буй заагч, ptr бол double төрлийн өгөгдлийг зааж буй заагч. Ө нэгэн жишээ: өр int *k, *i;
  • 8. Энд k болон i нь int төрлийн өгөгдлүүдийг зааж буй заагчууд юм. Ө нэгэн ижил өгөгдлийн .х. төрлийг заасан хоёр заагчийг зэрэг зарлаж байна. * тэмдэг бол заагчийн нэртэй хамт явах ёстой юм. Тиймээс олон заагчийг зэрэг зарлах тохиолдолд заагч бүрийн нэрний өмнө * тэмдгийг бичих ёстой гэдэг нь дээрх жишээнээс харагдаж байна. Заагчийг төрөлжүүлэх нь Уг нь заагчийг илэрхийлэх бие даасан өгөгдлийн төрөл байхгүй гэдгийг өмнөх сэдвийн эхэнд дурдсан байгаа. Тэгвэл бүгдээрээ дараах жишээг авч үзье: char *z; Си хэлний хөрвүүлэгч нь дээрх операторыг, char * гэсэн төрөлд хамаарах z гэсэн заагч-хувьсагчийг зарлаж байна гэж үздэг байна. Тэгэхээр: int *k; float *f; double *ptr; г.м. операторууд бол харгалзан int *, float*, double * гэсэн төрлийн заагч хувьсагчуудыг зарлаж байна гэсэн үг. Эндээс ямар дүгнэлт хийж болох вэ? Хэдийгээр заагчийг илэрхийлэх бие даасан төрөл байхгүй ч, стандарт өгөгдлийн төрлүүдийг ашиглан заагчийг төрөлжүүлж болдог байх нь ээ. * тэмдгийн тухай
  • 9. Заагч төрлийн хувьсагчийг зарлахад оролцож буй * тэмдгийг “хаягаар хандах үйлдэл” хэмээн нэрийддэг. Хаягаар хандах үйлдэл нь унар үйлдэл (оператор) байна. Учир нь түүний зүүн талд ямар нэг операнд бичигдээгүй, харин баруун талд нэг операнд бичигдсэн байна. Тэр нь нөгөө зарлагдсан заагч маань юм. Харин заагчийн утга бол хаяг байна. Ө.х. Тиймээс хаягаар хандах үйлдлийн үр дүн нь тухайн хаягаар орших (ө.х. заагчийн зааж буй) “байр” буюу эсвэл тэр “байранд” байрлах өгөгдөл байна. Түүний төрөл нь зарлах бичиглэлд буй өгөгдлийн төрөл байна. Ж.нь: char *z; гэсэн операторт бол *z нь z-ийн зааж буй өгөгдөл юм. Түүний төрөл нь char байна. Зураглан харуулваас: Үүний адилаар: int *k, *i; гэсэн операторт бол *k ба *i нь харгалзан k, i-ийн зааж буй өгөгдлүүд юм. Тэдгээрийн төрөл нь int байна. Заагчийг идэвхжүүлэх
  • 10. Зарлагдсан заагчийг ашиглахаасаа өмнө заагчаа идэвхжүүлэх нь зүй. Заагчийн авах утга бол мэдээж санах ойн хаяг байж таарна. Заагчид хэд хэдэн янзаар утга оноож болно. Эдгээрийн заримтай танилцъя. Хувьсагчийн хаяг оноох Заагчид ямар нэг хувьсагчийн хаягийг оноохын өмнө эхлээд тэр хаягаа олж тодорхойлох хэрэгтэй. Үүний тулд хаяг олох үйлдлийг (reference operation) хийнэ. Энэ нь & гэсэн унар үйлдэл байдаг. Үйлдэлд оролцох операнд нь ямагт хувьсагч байх ёстой. Ө .х.: &хувьсагч Үйлдлийн үр дүн нь хувьсагчийн хаяг байх болно. Тиймээс: заагч = &хувьсагч; Ингэснээр хувьсагчийн санах ой дахь хаяг нь заагчийн утга болно. Ж.нь дараах кодыг харцгаая: int a, *p; /* a гэсэн бүхэл хувьсагч ба p гэсэн заагч зарлаж байна */ p = &a; /* a-ийн хаягийг p-д оноож байна */ Энд p заагчид a хувьсагчийн хаягийг оноохын тулд эхлээд тэр хувьсагчаа зарлаж байна. Ингэснээр, a хувьсагч маань санах ойд тодорхой хаяг бүхий “байртай” болно. Тиймээс a-ийн хаягийг p-д оноох боломжтой болж байгаа юм. Ил хаягийг оноох
  • 11. Заагчид санах ойн хаягийг шууд тоон утга хэлбэрээр нь оноож болно. Ө.х.: заагч = хаяг; Ж.нь дараах кодыг харцгаая: char *ptr; /* бүхэл char төрлийн өгөгдлийг заасан ptr заагчийг зарлаж байна */ ptr = (char *) 0xB8000000; /* түүнд 0xB8000000 гэсэн тоон утгыг оноож байна */ Энд 0xB8000000*1+ бол 16-тын бүхэл тоо (Си хэлэнд 16-тын тоог бичихдээ урд нь 0x гэсэн угтвар залгадгийг сануулья). Ө ийм дугаар бүхий хаягийг заагчид оноож байна. .х. Гэхдээ Си хэлний хөрвүүлэгч нь заагчийг төрөлжсөн мэтээр ойлгодгийг санацгаая (“Заагчийг төрөлжүүлэх нь” хэсгийг хар). Манай тохиолдолд ptr заагч char * гэсэн төрөлтэй байна. Гэтэл 0xB8000000 бол unsigned long int төрлийн тоо байна. Тийм учраас төрөл илээр хувиргах (unsigned long int төрлийг char * төрөлд хувиргах) үйлдлийг дунд нь хийж өгсөн байна: (char *) 0xB8000000 Хоосон хаяг оноох Заримдаа заагчид санах ойн ямар ч нүдний хаягтай үл тэнцэх тийм утгыг оноож болно. Ийм утгыг хоосон хаяг хэмээн нэрийддэг. Ө байхгүй хаяг гэсэн үг. .х. Хоосон хаягийг илэрхийлэхийн тулд stdio.h, stddef.h г.м. толгой файлуудад NULL хэмээх тусгай тогтмол тодорхойлогдсон байдаг. Түүнийг тэг-заагч (null-pointer) гэнэ. ANSI-стандартад тэг-заагч нь 0 юм уу 0L гэсэн тоон утгатай байдаг. Ж.нь:
  • 12. int *ea; ea = NULL; Програмд тэг-заагчийг, өгөгдсөн заагч зөв идэвхжсэн байна уу ө.х. санах ойн тодорхой “байрыг” зааж байна уу үгүй юу гэдгийг тогтооход ашиглах боломжтой. Заагчийг оноох Заагчид бас өөр нэг заагчийг утга болгон оноож болно. Ингэхийн тулд утга болж буй заагч нь өөрөө эхлээд идэвхжсэн байх ёстой. Ж.нь: int a, *p; /* a гэсэн бүхэл хувьсагч ба p гэсэн заагч зарлаж байна */ int* r; /* бүхэл өгөгдлийг заасан r гэсэн заагч зарлаж байна */ p = &a; /* p заагчийг идэвхжүүлээд */ r = p; /* r заагчид утга болгон оноож байна */ Заагчийн ... заагч Заагч нь өөрөө хувьсагч юм чинь хөрвүүлэлтийн дараа мөн л санах ойд тодорхой “байранд” байрлаж таарна. Энэ байр нь мэдээж хаягтай байж таарна. Тэр хаягийг нь бид өөр нэг заагчид утга болгон оноож болно. Ингэж заагчийн хаягийг утга болгон авах зориулалттай заагчийг заагчийн заагч (pointer to pointer) хэмээн нэрийддэг. Зарлахдаа:
  • 13. өгөгдлийн_төрөл **заагчийн_заагч; гэж зарлана. Харин заагчийн хаяг бол хаяг олох оператороор (&) олдоно. Ж.нь: char x, *ptr1, **ptr2; /* энгийн хувьсагч, заагч, заагчийн заагч зарлаж байна */ x = ‘M’; /* энгийн хувьсагчид утга оноож байна */ ptr1 = &x; /* хувьсагчийн хаягийг заагчид оноож байна */ ptr2 = &ptr1; /* заагчийн хаягийг заагчийн заагчид оноож байна */ Энд ptr1 бол x-ийг зааж буй заагч, харин ptr2 бол ptr1-ийг зааж буй заагч буюу заагчийн заагч байна: Зарлагдаж байгаа хэлбэрийг нь харахад заагчийн заагчийг бас л төрөлжүүлж болох байна (char **, int **, float **, double ** г.м.). Тухайлбал дээрх жишээний ptr2 бол char ** гэсэн төрөлтэй юм. Заагчийн заагч нь өөрөө бас л хувьсагч байна. Тиймээс мэдээж санах ойд тодорхой хаягаар байрлана. Тэр хаягийг нь олж өөр нэг заагчид оноож болно. Ингэж заагчийн заагчийн хаягийг авах хувьсагчийг заагчийн заагчийн заагч (pointer to pointer to pointer) гэнэ. Зарлахдаа: өгөгдлийн_төрөл ***заагчийн_заагчийн_заагч; гэж зарлана.
  • 14. Гэх мэтээр бид заагчийн ... заагчийг тодорхойлж болно. Си хэлний хөрвүүлэгч заагчийн ... заагчийг мөн л төрөлжүүлэн үздэг. Заагч дээр хийх үйлдлүүд Заагч дээр дараах үйлдлүүдийг хийж болно: · хаягаар хандах үйлдэл · утга оноох үйлдэл · төрөл хувиргах үйлдэл · заагчийн хаягийг олох үйлдэл · арифметик үйлдлүүд · жиших үйлдлүүд Хаягаар хандах үйлдэл Хаягаар хандах үйлдэл нь заагчийн зааж буй санах ойн “байрыг” илэрхийлдэг гэдгийг бид мэдэж авсан (“* тэмдгийн тухай” хэсгийг хар). Тиймээс бид тэр “байр” луу утга “хийж” болно: *заагч = утга; Гэхдээ үүний тулд эхлээд заагч өөрөө идэвхжсэн байх ёстой. Идэвхжүүлэх утга нь зөвхөн хувьсагчийн хаяг байх ёстой. Эс тэгвээс алдаа заана. Ж.нь:
  • 15. char a, *ptr; /* энгийн хувьсагч, заагч хоёр зарлаж байна */ ptr = &a; /* энгийн хувьсагчийн хаягийг олоод заагчид оноож байна */ *ptr = ‘Y’; /* хаягаар хандах үйлдлээр заагчийн заах “байранд” утга хийж байна */ printf (“%c”, *ptr); /* заагчийн заах өгөгдлийг хэвлэж гаргая */ Энд *ptr нь char төрөлтэй учраас түүнд ‘Y’ гэсэн тэмдэгт утга оноож байгаа юм. Програмыг биелүүлэх юм дэлгэцэн дээр Y гэж гарах болно. Тэмдэглэж хэлэх зүйл байна. Дээрх жишээнд, *ptr бол ptr-ийн зааж буй санах ойн “байр” билээ. Гэтэл: ptr = &a; гэсэн үйлдлийн дараа ptr нь a-гийн санах ой дахь “байрыг” заадаг болж байгаа. Ө ptr заагч .х. идэвхжсэний дараагаар *ptr = a болж байгаа юм. Тиймээс: *ptr = ‘Y’; гэсэн үйлдэл бол үнэн хэрэгтээ: a = ‘Y’; гэсэнтэй адилхан ажээ. Гаралтанд *ptr-ийг биш харин a-г өгөөд үүнийг мэдэж болно:
  • 16. printf (“%c”, a); Энэ нь дэлгэцэн дээр мөн л Y гэж гаргах болно. Заагчийн заагчийн хувьд хаягаар хандах үйлдэл нь юуг илэрхийлэх вэ? Тухайлбал: char **z; гэсэн операторыг авч үзье. p=*z гэсэн орлуулга хийвэл **z-ийг *p гэж бичнэ. Мэдээж p бол z-ийн заах санах ойн “байрыг” илэрхийлнэ. Гэтэл z нь заагчийг заах ёстой учраас p нь заагч болж таарна. Харин *p бол p-ийн заах санах ойн “байрыг” илэрхийлнэ. Эндээс, **z бол z-ийн заах заагчийн заах санах ойн “байрыг” илэрхийлнэ гэдэг нь илт байна. Тиймээс бид тэр “байр” луу утга “хийж” болно: **заагчийн_заагч = утга; Гэхдээ үүний тулд эхлээд заагчийн заагч өөрөө идэвхжсэн байх ёстой. Идэвхжүүлэх утга нь зөвхөн ямар нэг заагчийн хаяг байх ёстой. Гэтэл үүний тулд тэр заагч нь бас идэвхжсэн байх ёстой. Идэвхжүүлэх утга нь зөвхөн ямар нэг хувьсагчийн хаяг байх ёстой. Эс тэгвээс алдаа заана. Ж.нь: char x, *p1, **p2; p1 = &x; /* заагч идэвхжүүлж байна */ p2 = &p1; /* заагчийн заагч идэвхжүүлж байна */
  • 17. **p2 = 'M'; /* хаягаар хандах үйлдлээр заагчийн заагчийн заах “байр” луу утга хийх */ printf ("%c", **p2); /* заагчийн заагчийн заах өгөгдлийг хэвлэж гаргая */ Энд байгаа: **p2 = 'M'; гэсэн үйлдэл үнэн хэрэгтээ: x = ‘M’; гэсэнтэй адилхан гэдгийг сануулъя. Яагаад тэгж байгаа билээ? Утга оноох үйлдэл Мэдээж энэ бол заагчид ямар нэг хаягийг утга болгож оноох үйлдэл. Ингэхдээ утга оноох оператор “=”-г ашиглаж байгаа. Төрөл хувиргах үйлдэл Хэрэв заагчид оноох гэж буй утга нь заагчийн төрлөөс өөр төрөлтэй байвал яах вэ? Мэдээж төрөл хувиргах үйлдлийг хийдэг байна. Ж.нь: char *z; /* z заагчийн төрөл нь char * байна */ int *k; /* k заагчийн төрөл нь int * байна */
  • 18. z = (char *) k; /* тийм учраас k-ийн төрлийг z-ийн төрөл рүү хувиргаж байна */ Ө нэг жишээг “Заагчийг идэвхжүүлэх” хэсгийн “Ил хаяг оноох” гэсэн дэд хэсгээс харж болно. өр Заагчийн хаягийг олох Хаяг олох оператороор заагчийн хаягийг олдог. Олсон утгыг заагчийн заагчид оноодог. Энэ тухай дээр үзсэн (“Заагчийн ... заагч” хэсэгт болон энэ хэсгийн “Хаягаар хандах үйлдэл” дэд хэсэгт). Арифметик үйлдлүүд Заагч дээр хийх арифметик үйлдлүүд бол: · ++ · -- · + · - гэсэн үйлдлүүд байна. ++ ба – гэсэн унар үйлдлүүд заагчийн утгыг “нэгж уртаар” өөрчилдөг. “Нэгж урт” гэдэг нь заагчийн зааж буй өгөгдлийн төрлийн урт (байтаар) юм. Бичих загвар нь: заагч++ /* постфикс хэлбэр*/
  • 19. ++заагч /* префикс хэлбэр*/ заагч-- /* постфикс хэлбэр*/ --заагч /* префикс хэлбэр*/ Ж.нь дараах кодыг харцгаая. Энд дандаа постфикс хэлбэрүүдийг ашигласан болно: char *cp, c; long *lp, L; double *dp, d; cp = &c; /* cp заагчийг идэвхжүүлье */ cp++; /* утга нь 1-ээр нэмэгдэнэ, учир нь түүний заах char төрлийн урт 1 байт байдаг */ lp = &L; /* lp заагчийг идэвхжүүлье */ lp--; /* утга нь 4-өөр багасна, учир нь түүний заах long төрлийн урт 4 байт байдаг*/ dp = &d; /* dp заагчийг идэвхжүүлье */
  • 20. dp++; /* утга нь 8-аар нэмэгдэнэ, учир нь түүний заах double төрлийн урт 8 байт байдаг */ Энд хэрэв cp-г идэвхжүүлэхэд утга нь 235915510 болсон гэвэл cp++; гэсний дараа утга нь 235915610 болно гэсэн үг. Түүнчлэн lp=235914410 байсан гэвэл lp--; гэсний дараа lp=235914010, dp=235912810 байсан гэвэл dp++; гэсний дараа dp=235913610 болох юм. + ба – гэсэн бинар үйлдлүүд нь заагч дээр бүхэл тоо нэмэх, хасах, мөн заагчаас заагчийг хасахад хэрэглэгдэнэ. Заагч дээр заагчийг нэмэх үйлдэл гэж Си хэлэнд байдаггүй. + тэмдэг ашиглан заагч дээр бүхэл тоо нэмдэг. Ингэхэд гарах нийлбэр нь бүхэл тоог “нэгж уртаар” үржсэн хэмжээгээр заагчийн утгаас их байна. Ж.нь: float *fp1, *fp2, f; fp1 = &f; /* эхлээд fp1 заагчийг идэвхжүүлье */ fp2 = fp1 + 2; /* харин fp2-ын утга fp1-ээс 8-аар их болно */ Энд хэрэв fp1=235914810 байсан гэвэл fp2=235915610 болох юм. Учир нь fp1-ийн зааж буй float төрөл 4 байтын урттай байдаг. Тиймээс 4 байтыг 2-оор үржээд 8 байт гарах юм. - тэмдэг ашиглан заагчаас бүхэл тоо хасахад гарах ялгавар бол бүхэл тоог “нэгж уртаар” үржсэн хэмжээгээр заагчийн утгаас бага байна.
  • 21. - тэмдэг ашиглан нэг заагчаас нөгөө заагчийг хасч болдог. Ингэхэд гарах ялгавар нь заагчуудын утгын ялгаврыг “нэгж уртад” хуваасан утга байна. Ж.нь: float *fp1, *fp2, f1, f2; int d; fp1 = &f1; /* эхлээд fp1 заагчийг идэвхжүүлье */ fp2 = &f2; /* дараа нь fp2 заагчийг идэвхжүүлье */ d = fp1 - fp2; /* нэг заагчаас нөгөө заагчийг хасъя */ Энд хэрэв fp1=235914810, fp2=235914410 байсан гэвэл d=(2359148-2359144)/4=1 гэж гарах болно. Үйлдлийн эрэмбэ тооцох Дээр дурдсан үйлдлүүдийг заагч дээр хийхдээ үйлдлийн эрэмбийг сайтар анхаарах ёстой юм. Унар үйлдлүүд болох хаягаар хандах * үйлдэл, хаяг олох & үйлдэл мөн ++ ба -- гэсэн үйлдлүүд (префикс хэлбэрүүд нь) адил эрэмбэтэй байдаг. Тиймээс илэрхийлэл дотор зэрэгцэн байрласан тохиолдолд баруунаасаа зүүн тийш хийгдэж эхлэнэ. Ж.нь: int d, *p; p = &d;
  • 22. *++p = 10; Хамгийн сүүлийн мөрийг нягтлан харъя. Энд хаягаар хандах * үйлдэл ба префикс ++ гэсэн унар үйлдлүүд зэрэгцэн орсон байна. Ингээд баруун талаасаа эхлэн ++p хийгдээд p-ийн утга өмнөхөөсөө “нэгж утгаар” нэмэгдэнэ. Дараа нь энэхүү шинэ утгаар (хаягаар) хандан p-ийн заах байр луу 10 гэсэн утгыг хийж байна. Ө *p=10. Бид энэ мөрийг: .х. ++p; *p = 10; гэж задлан бичиж болох юм. + ба – гэсэн бинар үйлдлүүд хоорондоо адил эрэмбэтэй, харин унар үйлдлүүдээс бага эрэмбэтэй байдаг. Тиймээс илэрхийлэл дотор бинар үйлдлүүд зэрэгцэн орсон тохиолдолд зүүнээсээ баруун тийш хийгдэж эхлэнэ. Харин унар ба бинар үйлдүүд зэрэгцэн орсон тохиолдолд мэдээж унар үйлдлүүд түрүүлж хийгдэнэ. Ж.нь: int *p, a=2; p = &a; a = *p+1; Хамгийн сүүлийн мөрийг нягтлан харъя. Эхлээд *p үйлдэл хийгдэнэ. p нь a-г заах учраас *p=2 гарна. Дараа нь түүн дээр 1-ийг нэмнэ. Ө *p+1=3 гарна. Эцэст нь энэ утгаа эргүүлээд a-д өгч .х. байна. Тиймээс a=3 гарах юм. Жиших үйлдлүүд
  • 23. Заагч дээр: · == - тэнцүү · != - тэнцүү биш · < - бага · <= - бага буюу тэнцүү · > - их · >= - их буюу тэнцүү гэсэн жиших үйлдлүүдийг хийх боломжтой. Ө заагч оролцсон нөхцөлт илэрхийлэл бичиж болно .х. гэсэн үг. Гэхдээ заагчийг зөвхөн түүнтэй адил төрлийн заагчтай л жишиж болно. Эсвэл тэг-заагчтай (NULL) жишиж болно. Заагч ба массив Бид массивтай өмнө нь танилцаж байсан билээ. Одоо харин массив нь заагчтай ямар холбоотой болохыг үзье. Си хэлний дүрэм ёсоор бол индексгүй бичигдсэн массивын нэр нь массивын хамгийн эхний элементийн хаягийг илэрхийлдэг байна.
  • 24. Ж.нь: char a[10]; гэсэн массив байна гэе. Энд: · a - массивын нэр · 10 – массивын элементийн тоо буюу урт. Зураглан үзүүлвээс: Энэ массивын хамгийн эхний элемент нь a*0+ байна. Түүний хаяг нь &a*0+ болно. Тэгвэл дээр дурдсан ёсоор a бол &a*0+-ыг илэрхийлдэг байна. Ө a=&a*0+ юм. Элементийн хаяг бол тогтмол .х. утга байх учраас a бол тогтмол хэмжигдхүүн байж таарна. Тиймээс a-ийн утгыг өөрчилж болохгүй. Одоо харин a-ийн утга дээр “нэгж уртыг“ нэмээд үзье: a+1 Манай тохиолдолд “нэгж урт” бол 1-тэй тэнцүү. Тиймээс энэ нь a-ийн заах нүдний дараагийн нүдний хаяг болж таарна. Гэтэл a-ийн заах нүдний дараагийн нүдэнд a*1+ элемент байрлаж байгаа биз дээ (дээрх зургийг хар). Тиймээс a+1=&a*1+ байх нь ээ. Тиймээс энэ хаягаар хандалт хийвэл a*1+ элементэд хүрч чадна. Ө *(a+1)=a*1+ байх нь. Дахиад цаашаа хөдөлвөл *(a+2)=a*2+ байна. Дахиад .х. цаашаа хөдөлвөл *(a+3)=a*3+ байна. Г.м.-ээр бид a массивын дурын i-р элементэд *(a+i) гэж хандаж болно. Ө бид a массивын дурын элементэд хандахдаа: .х.
  • 25. a[i] гэсэн индекстэй бичиглэлийн оронд заагч бүхий: *(a+i) гэсэн бичиглэл ашиглаж болох юм. Жишээ болгоод ийм нэг бодлогыг авч үзье. Дээр зарлагдсан a массивыг индекс хэрэглэхгүйгээр 0- ээс 9 хүртэлх тоогоор идэвхжүүл. Үүнийг гүйцэтгэх програмын код нь: #include <stdio.h> main ( ) { int a[10]; int i; for (i = 0; i <= 9; i++) *(a+i) = i; /* массиваа идэвхжүүлэх */ for (i = 0; i <= 9; i++) printf ("%dn", a*i+); /* гаралтын үйлдэл */
  • 26. getchar ( ); } Үргэлжлэл бий. *1+ Энэ нь 3087007744 гэсэн 10-тын тоо болно