Си хэл

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 төрлөөр утга буцааж байвал функцийн зарлагаанд буцаах утгын төрлийг заах шаардлагагүй байсан.

Жишээ нь:

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</code>
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 түлхүүр үгийг нэмсэн:

_Alignas
_Alignof

_Atomic
_Generic

_Noreturn
_Static_assert

_Thread_local

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

 Оператор буюу Үйлдэл 

Си хэлний үйлдлүүд:

 Арифметик үйлдэл: +, -, *, /, %
 Утга олгох үйлдэл: =
 Арифметик утга олгох үйлдэл: +=, -=, *=, /=, %=, &=, |=, ^=, <<=, >>=
 Бит логик үйлдэл: ~, &, |, ^
 Бит шилжүүлэх үйлдэл: <<, >>
 Булын логик үйлдэл: !, &&, ||
 Нөхцөлт үйлдэл: ? :
 Тэнцвэр шалгах үйлдэл: ==, !=
 Функц дуудах үйлдэл: ( )
 Нэмэгдүүлж хоргодуулах үйлдэл: ++, --
 Гишүүн уруу хандах үйлдэл: ., ->
 Объектын хэмжээг олох үйлдэл: 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" гэж хэвлэдэг.

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

#include <stdio.h>
main()
{
printf("hello, world\n");
}

Стандартын дагуу:
#include <stdio.h>
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 өргөтгөлтэй текст файл байх бөгөөд толгой файлыг програмд холбож өгснөөр түүн дотор тодорхойлогдсон функц, макро, төрөл, тогтмолуудыг хэрэглэх боломжтой болдог. 
Си хэлэнд дараах стандарт толгой файлд бий.
<stdio.h>  <math.h> <string.h> <time.h> <float.h> <[[<stdlib.h]]>

 Өгөгдлийн төрөл 

Цуваа

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

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

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

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

 Эшлэл 
  Regarded by many to be the authoritative reference on C.
: "Thompson had made a brief attempt to produce a system coded in an early version of C—before structures—in 1972, but gave up the effort."
: "The scheme of type composition adopted by C owes considerable debt to Algol 68, although it did not, perhaps, emerge in a form that Algol's adherents would approve of."
ISO/IEC 9899:201x (ISO C11) Committee Draft


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