Си хэл

**C** буюу **Си** нь 1969-өөс 1973 оны хооронд AT&T-ийн Беллийн Лабораторид Деннис Ритчи зохиосон ерөнхий зориулалтын програмчлалын хэл.

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

Сүүлд гарсан олон хэлнүүд Си-гээс шууд болон шууд бусаар зээлсэн байдаг. Тухайлбал C#, D, Go, Rust, Java, JavaScript, Limbo, LPC, Objective-C, Perl, PHP, Python, Verilog, Unix-ийн Си бүрхүүл багтана. 

Си хэл баталгаажихаас өмнө хөгжүүлэгчид Деннис Ритчи, Брайан Керниган хоёрын гаргасан номоор явж байлаа. Баталгаажаагүй энэхүү Си хэлийг "K&R" С гэдэг.  1989 онд Америкийн хэмжил зүйн байгууллагаас "ANSI C" (C89) хэлийг батлаж гаргажээ.  Дараа жил нь Олон улсын хэмжил зүйн байгууллага С90-г баталж гаргажээ.  ISO дараа нь 1995 онд олон улсын дэмжлэгийг гаргасан бөгөөд 1999 онд (C99) дахин хянагдсан. 2011 оны 12-р сард С11 хэмээх стандарт нь батлагдсан.

## Зорилго
Си хэл нь процедурын хэл.  Тиймээс Си нь системийн програмчлал мэтийн ассемблер хэлээр бичигддэг програмуудад хэрэгтэй юм.

Си хэл нь доод түвшний програмчлал хийж чаддаг систем голдоггүй (*Анг.* platform independent) зохиогдох эхлэлийг тависан. Олон төрлийн төхөөрөмж болон үйлдлийн системд Си программаа ажиллуулахад үндсэн кодод нээх их өөрчлөлт ордоггүй. Си хэл бол микроконтроллероос авахуулаад суперкомпьютерыг ч хүртэл програмчилдаг.

## Шинж чанар
Си-д бүх код нь "функц" гэдэг дэд програмууд дотор бичигддэг. Функцийн параметрууд нь үргэлж утгаар дамждаг. Параметрт хувьсагч дамжуулахдаа "заагч" төрлийг ашигладаг. Си програмын код нь баригдмал бус, цэгтэй таслалаар төгсгөх командыг хэрэглэдэг, угалзан хаалтаар кодоо бүлэглэдэг.

Си хэл нь дараах шинж чанартай:

* Цөөн, тогтсон *түлхүүр үг*-тэй (keyword). Шинээр тодорхойлсон нэр нь түлхүүр үгээсээ ялгаатай байх ёстой.
* Олон арифметик, логик операторуудтай: +, +=, ++, &, ~, гэх мэт. 
* Нэг мөрөнд хэд хэдэн утга олгох үйлдэл хийж болно.
* Функц нь шаардлагагүй бол утга буцаахыг шаарддаггүй.
* Бүх өгөгдөл нь өгөгдлийн төрөлтэй байх бөгөөд хооронд нь шилжүүлж болно. Тухайлбал: тэмдэгт төрлийг бүхэл тоон төрлөөр ашиглаж болно.
* Зарлагааны бичиглэл нь үгээр байх албагүй. Си хэлэнд "define" гэдэг түлхүүр үг байдаггүй. Харин үүний оронд өгөгдлийн төрлөөр эхэлж байгаа нь зарлагаа болдог. "function" гэдэг түлхүүр үгийн оронд аргументуудтай дугуй хаалт нь функцийг илэрхийлдэг.
* Шинээр тодорхойлсон (typedef) болон давхар төрлүүдтэй.
* Олон төрлүүдийг нэгтгээд нэг өгөгдлийн төрөл (struct) болгож болно.
* Цуваагаа дугаарлахдаа заагчаар (pointer) тооцоолдог. Бүтэц төрлөөс ялгаатай нь тэдгээр нь хувьсагч бөгөөд төрөлхийн операторуудаар утга олгох, харьцуулах үйлдэл хийж болохгүй. "array" гэдэг түлхүүр үг хэрэглэлгүйгээр дөрвөлжин хаалтаар тодорхойлдог, month[11].
* Дугаарлагдсан төрөл нь **enum** түлхүүр үгээр хэрэглэгддэг. Бүхэл тоо уруу чөлөөтэй хувирдаг.
* Тэмдэгт мөр нь тусдаа өгөгдлийн төрөл биш бөгөөд хоосон тэмдэгтээр (null character - '\0') төгссөн тэмдэгтүүдийн цуваагаар илэрхийлэгддэг.
* Компьютерын санах ой уруу доод түвшинд хандахдаа машины хаягийг заагч төрөл уруу хөрвүүлж ашигладаг.
Процедур (утга буцаадаггүй дэд програм)-ыг утгагүй **void** буцаах төрөлтэй функцээр илэрхийлдэг.
* Функц нь зарим функцийн мужид тодорхойлогдохгүй.
* Урьдчилан боловсруулагч (preprocessor) нь макрог ажиллуулдаг.
* Модуль болгон ашиглахдаа: файлуудаа тус тусад нь хөрвүүлж холбож нэгтгэнэ. Функц, өгөгдөл нь бусад файлдаа **static**, **extern** төрлүүдээр харагддаг.
* Оролт/Гаралт, тэмдэгт мөрийн үйлдэл, математик функц гэх мэт төвөгтэй функцууд нь санд тодорхойлогдсон байдаг.
* Си хэлэнд зарим шинэ, сүүлийн үеийн хэлнүүдэд байдаг давуу талууд байдаггүй: объект үүсгэх, санах ой цэвэрлэх (garbage collection) гэх мэт.

## Түүх
### Анхны хөгжүүлэлт
AT&T-ийн Беллийн Лабораторид 1969-өөс 1973 оны хооронд анх хөгжүүлсэн бөгөөд 1972 онд хамгийн оргил үе нь байжээ.   Кен Томпсоны BCPL програмчлалын хэлний хялбаршуулсан хувилбар болох B ("Би" гэж дуудна) гэдэг хэлнээс хөгжүүлсэн учир Си гэдэг нэр өгчээ.

Си хэлний үүсэл нь Ритчи, Томпсон хоёрын PDP-7 машины ассемблер хэлээр хөгжүүлсэн Unix үйлдлийн системтэй салшгүй холбоотой юм.  Тэгтэл үйлдлийн системээ PDP-11 уруу шилжүүлэхээр болж.  B хэл нь PDP-11 машинаа бүрэн ашиглаж чадахгүй байсан тул Си хэлийг хөгжүүлэх шалтгаан болжээ.

Анхны PDP-11 машины Unix систем нь ассемблер хэлээр хийгдсэн.  1973 онд Си хэл нь struct төрлийг нэмснээр Unix-ийн цөмийн ихэнх хэсгийг бичиж чадах чадамжтай болжээ.  Энэ нь үйлдлийн системийн цөмийг анх удаа ассемблер хэлнээс өөр хэлээр програмчилсан явдал юм. 1977 оны үеэр Ритчи, Стивен Си.Жонсон хоёр Unix үйлдлийн системийг шилжих (portable) чадвартай болгохоор Си хэлэнд өөрчлөлт оруулав.  Жонсоны Шилчих Чадвартай Си-гийн Хөрвүүлэгч нь шинэ системүүдэд Си хэлний хөгжүүлэлт хийхэд ашиглагдсан.

### K&R C
1978 онд Брайан Керниган, Деннис Ритчи нар "*The C Programming Language*" гэдэг номоо гаргав. Энэхүү ном нь олон жилийн турш уг хэлний тухай сурах бичиг болсон бөгөөд Си хэлний програмистууд уг номыг "K&R" гэж нэрлэдэг.  Номонд гарч буй Си хэлний хувилбарыг K&R C гэдэг. Хоёр дахь хэвлэлээсээ **ANSI C** стандартаар гаргажээ.

K&R нь дараах зүйлсийг багтаана:

* Стандарт оролт гаралтын сан
* *long int* өгөгдлийн төрөл
* *unsigned int* өгөгдлийн төрөл
* Давхар үйлдэлт утга олгох үйлдэл буюу =op (жишээ нь =-) төрлийн үйлдлийг андуурагдахаас сэргийлж хассан. Жишээ нь i =- 10 гэдэг нь i-ийн утгыг 10-аар хоргодуулах гэдэг нь i=-10 буюу i нь тэнцүү -10 гэдэгтэй андуурагдах явдал их тул =op үйлдлийг хассан байна.
1989 онд Си хэлний стандарт гарсан ч гэлээ олон жил K&R C хэл Си хэлний програмистуудад хэрэглэгдсээр байсан бөгөөд хуучин хөрвүүлэгчид хэрэглээнээс гараагүй, мөн зөв бичсэн K&R C нь стандарт Си-тэй нийцдэг байсантай холбоотой юм.

Си хэлний анхны хувилбаруудад функц нь *int* төрлөөр утга буцааж байвал функцийн зарлагаанд буцаах утгын төрлийг заах шаардлагагүй байсан.

Жишээ нь:

```cpp
long some_function();
/* int */ other_function();

/* int */ calling_function()
{
long test1;
register /* int */ test2;

test1 = some_function();
if (test1 > 0)
test2 = 0;
else
test2 = other_function();
return test2;
}
```

Тайлбарт оруулсан *int*-үүдийг K&R C-д хасаж болдог байсан ч сүүлд гарсан стандартаар заавал тавьдаг болсон.

K&R функцийн зарлагаанд функцийн аргументийн тухай мэдээлэл байдаггүй,  функцийн параметрийн төрлийг шалгадаггүй, мөн зарим хөрвүүлэгчид нь дотоод функцийн аргументийн тоо зөрөх юм уу аргументийн тоо, төрөл зөрсөн гадаад функцийг олон дуудахад анхааруулгын мэдээд өгдөг байв.  Код дахь функцийг шалгадаг Unix-ийн "lint" мэтийн тусдаа хэрэгслүүд хөгжүүлэгдэв.

K&R C хэвлэгдсэн онд AT&T болон өөр бусад компаниудаас гаргасан хөрвүүлэгчдийг дэмжих үндсэн бус зүйлүүд Си хэлэнд орж ирэв. Тэдгээр нь:

* *void* функц (утга буцаадаггүй функц)
* *struct*, *union* төрлийг буцаадаг функц (заагчаас гадна)
* *struct* төрөлд утга олгох
* Дугаарлагдсан төрөл (enum)
Си хэл түгэхийн хэрээр олон тооны нэмэлтүүд гарч тэдгээр бүгдийг стандартад оруулах шаардлага гарсан.

### ANSI C болон ISO C
1970 оны сүүлчээс эхлэн Си хэл нь олон төрлийн мейнфрейм компьютер, миникомпьютер, микрокомпьютер, ялангуяа нэлээд алдартай болж байгаа IBM PC-д гарав. 

1983 онд Америкийн Үндэсний Хэмжилзүйн Байгууллага (ANSI) Си хэлний стандарт буюу X3J11-ийг гаргав. 1989 онд "Програмчлалын Си хэл" ANSI X3.159-1989 Си хэлний стандарт батлагдав. Энэ хувилбарыг ANSI C, Стандарт Си, заримдаа С89 гэдэг.

1990 онд ANSI C стандартыг Олон Улсын Хэмжилзүйн Байгууллага (ISO)-аас ISO/IEC 9899:1990 стандартад авав . Заримдаа С90 ч гэдэг.  Тиймээс "С89", "С90" хоёр нь адилхан програмчлалын хэлнүүд юм.

### C99
ANSI/ISO-ын стандарчлалаас хойш Си хэл 7 жил шинэчлэгдээгүй. 1995 оны 1990 оны Си-гийн стандартын Нормжуулсан Сайжруулалт 1 (ISO/IEC 9899/AMD1:1995 буюу С95) олон хэлний тэмдэгтүүдийг дэмждэг, бас бусад зүйлсийг зассанаар хэвлэгдэв. 1990 оны сүүлжээр Си хэлний стандартыг дахин хянасан нь 1999 оны ISO/IEC 9899:1999 буюу С99 гарахад хүргэсэн юм. Үүнээс хойш гурван удаа засвар орсон.

С99-д inline функц, шинэ өгөгдлийн төрлүүд (Жишээ нь: long long int, complex), хэмжээ нь хувьсдаг цуваа, IEEE 754 хөвөх таслалтай тоо, хувьсагчтай макро, нэг мөр тайлбар (//-аар эхэлдэг) нар шинээр орсон. Эднээс ихэнх нь хэд хэдэн Си-гийн хөрвүүлэгчдэд дэмжигдээд эхэлсэн байсан.

### C11
2007 онд Си-гийн стандартыг дахин шинэчлэх ажил эхэлсэн бөгөөд 2011 оны 12-р сарын 8-нд стандарт гарах хүртлээ "C1X" гэж нэрлэгдэж байлаа.

## Си хэлний дүрэм 
Си хэл нь Си хэлний стандартад заасан дүрмийн дагуу бичигддэг. Си хэлний код нь чөлөөт бичиглэлтэй буюу нэг мөрөнд хэдэн ч үйлдлийг багтааж болдог. Тайлбар оруулахдаа /* */ гэсэн хоёр зааглагчийн хооронд юм уу //-аас хойшхи мөрөнд бичдэг. Тайлбар дотор тайлбар хийж болохгүй. Тайлбар доторх ямар ч код ажиллахгүй.

Си хэлний код нь зарлагаа болон функцийн тодорхойлолтоос тогтоно. Функцийн тодорхойлолт нь зарлагаа болон командуудаас тогтоно. Зарлагаа нь өгөгдлийн төрөл, хувьсагчийн нэрээс тогтоно. Өгөгдлийн төрөл нь struct, union, enum-аар шинэ тодорхойлогдсон шинэ төрөл байж болно. Угалзан хаалтаар ({}) кодоо бүлэглэнэ. if-else нөхцөлт үйлдэл, do-while, while, for-оор давталт ашиглан "Бүтэцлэгдсэн Програмчлал"-ыг хэрэгжүүлдэг.

Ихэнх команд нь цэгтэй таслалаар нь төгсдөг.

### Цагаан толгой 
Си хэлний цагаан толгойд:
* Латин цагаан толгойн том, жижиг үсгүүд: `a`–`z` `A`–`Z`
* Цифрүүд: `0`–`9`
* Тусгай тэмдгүүд: ! " # % & ' ( ) * + , - . / : ;  ? [ \ ] ^ _ { | } ~
* Хоосон тэмдэгтүүд: жижиг зай (space), хэвтээ урт зай (horizontal tab), босоо зай (vertical tab), шинэ хуудас (form feed), шинэ мөр (newline)
Шинэ мөр нь мөрийг төгсгөх үйлдэл бөгөөд яг нэг тэмдэгт байдаггүй ч Си хэлэнд нэг тэмдэгтээр тооцдог.

Си хэлний сүүлчийн хувилбар (C11)-т Unicode дэмжигдсэн бөгөөд \uDDDD (DDDD нь Unicode тэмдэгтийн код байна) хэлбэртэйгээр хэрэглэгдэнэ. Гэвч бүрэн нэвтэрч чадаагүй байна.

Си хэлэнд дуу гаргах (alert), арагшаа тэмдэгт устгах (backspace), шинэ мөр (carriage return) гэх мэт тэмдэгтүүд бий. Си хэлний стандартыг өөрчлөх бүрийд цагаан толгой нь нэмэгдсээр байгаа билээ.

### Түлхүүр үгс 
C89 нь 32 түлхүүр үгтэй:

:<>auto
:`break`
:`case`
:`char`
:`const`
:`continue`
:`default`
:`do`

:`double`
:`else`
:`enum`
:`extern`
:`float`
:`for`
:`goto`
:`if`

:`int`
:`long`
:`register`
:`return`
:`short`
:`signed`
:`sizeof`
:`static`

:`struct`
:`switch`
:`typedef`
:`union`
:`unsigned`
:`void`
:`volatile`
:`while`

C99 нь нэмэлт 5 түлхүүр үг оруулж ирсэн:

:`_Bool`
:`_Complex`

:`_Imaginary`
:`inline`

:`restrict`

C11 нь 7 түлхүүр үгийг нэмсэн:

### Оператор буюу Үйлдэл 
Си хэлний үйлдлүүд:

* *Арифметик үйлдэл:* +, -, *, /, %
* *Утга олгох үйлдэл:* =
* *Арифметик утга олгох үйлдэл:* +=, -=, *=, /=, %=, &=, |=, ^=, >=
* *Бит логик үйлдэл:* ~, &, |, ^
* *Бит шилжүүлэх үйлдэл:* >
* *Булын логик үйлдэл:* !, &&, ||
* *Нөхцөлт үйлдэл:* ? :
* *Тэнцвэр шалгах үйлдэл:* ==, !=
* *Функц дуудах үйлдэл:* ( )
* *Нэмэгдүүлж хоргодуулах үйлдэл:* ++, --
* *Гишүүн уруу хандах үйлдэл:* ., ->
* *Объектын хэмжээг олох үйлдэл:* sizeof
* *Харьцаа олох үйлдэл:* , >=
* *Хаяг авах, утга авах үйлдэл:* &, *, [ ]
* *Олон үйлдэл зааглах үйлдэл:* ,
* *Дэд илэрхийлэл ялгах үйлдэл:* ( )
* *Төрөл хувиргах үйлдэл:* (ТөрлийнНэр)

Си хэл нь = үйлдлээр математикийн тэнцүүг илэрхийлэлгүй Fortran, PL/I хэлийг дагаж утга олгох үйлдлийг илэрхийлдэг. Си хэлэнд =, == үйлдлүүд нь хоорондоо төстэй учир хооронд нь хольж солих явдал их байдаг. Андуурч бичсэн ихэнх тохиолдолд хөрвүүлэгч алдаа өгдөггүй. Жишээ нь: if(a=b+1) гэдэг нөхцөлт илэрхийлэлд a-д утга олгосны дараа a нь 0-ээс ялгаатай бол үнэн нөхцлийг илэрхийлнэ. Си хэл үйлдлүүдийн эрэмбээ өөрөө тохируулдаггүй. Тухайлбал == нь & | хоёроос өндөр эрэмбэтэй тул x & 1 == 0 гэдэг илэрхийлэлд эхлээд == үйлдлийг хийх тул (x & 1) == 0 гэж бичвэл зөв илэрхийлэл болох юм.

## "Hello, world" жишээ програм
"hello, world" жишээ програмыг K&R номын анхны хувилбарт Си хэлний удиртгал програм болгон ашиглажээ. Энэхүү програм нь стандарт гаралтын төхөөрөмж буюу дэлгэцэд "hello, world" гэж хэвлэдэг.

Анхны хувилбар:

```c
#include 
main()
{
printf("hello, world\n");
}
```

Стандартын дагуу:

```c
#include 
int main(void)
{
printf("hello, world\n");
}
```

Эхний мөрөнд урьдчилан боловсруулах файл *#include*-ээр эхэлсэн байна. Өөрөөр хэлбэл *stdio.h* файлын бүх текстийг уг мөрөнд орлуулж тавина гэсэн үг. *printf* функц *stdio.h* толгой файлд тодорхойлогдсон учир заавал зааж өгөх ёстой. Толгой файл нь өнцөгтэй хаалтан дотор заасан бол үндсэн толгой файлын хаягаас хайх бол давхар хашилтан (") дотор байвал зааж өгсөн хаягаас хайдаг.

Дараагийн мөр нь *main* функц. Си хэлэнд *main* функц нь их чухал үүрэгтэй. Програм ажиллахдаа *main* функцээс эхэлж ажилладаг. Өмнөх *int* гэдэг нь *main* функцийн буцаах утгын төрлийг заадаг. Дугуй хаалтан дотор *void* гэж байгаа нь функц аргументгүй гэдгийг илэрхийлнэ.

Нээж байгаа угалзан хаалтнаас *main* функцийн тодорхойлолт эхэлнэ.

Дараагийн мөрөнд стандарт сангийн printf функцийг "hello, world\n" аргументтэй дуудаж байна. *printf* функц нь өгөгдсөн форматтын дагуу хэлбэржүүлсэн тэмдэгт мөрийг (format string) хэвлэдэг. \n нь шинэ мөрийн тэмдэгтийг илэрхийлдэг. *printf* функц нь *int* төрлийн утга буцаадаг боловч уг буцаасан утгыг ашигласан зүйл байхгүй учир зүгээр хаягдах юм. Төгсгөлд нь цэгтэй таслал байх бөгөөд командыг төгсгөдөг үйлдэл юм.  

Хаасан угалзан хаалтаар *main* функцийн тодорхойлолт дуусна.

C99 стандартад *main* функц нь заавал *int* утга буцаах ёстой байдаг бөгөөд хаасан угалзан хаалтын өмнө *return 0;* гэсэн мөрийг бичдэг. *main* функц 0 утгыг буцааж байвал програм алдаагүй ажилласаныг илэрхийлдэг.

## Толгой Файл
Зарим програмын хэл нь толгой файл ашигладаг.Энэ нь програмчидад эх кодын тодорхой элемэнтдүүдийг ахин ашигладдаг файл болгодог. Си хэлний толгой файлууд нь
.h өргөтгөлтэй текст файл байх бөгөөд толгой файлыг програмд холбож өгснөөр түүн дотор тодорхойлогдсон функц, макро, төрөл, тогтмолуудыг хэрэглэх боломжтой болдог. 
Си хэлэнд дараах стандарт толгой файлд бий.
      

## Өгөгдлийн төрөл 
### Цуваа
Си хэлний Цуваа төрөлд заавал хэмжээг нь зааж өгсөн байх ёстой.  (С99 стандартад цувааны хэмжээг хувьсагчаар илэрхийлж болдог болсон.)  Харин ажиллаж байх үед цувааны уртыг стандарт сангийн *malloc* функцийн тусламжтайгаар санах ойгоос зай нөөцөлж авах боломжтой.  Си хэлэнд цуваа, заагч хоёр нь нэг нэг рүүгээ хувирч болдог.

Цувааны элементэд заагчаар хандаж болдог учир цуваанд хандахад цувааны урт дотор эсэхийг шалгадаггүй учир зарим хөрвүүлэгчид цувааны хязгаарыг шалгадаг сонголттой байдаг.  Програмаа бичихдээ цувааны хязгаарыг сайн тооцохгүй бол зөвшөөрөлгүй санах ой руу хандах, буруу өгөгдөл өөрчлөх, буффер дүүрэх, ажиллах үеийн алдаа гэх мэт янз бүрийн хүсээгүй үр дүн гаргадаг.  Автоматаар хязгаараа боловсруулдаггүй учир сайн тооцох хэрэгтэй.

Си хэлэнд тусдаа олон хэмжээстэй цуваа зарлах гэж байдаггүй ч цуваануудын цуваагаар дүрсэлдэг.

Олон хэмжээст цуваа нь ихэвчлэн тоон алгоритм (ихэвчлэн шугаман алгебр)-т матриц хадгалахад хэрэглэгддэг. 
Гэхдээ цуваа гэдэг маань цаанаа заагчид байдаг учир цувааны хязгаарыг сайн тодорхойлох шаардлагатай.

## Эшлэл 


Ангилал:Императив программчлалын хэл
Ангилал:Unix