Массив
Массив гэж юу вэ?
Одоог хүртэл бид дандаа энгийн өгөгдлүүдтэй ажиллаж ирсэн билээ. Энгийн өгөгдөл гэдэг нь нэр бүхий ганц өгөгдөл юм. Ө.х. нэг нэрэнд нэг л өгөгдөл харгалзана гэсэн үг.
Энгийн өгөгдөл нь өгөгдлийн үндсэн төрлүүдийн (char, unsigned char, short, unsigned short, int, unsigned int, long int, unsigned long, float, double, long double г.м.) аль нэгт хамаарна.
Тэгвэл Си хэлэнд, энгийн өгөгдлүүдээр нийлмэл өгөгдлийг бүтээж болно. Нийлмэл өгөгдлүүд гэдэгт массив, бүтэц, нэгдэл, файл гэх мэтийг хамааруулдаг. Эдгээрээс массивыг авч үзье.
Массив (array) гэдэг нь нэгэн ижил төрлийн, олон тооны энгийн өгөгдлийн нэгдэл юм. Массив нь нэртэй (name of array) байна. Ө.х. массивын хувьд, нэг нэрэнд нэгээс олон адил төрлийн энгийн өгөгдөл харгалздаг ажээ. Энэ утгаараа нийлмэл өгөгдөл болно.
Массивын бүрдэлд орогч тухайн нэг өгөгдлийг массивын элемент гэнэ. Элементүүд бүгд дугаарлагдсан байна. Элементийн дугаарыг индекс (index) гэнэ. Индекс нь 0-ээс эхлэн тоологдоно.
Массив нь ямар ач холбогдолтой вэ? Их хэмжээний мэдээлэл, төрөл бүрийн статистик өгөгдлийг боловсруулах програмд массив ашиглах нь тун тохиромжтой байдаг.
Массив нь математикийн нэгэн чухал ойлголт болох матрицыг програмчлалын хэлэнд илэрхийлдгээрээ онцгой ач холбогдолтой юм.
Массивын төрлүүд, хэлбэрүүд
Массив нь нэгэнт өгөгдөл учраас аль нэг үндсэн өгөгдлийн төрөлд хамаарах ёстой. Энэ нь түүний элементийн төрлөөр тодорхойлогдоно. Тиймээс:
· бүхэл төрлийн массив (элементийн төрөл нь char, unsigned char, short, unsigned short, int, unsigned int, long int, unsigned long г.м.-ийн аль нэг)
· бодит төрлийн массив (элементийн төрөл нь float, double, long double г.м.-ийн аль нэг)
гэсэн төрлүүд байх нь ээ.
Массив нь нэгэнт өгөгдөл учраас тогтмол ба хувьсагч гэж бас хуваагдана. Тогтмол массивт нэг удаа утга оноосон бол програмын явцад дахиж утгыг нь өөрчлөх боломжгүй. Харин хувьсагч массивт хэдэн ч удаа утга оноож болно.
Массивын урт ба хэмжээс
Массивын элементүүдийн тоог массивын урт (length) гэдэг. Компьютерын санах ойн багтаамжаас хамаараад хэрэглэгч ямар ч урттай массив зарлаж болно. Жишээлбэл бүхэл int төрлийн 5 гэсэн урттай массив зарлалаа гэвэл түүний санах ойд эзлэх зай нь 10 байт болно. Учир нь int төрлийн нэг энгийн элемент 2 байт болдог, тиймээс 5*2=10 болж байгаа юм. Үүнтэй адил зарчмаар, хэрэв бодит float төрлийн 20 гэсэн урттай массив зарлавал, нэг энгийн float төрлийн элемент 4 байт авах учраас 20*4=80 байт зай эзлэх ажээ.
Массивын хэмжээс гэдэг нь арай өөр ойлголт. Логикийн хувьд массив нь шулуун, эсвэл “тэгш өнцөгт”, эсвэл “параллелипипед” гэх мэт геометрийн ямар төсөөллийг бүрдүүлж буйгаас нь хамааруулаад нэг хэмжээст (1D - one dimensional), хоёр хэмжээст (2D - two dimensional), гурван хэмжээст (3D - three dimensional) массив гэх мэтээр нэрлэдэг. Гурваас олон хэмжээстэй (multidimensional) ч байж болно. Хоёр хэмжээст массивыг массивын массив (array of array), гурван хэмжээстийг массивын массивын массив (array of array of array) гэх мэтчилэн үгээр илэрхийлж болно.
Массивыг зарлах
Массивыг ашиглахын тулд эхлээд түүнийг зарлах ёстой.
Хувьсагч массив
Нэг хэмжээст (1D) хувьсагч массивыг дараах загварын дагуу зарлана:
өгөгдлийн_төрөл массивын_нэр [урт];
Энд:
· өгөгдлийн_төрөл – массивын төрлийг заасан албаны үг
· массивын_нэр – массивыг нэрлэхийн тулд програм зохиогчийн сонгож авсан чөлөөт идентификатор
· урт – массивын элементийн тоо.
Ж.нь:
int mas [25];
Энд бид бүхэл int төрлийн 25 элементтэй 1D mas гэсэн хувьсагч массив зарлаж байна. Хамгийн эхний элемент 0 гэсэн индекстэй (дугаартай), хамгийн сүүлийн элемент 24 гэсэн индекстэй байна.
float my [10];
Энд бодит float төрлийн 10 элементтэй 1D my гэсэн хувьсагч массив зарлагдав.
char titles [9];
Энд тэмдэгт төрлийн 9 элементтэй 1D titles хэмээх массив зарлалаа.
Хоёр хэмжээст (2D) хувьсагч массивыг зарлахдаа:
өгөгдлийн_төрөл массивын_нэр [урт_1] [урт_2];
гэсэн загварыг баримтлана. Энд:
· урт_1 – массивын эхний хэмжээсийн урт (элементийн тоо)
· урт_2 - массивын хоёр дахь хэмжээсийн урт (элементийн тоо)
Практикт, 2D массивын эхний хэмжээсийг мөр (row), хоёр дахь хэмжээсийг багана (column) хэмээн нэрийддэг. Ж.нь:
int matrix [5] [10];
Энд бүхэл int төрлийн matrix гэсэн 5 мөр, 10 баганатай 2 хэмжээст массивыг зарлаж байна.
Массивын массив гэдэг томъёололтой энэ нь яг тохирч байгаа юм. Учир нь 5 элементтэй matrix массивын нэг элемент нь мөн 10 элементтэй бүхэл төрлийн массив байна гэсэн үг.
Matrix массивын нийт элементийн тоо нь 5*10=50 байна.
Зарим нэг жишээ.
double table [20] [20];
Энд 20 мөр, 20 баганатай бодит double төрлийн table гэсэн квадрат массив зарлагдав. Элементийн тоо нь 400 болно.
unsigned char sheet [6] [5];
Энд 6 мөр, 5 багана бүхий sheet гэсэн бүхэл unsigned char төрлийн массив зарлаж байна. Элементийн тоо нь 30 болно.
Гурван хэмжээст (3D) хувьсагч массив зарлах нь:
өгөгдлийн_төрөл массивын_нэр [урт_1] [урт_2] [урт_3];
гэж бичигдэнэ. Энэ тохиолдолд мөр, багана дээр нэмэгдээд гурав дахь хэмжээс (өндөр ?) гарч ирнэ. Хэрэв гурван хэмжээсийн уртууд нь адил бол куб массив гэнэ.
Г.м.-ээр олон хэмжээст массивыг зарлаж болно. Онолын хувьд, массивын хэмжээст хязгаар тавьсан юм байхгүй.
Массивыг идэвхжүүлэх
Массивыг зарлахдаа шууд анхны утга оноох, ө.х. идэвхжүүлэх боломжтой. Гэхдээ массив нь олон элементээс тогтсон нийлмэл өгөгдөл учраас түүнтэй бүхэлд нь харьцаж, нэг утга оноох боломжгүй. Харин түүний элементүүдийн авах утгыг тоочин бичнэ.
Тухайлбал 1D массивыг идэвхжүүлэхдээ:
өгөгдлийн_төрөл массивын_нэр [урт] = { утга0, утга1, утга2, ..., утгаурт-1} ;
гэж бичнэ. Энд:
· утга0, утга1, утга2, ..., утгаурт-1 – массивын элемент бүрийн авах анхны утга.
Ж.нь:
int numbers [5] = {12, 345, -41, 1361, 3};
numbers массивын эхний элемент 12, хоёр дахь элемент 345, гурав дахь элемент -41, дөрөв дэх элемент 1361, тав дахь элемент 3 байна.
float v [3] = {0.1564. 9.23, -1.0};
v массивын гурван элемент харгалзан 0.1564, 9.23, -1.0 гэсэн анхны утгуудыг авна.
unsigned char symbols [6] = {‘a’, ‘b’, ‘c’, ‘d’, ‘e’, ‘f’};
symbols массивын элементүүд харгалзан ‘a’, ‘b’, ‘c’, ‘d’, ‘e’, ‘f’ гэсэн тэмдэгт утгуудыг авна.
Бичигдсэн анхны утгын тоо массивын уртаас хэтрэхгүй байх ёстой. Эс тэгвээс хөрвүүлэгч алдаа заана. Харин массивын уртаас цөөн тоотой анхны утга бичигдсэн байвал массивын элементүүд эхнээсээ тэдгээр утгыг авч, үлдсэн элементүүд идэвхжихгүйгээр үлдэнэ. Ж.нь:
int M [8] = { 8, 4, 2};
Энд, M массивын зөвхөн эхний гурван элемент идэвхжээд үлдсэн элементүүд идэвхжихгүй.
Массивын уртыг ил зааж өгөлгүйгээр идэвхжүүлж бас болдог:
өгөгдлийн_төрөл массивын_нэр [ ] = { утга0, утга1, утга2, ... } ;
Ийм тохиолдолд хөрвүүлэгч өөрөө, { } дотор бичигдсэн анхны утгуудын тоогоор массивын уртыг тодорхойлдог. Ж.нь:
double d [ ] = {1.0. 2.0, 3.0, 4.0, 5.0};
2D массив идэвхжүүлэхдээ, ийм массивын мөр бүр нь өөрөө дахиад хэд хэдэн баганад хуваагдана гэдгийг тооцож, анхны утгуудыг мөр мөрөөр бүлэглэн { } дотор бичдэг. Ж.нь:
double A [3] [2] = { {10, 20}, {30, 40}, {50, 60} };
3 мөртэй 2 баганатай A массивын элементүүдийг мөрөөр нь бүлэглэн идэвхжүүлж байна. Тиймээс эхний мөрийн элементүүд харгалзан 10 ба 20, хоёр дахь мөрийн элементүүд харгалзан 30 ба 40, гурав дахь мөрийн элементүүд харгалзан 50 ба 60 гэсэн утгыг авна.
Дээрх бичиглэл нь.
double A [3] [2] = { 10, 20, 30, 40, 50, 60 };
гэсэн бичиглэлтэй эквивалент юм. Ө.х. мөр мөрөөр бүлэглэхгүйгээр идэвхжүүлэх бас боломжтой. Гэхдээ ингэх нь, 2D массивын шинж чанарыг бүрхэгдүүлэх тул учир дутагдалтай.
3D массив идэвхжүүлэхдээ, ийм массивын мөр бүр нь хэд хэдэн баганад, харин багана бүр нь хэд хэдэн “өндөрт” хуваагдана гэдгийг тооцож, мөр мөрөөр бүлэглэн { } бичиж, тус бүрийн дотор нь дахиад багана баганаар бүлэглэн { } дотор анхны утгуудыг бичдэг. Ж.нь:
int mmm [4] [3] [2] = { { {1, 2}, {11, 22}, {111, 222} },
{ {3, 4}, {33, 44}, {333, 444} },
{ {5, 6}, {55, 66}, {555, 666} },
{ {7, 8}, {77, 88}, {777, 888} } }
Г.м.-ээр олон хэмжээст массивыг идэвхжүүлж болно.
Мэдээж, бичигдсэн анхны утгын тоо олон хэмжээст массивын нийт элементийн тоогоос хэтрэхгүй байх ёстой. Эс тэгвээс хөрвүүлэгч алдаа заана. Харин элементийн тооноос цөөн анхны утга бичигдсэн, ө.х. массивыг бүхэлд нь идэвхжүүлээгүй байж болно.
Ж.нь дараах тохиолдлыг авч үзье. Энд 4 мөр, 6 баганатай 2D массивын зөвхөн 1-р баганын дагуух элементүүдийг идэвхжүүлж байгаа юм:
double z [4] [6] = { {1}, {2}, {3}, {4} };
Зургаар дүрсэлвэл:
1 | * | * | * | * | * |
2 | * | * | * | * | * |
3 | * | * | * | * | * |
4 | * | * | * | * | * |
Харин энэ жишээ 5 мөр, 4 баганатай “тэгш өнцөгт” массиваас “гурвалжин” массив үүсгэж байна:
int x [5] [4] = { {1}, {2, 3}, {4, 5, 6}, {7, 8, 9, 10} };
Зургаар дүрсэлвэл:
1 | * | * | * |
2 | 3 | * | * |
4 | 5 | 6 | * |
7 | 8 | 9 | 10 |
* | * | * | * |
Тогтмол массив
Тогтмол массив зарлах нь үндсэндээ хувьсагч массивыг идэвхжүүлэхтэй адилхан. Гэхдээ хамгийн урд нь const гэсэн нөөц үгийг бичих ёстой.
Тухайлбал 1D тогтмол массивыг зарлах:
const өгөгдлийн_төрөл массивын_нэр [урт] = { утга0, утга1, утга2, ..., утгаурт-1} ;
Ж.нь:
const unsigned char a [5] = { 23, 45, 8, 128, 2 };
const float values [6] = { 0.5, 2.1, 9.81, 4.5, 560.4, 7.0 };
const char letters [7] = {‘p’, ‘h’, ‘y’, ‘s’, ‘i’, ‘c’, ‘s’ };
2D тогтмол массив зарлаж буй жишээ:
const unsigned char z [3] [5] = { { 11, 12, 13, 14, 15 }, { 21, 22, 23, 24, 25 }, {31, 32, 33, 34, 35 } };
Массивын элементэд хандах. Массивын санах ой дахь дүрслэл
Өмнөх хэсэгт бид массивыг хэрхэн идэвхжүүлэх талаар үзсэн. Харин одоо, нэгэнт зарлагдсан хувьсагч массив дээр утга оноох г.м. биелэгдэх операторуудыг хэрхэн гүйцэтгэх талаар ярилцъя.
Нэгэнт массив нь олон элементээс тогтсон нийлмэл өгөгдөл учраас түүнтэй бүхэлд нь харьцаж, нэг утга оноох боломжгүй гэдгийг дурдсан билээ. Ө.х.:
массивын_нэр = утга;
гэсэн үйлдэл байж болохгүй юм. Ж.нь ийм програм байвал хөрвүүлэгч алдаа заах нь ээ:
/* жишээ програм №1 – алдаатай програм */
void main ( )
{
unsigned char d [3]; /* бүхэл unsigned char төрлийн 3 элементтэй d массив */
int buhel [10]; /* бүхэл төрлийн 10 элементтэй buhel массив */
d=12; /* энэ бол алдаа! */
buhel=0; /* энэ ч гэсэн алдаа! */
}
Зөвхөн массивын элементүүдэд хандалт хийж утга онооно. Элементэд хандалт хийхдээ:
массивын_нэр [элементийн_индекс]
гэсэн бичлэгийн загварыг баримтлана. Түүнд утга оноохын тулд:
массивын_нэр [элементийн_индекс] = утга;
гэж бичих хэрэгтэй. Элементийн индекс 0-ээс эхэлдгийг санах хэрэгтэй. Ө.х. хэрэв N элементтэй массив байна гэвэл элементийн индекс нь 0-ээс N-1 хооронд утга авна.
Ж.нь дараах програмыг харцгаая.
/* жишээ програм №2 */
void main ( )
{
unsigned char a [5] ; /* unsigned char төрлийн 5 элементтэй a массив */
float myarr [9] ; /* бодит float төрлийн 9 элементтэй myarr массив */
char ch [6] ; /* бүхэл char төрлийн 6 элементтэй ch массив */
a[0] = 12; /* a массивын 0-р элементэд 12 гэсэн утга оноох */
myarr[1] = -23.14; /* myarr массивын 1-р элементэд -23.14 гэсэн утга оноох */
ch[2] = ’R’; /* ch массивын 2-р элемент нь ‘R’ гэсэн утга авна */
ch[3] = ’?’; /* ch массивын 3-р элемент нь ‘?’ гэсэн утга авна */
ch[4] = 154; /* ch массивын 4-р элемент нь 154 гэсэн утга авна */
}
Дээрх жишээнд буй массивууд анх зарлагдахдаа санах ойд ямар байдалтай байрлахыг зургаар дүрслэн харуулъя. Тухайлбал a массив:
гэж дүрслэгдэнэ. Харин a[0]=12; үйлдэл хийгдсэний дараагаар:
болно. Энэ массивын элемент бүр нь unsigned char төрлийнх учраас санах ойн нэг нүдийг эзлэх тул массив өөрөө элементийнхээ тоотой тэнцүү тооны нүднүүдийг эзэлж байна.
Харин ch массив:
байна. ch[2]=’R’; ch[3]=’?’; ch[4]=154; гэсэн үйлдлүүдийн дараа:
болно. Энэ массивын элемент бүр char төрлийнх учраас мөн л санах ойн нэг нүдийг эзлэх ба массив өөрөө элементийнхээ тоотой тэнцүү тооны нүдийг эзэлж байна.
myarr массивын хувьд:
болно. Түүний нэг элемент нь бодит float төрлийнх учраас санах ойд 4 нүд эзлэх юм. Тиймээс массив өөрөө 4*3=12 нүд (байт) эзэлж байгаа.
Дээрх жишээ програмд, массивын элементийн индексийг дандаа тогтмол утгаар илэрхийлсэн байгаа. Тэгвэл элементийн индексийг хувьсагчаар илэрхийлж болно. Тухайлбал дараах дараах жишээг авч үзье.
/* жишээ програм №3 */
#include
#include
void main ( )
{
unsigned char mas [8]; /* unsigned char төрлийн 8 элементтэй mas массив */
int i; /* массивын элементийн индексийг илэрхийлэх хувьсагч */
for (i=0; i<=7; i++)
{
mas[i]=1; /* mas массивын i-р элементэд 1 гэсэн утга оноох */
printf(" mas[%d]=%u", i, mas[i]); /* i-р элементийн утгыг дэлгэцэнд гаргах */
}
getch ( );
}
Энэ програм ажиллаж дуусахад mas массивын бүх элемент бүгд 1 гэсэн утгыг авсан байна. Учир нь массивын элементийн индексийг i-ээр тэмдэглэж, элементэд 1 гэсэн утга оноосон, тэгээд утга оноох энэ үйлдэл for операторын давталтын бие болсон, гэтэл i нь өөрөө давталтын параметр учраас утга нь 0-ээс 7 хүртэл гүйнэ. Тиймээс элементийн индекс мөн 0-ээс 7 хүртэл гүйж, a[0]:=1, a[1]:=1, a[2]:=1, …, a[7]:=1 болж байгаа юм. Энэ жишээнд, for давталт ашиглан массивуудын бүх элементэд утга оноож байна. Тэгвэл while, do-while операторуудыг ашиглан мөн ингэж болох нь мэдээж.
2D массивын элементэд хандалт хийж, утга оноох нь:
массивын_нэр [элементийн_индекс1>][элементийн_индекс2] = утга;
гэж бичигдэнэ.
Ж.нь:
/* жишээ програм №4 */
void main ( )
{
unsigned char d2[4][3]; /* unsigned char төрлийн 4 мөр, 3 баганатай 2D массив */
d2[2][1] = 91; /* d2 массивын 2-р мөр 1-р баганын элемент 91 гэсэн утга авна */
d2[0][2] = 123; /* d2 массивын 0-р мөр 2-р баганын элемент 123 гэсэн утга авна */
}
2D массивын эхний индекс мөр, дараагийнх нь багана байдаг гэсэн. Яг энэ дарааллаар санах ойд байрлана:
Эхлээд 0-р мөрийн 3 баганын элементүүд, дараа нь 1-р мөрийн 3 баганын элементүүд г.м.-ээр дараалжээ. Хэдийгээр 2D массив матрицтай адил боловч санах ойд бол шугаман хэлбэртэй цуварч байрладаг байна.
1D массивын адилаар 2D массивын индексүүдийг мөн хувьсагчаар тэмдэглэж болно. Дараах жишээнд, 5x5 гэсэн квадрат массивыг 0-16 хүртэлх бүхэл тоогоор “бөглөж” байна.
/* жишээ програм №5 */
#include
#include
void main ( )
{
unsigned char sq[5][5];
unsigned char i, j;
for (i=0; i<=4; i++)
for (j=0; j<=4; j++)
{
sq[i][j]=i*j; /* sq массивын (i, j)-р элемент i*j гэсэн утга авна */
printf(" sq[%u][%u]=%u", i, j, sq[i][j]); /* (i, j)-р элементийн утгыг хэвлэх */
}
getch ( );
}
Энд давхар давталт хийгдсэнийг анхаарч байна уу. Гадна талын for давталтын параметр i-ийн нэг утганд дотоод for давталтын параметр j 0-ээс 4 хүртэл гүйсэн утга авна. i-ийн утга өөрөө мөн 0-ээс 4 хүртэл гүйнэ. Тиймээс sq[i] [j]=i*j; үйлдэл 5*5=25 удаа хийгдэх юм.
2D массивын элементэд утга оноодгийн адилаар 3 болон түүнээс олон хэмжээст массивт хандаж болно. Энэ тохиолдолд нэмэлт индексүүд орж ирнэ. 3D массивын элемент 3 индексээр, 4D массивын элемент 4 индексээр тодорхойлогдоно г.м. Ж.нь:
/* жишээ програм №6 */
void main ( )
{
int d3[4][2][5]; /* 3D d3 массив: 4 мөр, 2 багана, 5 өндөр? */
unsigned char d4[2][3][4][3]; /* 4D d4 массив: 2 мөр, 3 багана, 4 өндөр, 3 ? */
d3[1][0][3] = 2005; /* d3 массивын 1-р мөр 0-р баганын 3-р элементэд 2005 */
d4[0][2][0][2] = 64; /* d4 массивын 0-р мөр 2-р баганын 0-ийн 2-р элементэд 64 */
}
Олон хэмжээст массивын индексүүдийг хувьсагчаар тэмдэглэх тохиолдолд дор хаяж хэмжээсийн тоотой тэнцүү ширхэг хувьсагч ашиглагдаж болно.
Олон хэмжээст массив нь санах ойд мөн л шугаман хэлбэртэйгээр цуварч байрлана.
Тогтмол массив санах ойд байрлах хэлбэр нь адил төрлийн хувьсагч массив байрлахтай адил байна. Гол ялгаа нь, тийм массив зарлагдахад бүх элемент (бүх нүд) утгаар бөглөгдсөн байх болно.
Массивтай ажиллах
Одоо массив төрлийн өгөгдөлтэй хэрхэн ажиллах тухай ерөнхий ойлголт, дадлыг өгөх зорилгоор зарим жишээ авч үзье. Эдгээр нь, массивтай ажиллах үйл ажиллагаанд олонтаа таардаг, хамгийн хялбар тохиолдлууд юм.
· Жишээ 1. 1D массивын элементүүдийн утгыг гаднаас оруулах:
· Жишээ 2. 2D массивын элементүүдийн утгыг гаднаас оруулах:
·
· Жишээ 2_1. Массивын элементүүдийн нийлбэрийг олох: