C (мова праграмавання)

Мо́ва праграмава́ння  (вымаўляецца: Сі́) — стандартызаваная імператыўная мова праграмавання, створаная на пачатку 1970-х гадоў Кенам Томпсанам і Дэнісам Рычы для аперацыйнай сістэмы UNIX. Потым яна была перанесена на іншыя аперацыйныя сістэмы, і цяпер з’яўляецца адной з найбольш ужывальных моў праграмавання. Мове C часта аддаюць перавагу за яе эфектыўнасць, і гэта адна з самых пашыраных моў сістэмнага праграмавання, хоць з яе дапамогай можна пісаць і праграмы агульнага прызначэння. Яна часта выкарыстоўваецца пры навучанні праграмаванню, хоць стваралася не для пачаткоўцаў.

 Асноўныя рысы 

 Агляд 
C — адносна мінімалістычная мова праграмавання, бліжэйшая да апаратнага забеспячэння і мовы асэмблера, чым іншыя мовы праграмавання высокага ўзроўню. Сапраўды, C часам называюць «пераносным асэмблерам», паказваючы яго адрозненні ад моў праграмавання нізкага ўзроўню, напрыклад, асэмблера: код на C можна скампіляваць і выканаць на амаль любым камп’ютары, а код на асэмблеры можна выконваць толькі на той мадэлі камп’ютара, для якой ён напісаны. Таму мову C называюць мовай сярэдняга ўзроўню.

Пры распрацоўцы C асноўнай мэтай было стварэнне працэдурнай мовы праграмавання, якая дазволіла б пісаць праграмы з меншай колькасцю памылак, не ствараючы пры гэтым дадатковых цяжкасцей для аўтараў кампілятараў складанымі асаблівасцямі мовы.  Такім чынам, у C ёсць такія важныя рысы:

 Простая моўная база, у якой важныя функцыі, падобныя да матэматычных і файлавых, рэалізуюцца з дапамогай бібліятэк
 Засяроджанне на парадыгме працэдурнага праграмавання, з магчымасцямі праграмавання ў структурным стылі
 Простая сістэма тыпаў, якая не дапускае замнога безсэнсоўных аперацый
 Выкарыстанне мовы прэпрацэсара C, напрыклад, для стварэння макрасаў ці ўключэння частак кода
 Нізкаўзроўневы доступ к памяці праз выкарыстанне указальнікаў
 Невялікая колькасць ключавых слоў
 Параметры функцый заўсёды перадаюцца па значэнні, а не па спасылцы
 Указальнікі на функцыі, з дапамогай якіх ствараецца простая форма полімарфізму
 Вобласці дзеяння зменных
 Структуры (запісы), ці ствараемыя праграмістам састаўныя тыпы даных (struct), якія дазваляюць спалучаць даныя і кіраваць звязанай інфармацыяй як адзіным цэлым

Рысы іншых моў, якіх не хапае ў C:
 Бяспечнасць тыпаў
 Аўтаматычная зборка смецця
 Класы ці аб’екты з пэўнымі паводзінамі
 Развітая сістэма тыпаў
 Укладзеныя функцыі
 Універсальнае праграмаванне
 Перагрузка аператараў
 Падтрымка шматніцевасці і камп’ютарных сетак

Хоць спіс адсутных у C зручных канструкцый доўгі, іх адсутнасць дапамагла прыняць C, бо кампілятары для новых платформ пісаліся хутчэй, і праграмістам было лягчэй зразумець, што робіць той ці іншы код, і кантраляваць яго. Таму код на C часта выконваецца хутчэй за код на іншых мовах праграмавання.

Яшчэ адна з прычын шырокага выкарыстання C палягае ў тым, што кампілятары, бібліятэкі і інтэрпрэтатары іншых высокаўзроўневых моў часта ствараюцца на мове C.

 Прыклад «hello, world» 

Упершыню гэта простая праграма з’явілася ў першым выданні кнігі K&R і стала стандартнай уводнай праграмай у большасці падручнікаў па праграмаванні, незалежна ад мовы. Гэта праграма выводзіць «hello, world» у стандартны вывад, звычайна, ў тэрмінал ці на экран.

main()
{
    printf("hello, world\n");
}

Прыведзеная вышэй праграма нармальна скампілюецца на большасці сучасных кампілятараў. Аднак пры кампіляцыі згодна са стандартам ANSI C будзе выдадзена некалькі папярэджанняў. Акрамя таго, код не скампілюецца, калі кампілятар дакладна выконвае стандарт C99, бо вяртаемае значэнне не будзе лічыцца значэннем тыпа int пры адсутнасці іншага. Усё гэта можна выправіць, дадаўшы некалькі невялікіх змяненняў у зыходную праграму:

#include <stdio.h>

int main(void)
{
    printf("hello, world\n");

    return(0);
}

Разгледзім праграму па радках:

#include <stdio.h>
Першы радок праграмы з’яўляецца дырэктывай прэпрацэсара, #include. Гэты тэкст паказвае, што прэпрацэсару — першай праграме, якая апрацоўвае зыходны файл пры кампіляцыі — трэба замяніць гэты радок на названы ў ім файл. У дадзеным выпадку гэта загаловачны файл, stdio.h, які апісвае стандартныя функцыі ўводу і вываду. Вуглавыя дужкі вакол stdio.h паказваюць, што файл трэба шукаць у папцы, пазначанай як шлях пошуку загаловачных файлаў.

int main(void)
Наступны радок паказвае, што азначаецца функцыя пад назвай main. Функцыя main мае адмысловае значэнне ў праграмах на C. Калі выконваецца праграма, першай выклікаецца функцыя main(). Слова int паказвае, што вяртаемае значэнне — значэнне, якое будзе вылічана ў функцыі main — ёсць цэлым лікам (integer). Частка коду (void) паказвае, што функцыя main не прымае ніякіх аргументаў.

{
Тут фігурныя дужкі паказваюць месца, дзе пачынаецца код функцыі main.

    printf("hello, world\n");
Гэты радок выклікае (г.зн. выконвае код і вяртаецца да далейшага выканання праграмы) функцыю з назвай printf, якая аб’яўлена ў загаловачным файле stdio.h. У гэтым выкліку функцыі printf перадаецца адзін аргумент, радок тэксту «hello, world\n». Спалучэнне сімвалаў «\n» называецца escape-паслядоўнасцю і пераўтвараецца ў сімвал EOL (end-of-line, канец радка), які пераводзіць курсор у тэрмінале на пачатак новага радка. Функцыя printf вяртае значэнне тыпу int, аднак мы яго не выкарыстоўваем, таму не пішам ніякага кода для яго апрацоўкі.

    return(0);
Гэты радок заканчвае выкананне функцыі main вяртаннем значэння 0.

}
Гэта фігурная дужка паказвае канец кода функцыі main.

 Ключавыя словы 
У стандарце С89 азначаны 32 ключавыя словы:
auto double int structbreak else long switchcase enum register typedefchar extern return unionconst float short unsignedcontinue for signed voiddefault goto sizeof volatiledo if static while

Стандарт C99 дабаўляе пяць новых: _Bool, inline, _Complex, _Imaginary, restrict.

 Тыпы 

Тыпы ў C падобныя да тыпаў іншых пераемнікаў мовы ALGOL, такіх як Pascal, хоць яны і шмат у чым адрозніваюцца. Ёсць тыпы для цэлых лікаў розных памераў, знакавых і бяззнакавых, лікаў з плаваючай коскай, літар, пералічэнняў (enum), структур (struct) і аб’яднанняў (union), з дапамогай якіх можна захоўваць даныя некалькі тыпаў (але нельга захоўваць даныя двух розных тыпаў адначасова).

C вельмі часта выкарыстоўвае ўказальнікі і вельмі просты тып спасылак для захавання адрасоў памяці. Гэты адрас можна змяніць з дапамогай аператара прысвойвання ці арыфметыкі ўказальнікаў. Падчас выканання ўказальнік з’яўляецца адрасам у памяці, а падчас кампіляцыі — адрасам і тыпам даных, што дазваляе праверыць частку выразаў з указальнікамі падчас кампіляцыі. Указальнікі выкарыстоўваюцца ў C для розных мэт. Тэкставыя радкі звычайна прадстаўлены як указальнік на масіў сімвалаў. Дынамічнае выдзяленне памяці, якое апісана ніжэй, ажыццяўляецца з дапамогай указальнікаў.

Нулявы ўказальнік ёсць адмысловым указальнікам, які ўказвае на ячэйку памяці з адрасам 0. Працаваць напрамую з такім указальнікам нельга, звычайна яго выкарыстоўваюць, каб пазначыць неіснаванне пэўных аб’ектаў у памяці. Нулявыя ўказальнікі зручна выкарыстоўваць у адмысловых выпадках, напрыклад для запісу ўказальніку на наступны аб’ект у канцы спісу. Спробы звярнуцца да ячэйкі памяці, на якую ўказвае нулявы ўказальнік, можа прывесці да непрадбачаных вынікаўПры стварэнні звычайнай праграмы, хутчэй за ўсё, проста вылеціць сама праграма, і больш нічога. А вось пры распрацоўцы драйвера так можна зруйнаваць аперацыйную сістэму.. Указальнікі на тып void таксама існуюць, і паказваюць на аб’ект невядомага тыпу. Яны звычайна выкарыстоўваюцца ва ўніверсальным праграмаванні. Памер аб’екта, на які яны ўказваюць, невядомы, таму іх можна пераўтварыць ва ўказальнікі любога іншага тыпу.

Масівы ў C маюць нязменную даўжыню, якая вядома падчас кампіляцыі (праўда, у C99 былі дабаўлены масівы зменнай даўжыні); на практыцы, гэта не вельмі вялікая праблема, бо C дазваляе выдзяляць блокі памяці любой даўжыні ў час работы праграмы з дапамогай стандартнай бібліятэкі і выкарыстаць іх у якасці масіваў. У адрозненне ад іншых моў, у мове C масівы ёсць указальнікамі: як звычайны адрас у памяці і тып даных. Таму няма ніякіх праверак на выхад за межы масіву.

У мове C не прадугледжана адмысловых сродкаў падтрымкі мнагамерных масіваў, і мнагамерныя масівы азначаюцца зваротным чынам: як масівы масіваў. Так, напрыклад, двухмерны масіў ёсць масівам аднамерных масіваў, трохмерны — масівам двухмерных, і г.д. Увогуле кажучы, фізічнае ўладкаванне мнагамернага масіва у памяці камп’ютара залежыць ад таго, які гэты масіў: статычны ці дынамічны.

Мова C часта выкарыстоўваецца ў нізкаўзроўневым сістэмным праграмаванні, дзе можа ўзнікнуць неабходнасць разглядаць цэлы лік як адрас у памяці, лік з плаваючай коскай двайной дакладнасці як цэлы лік ці адзін тып указальнікаў як іншы. Для такіх выпадкаў у C есць пераўтварэнне тыпаў, з дапамогай якога выконваецца няяўны перавод аднаго значэння тыпу ў значэнне іншага тыпу. Няяўнае пераўтварэнне тыпаў можа быць небяспечным, бо часам працуе не так, як на першы погляд можна было б чакаць, і ў выніку прыводзіць да цяжка ўлоўных памылак.

 Захоўванне даных 

У мове C ёсць тры спосабы атрымаць памяць для аб’ектаў:
 Статычнае выдзяленне памяці: месца для аб’екта выдзяляецца ў двайковым выканальным файле падчас кампіляцыі. Пры запуску праграмы загрузчык адлюстроўвае гэтыя аб’екты ў аператыўную памяць; там яны знаходзяцца на працягу ўсяго існавання праграмы ў памяці.
 Аўтаматычнае выдзяленне памяці: часовыя аб’екты можна захоўваць у стэку, і месца, якое выкарыстоўваецца імі, аўтаматычна вызваляецца пасля таго, як завяршаецца выкананне блока, дзе яны выкарыстоўваліся
 Дынамічнае выдзяленне памяці: праграміст можа запрасіць блок памяці пажаданага памеру падчас выканання праграмы з дапамогай функцый malloc(), realloc() з вобласці памяці, якая называецца кучай; гэтыя блокі можна выкарыстоўваць ізноў, пасля таго як праграміст вярнуў іх з дапамогай функцыі free()

Кожны са шляхоў выдзялення памяці выкарыстоўваецца ў розных выпадках і мае свае перавагі і недахопы. Напрыклад, статычнае выдзяленне памяці не патрабуе выклікаў адмысловых працэдур, пры аўтаматычным выдзяленні кампілятар зменіць толькі адну сістэмную зменную, а для дынамічнага выдзялення памяці патрэбна шмат коду як з боку праграміста, так і з боку кампілятара. Аднак памяць стэка звычайна вельмі абмежаваная ў параўнанні з памяццю кучы, і толькі дынамічнае выдзяленне памяці дазваляе выдзяліць блок памяці, памер якога будзе вядомы толькі падчас выканання праграмы. У большасці праграм на С выкарыстоўваюцца ўсе тры шляхі.

Там, дзе гэта магчыма, лепей карыстацца аўтаматычным і статычным выдзяленнем памяці, бо пры іх выкарыстанні памяці выдзяляе кампілятар, і праграмісту не трэба пісаць код для выдзялення і вызвалення памяці, які часта прыводзіць да памылак. Аднак даволі часта памер структур даных змяняецца падчас выканання праграмы; для выкарыстання аўтаматычнага і статычнага выдзялення патрэбна ведаць памер падчас кампіляцыі, таму часта (напрыклад, пры стварэнні масіваў са зменнай даўжынёй) трэба выкарыстоўваць дынамічнае выдзяленне памяці.

 Сінтаксіс 

У адрозненні ад моў, падобных да Fortran 77, у C праграміст можа выкарыстоўваць прабелы і пераводы радкоў там, дзе пажадае. Каментары могуць быць:
 шматрадковымі: любы тэкст паміж першым з’яўленнем /* і да першага з’яўлення */;
 аднарадковымі (у C99): любы тэкст пасля // да канца радка.

Амаль любы зыходны файл утрымлівае аб’яўленні і азначэнні функцый. У сваю чаргу, азначэнні функцый змяшчаюць аб’яўленне і інструкцыі (прадпісанні). Аб’яўленні або азначаюць новыя тыпы з дапамогай ключавых слоў (падобных да struct, union, і enum), або ўводзяць новыя зменныя пэўных тыпаў і задаюць іхнія значэнні.

Прыклад: аб’яўленне структуры Пункт (Point) з дзвюма цэлымі каардынатамі (x, y)
struct Point{
	int x;
	int y;
};

Прыклад: аб’яўленне зменнай x тыпу «лік з плаваючай коскай двайной дакладнасці»
double x;

Ключавыя словы, падобныя да char, int, float і г.д., а таксама сімвал * у спалучэнні з імі дазваляюць прызначаць зменным пэўныя ўбудаваныя тыпы. Каб пазначыць межы дзеяння аб’яўленняў і кіруючых структур, кавалкі (блокі) зыходнага кода змяшчаюцца ў фігурныя дужкі ({ і }).

Прыклад:
unsigned int x; // зменная тыпу "бяззнакавы цэлы"
char *p; // зменная-ўказальнік на зменную сімвальнага тыпу

У мове C асноўную работу выконваюць інструкцыі. Большасць інструкцый ёсць выразамі-аператарамі, апрацоўка якіх складаецца з вылічэння пэўнага выразу і прысвойвання новага значэння зменным. Інструкцыі кіравання будуюцца з дапамогай ключавых слоў if, else, switch, do, while і for. Адвольныя пераходы ажыццяўляюцца з дапамогай аператара goto. Існуюць убудаваныя аператары для выканання простых арыфметычных, лагічных, пабітавых аперацый, прысвойванняў, параўнанняў, доступу па індэксе к элементам масіва. Таксама ў выразах можна выклікаць функцыі, у тым ліку стандартныя бібліятэчныя.

 Сістэма кампіляцыі 

 GCC 
Драйвер кампілятара GCC праводзіць шэраг аперацый з зыходным файлам на мове C, каб канверсаваць яго ў , што можа быць запушчаны ў сістэме. Праграмы, якія выконваюць гэтыя аперацыі, завуцца сістэмай кампіляцыі. Працэс складаецца з наступных фаз:
 Перадапрацоўка. Сістэмныя загаловачныя файлы, такія як stdio.h (задаюцца ў кодзе праз каманду include), далучаюцца ў файл з кодам. У выніку ствараецца файл з кодам C, звычайна з суфіксам .i.
 Кампіляцыя. Тэкставы файл з папярэдняй фазы перакладаецца ў праграму на мове асэмблера з суфіксам .s. Атрыманы файл пералічвае машынныя інструкцыі ў тэкставым фармаце, які можа быць прачытаны чалавекам.
 Асэмбляванне. Праграма-асэмблер перакладае .s-файл у інструкцыі на машыннай мове, каторыя могуць выконвацца камп’ютарам. Гэта прыводзіць да стварэння перамяшчальнай аб’ектнай праграмы з суфіксам .o.
 . Аб’ектная праграма звязваецца з іншымі, загадзя скампіляванымі аб’ектнымі праграмамі, напрыклад для выканання функцыі printf. Выканальны аб’ектны файл, атрыманы ў працэсе лінкоўкі, можа быць загружаны ў памяць і выкананы камп’ютарам.

 Праблемы 

Вядомы выраз: «З дапамогай C лёгка стрэліць ва ўласную нагу». Інакш кажучы, мова C дапускае многія дзеянні, даволі часта непажаданыя, таму многія памылкі праграмавання кампілятар не выяўляе, і ў выніку яны могуць праявіцца толькі ў час выканання праграмы. Гэта прыводзіць да непрадбачаных паводзін праграмы і «дзірак» пры выкананні. Дыялект Cyclone мовы C развязвае частку гэтых праблем.

З аднаго боку, праверкі на адсутнасць такіх памылак істотна запавольвалі выкананне праграмы ў той час, калі мова C стваралася. З другога боку, аўтары хацелі зрабіць C як мага болей эфектыўнай і гнуткай; чым больш магутная мова, тым складаней пісаць праграмы з яе дапамогай. Частка праверак выконваецца знешнімі прыладамі.

 Выдзяленне памяці 

Адна з праблем C палягае ў тым, што дынамічна выдзеленыя аб’екты не ініцыялізуюцца; яны захоўваюць тое, што знаходзілася да іх на тым месцы ў памяці (гэта называюць смеццем). Змест смецця амаль немагчыма прадбачыць, бо ён змяняецца не толькі ў залежнасці ад камп’ютара, але нават пры розных запусках праграмы ці розных выкліках адной функцыі. Таму, калі праграма выкарыстоўвае неініцыялізаваныя даныя, вынік прадбачыць немагчыма. Большасць сучасных кампілятараў папярэджваюць пра гэтую праблему ў частцы выпадкаў, аднак поўнасцю выключыць гэту праблему немагчыма.

Яшчэ адной праблемай з’яўляецца тое, што памяць з кучы нельга выкарыстоўваць ізноў, пакуль праграміст не вызваліць яе функцыяй free(). Таму, калі праграміст забудзе вярнуць памяць, праграма будзе працягваць трымаць яе, у выніку разам з новымі выдзяленнямі ўсё болей і болей памяці будзе спажывацца дарэмна. Гэта праява называецца ўцечкай памяці. Каб пазбегнуць уцечкі памяці, неабходна своечасова вызваляць памяць, што ўскладае на праграміста дадатковы клопат. Гэтыя праблемы развязаны ў мовах з аўтаматычнай зборкай смецця.

 Указальнікі 

Указальнікі з’яўляюцца адным з найбольшых небяспечных сродкаў мовы C; яны не правяраюцца, таму яны могуць указваць на любы аб’ект любога тыпу, у тым ліку код, і могуць мяняць гэты аб’ект, што прывядзе да непрадбачаных вынікаў. Указальнікі можна змяняць з дапамогай арыфметыкі ўказальнікаў, памяць, на якую яны ўказваюць, можа быць вернута, выкарыстана паўторна. Указальніку можна прысвоіць любое значэнне з дапамогай пераўтварэння тыпаў. Яшчэ адной праблемай указальнікаў з’яўляецца тое, што ў C дапускаецца вольна пераўтвараць адзін тып указальнікаў у іншы. У іншых мовах гэтыя праблемы развязваюцца выкарыстаннем болей абмежаваных тыпаў спасылак.

 Масівы 

Хоць у мове C ёсць падтрымка статычных масіваў, у ёй не выконваецца праверка на выхад індэкса за межы масіву. Таму можна звярнуцца да шостага элемента масіву з пяці элементаў, і невядома, да чаго гэта прывядзе. Такая з’ява называецца перапаўненнем буфера. Перапаўненне буфера з’яўляецца крыніцай шматлікіх дзірак пры выкананні ў праграмах на C. З іншага боку, праверка індэкса прыводзіць да зніжэння хуткасці выканання праграм, асабліва ў вылічэннях, і, як лічыцца, не сумяшчальнае з мінімалістычным замыслам C.

Мнагамерныя масівы патрэбны ў лікавых алгарытмах (у асноўным іх выкарыстоўваюць у лінейнай алгебры) для захоўвання матрыц. Іх рэалізацыя ў C нязручная і ў цэлым не вельмі падыходзіць для рэалізацыі гэтай задачы. Гэта праблема апісана ў кнізе Numerical Recipes in C, Chap. 1.2, page 20 ff (read online). У гэтай кнізе ёсць вырашэнне гэтай праблемы, хоць яно і дастаткова аб’ёмнае.

 Функцыі са зменнай колькасцю аргументаў 

Яшчэ адной праблемай з’яўляюцца функцыі, якія прымаюць зменную колькасць аргументаў. У адрозненне ад большасці функцый C, яны не маюць прататыпаў, таму праверка правільнасці іх аргументаў, як правіла, не выконваецца, ды і немагчыма без дадатковай інфармацыі. Вынік перадачы няслушнага тыпу даных прадбачыць немагчыма, і часта ён прыводзіць да аварыйнага заканчэння работы праграмы. Акрамя таго, функцыям са зменнай колькасцю параметраў нельга перадаваць нулявыя ўказальнікі.

Праверка правільнасці тыпаў функцый са зменнай колькасцю аргументаў залежыць выключна ад якасці рэалізацыі, аднак многія сучасныя кампілятары выконваюць праверку тыпаў пры выкліках функцыі printf і папярэджваюць пры памылковым спісе аргументаў. Аднак не ўсе выклікі printf можна праверыць статычна, бо радок фармату можна ствараць у час выканання.

 Сінтаксіс 

Сінтаксіс C часта капіруецца ў іншых мовах праграмавання, аднак часта кажуць, што ён з’яўляецца адным са слабейшых пунктаў мовы. Сярод праблем сінтаксісу C наступныя:

 Прататыпы функцыі, якія на самай справе дапускаюць любы набор параметраў, што звязана з адсутнасцю прататыпаў у K&R C
 Не дужа відавочныя прыярытэты аператараў, напрыклад, у аператара == большы прыярытэт, чым у аператараў & і | у выразах на ўзор: x & 1 == 0 (што пераўтвараецца ў: x & (1 == 0)).
 Выкарыстанне аператара =, які ў матэматыцы азначае роўнасць, для абазначэння прысвойвання, што прыводзіць да неправільных прысвойванняў у праверках. Выкарыстанне аператара = для прысвойвання было ідэяй Рычы, які адзначыў, што прысвойванне выконваецца часцей за праверку на роўнасць.
 Адсутнасць бінарных аператараў для складаных аб’ектаў, у асаблівасці для аперацый над радкамі літар, што робіць праграмы, у якіх такія тыпы часта ужываюцца, вельмі складанымі для чытання. 
 Частае выкарыстанне сімвалаў пунктуацыі, нават там, дзе гэта не вельмі зразумела, — напрыклад, выкарыстанне && і || замест and і or.
 Не відавочны сінтаксіс аб’яўлення тыпаў (некаторыя элементы — прэфіксныя, некаторыя — постфіксныя, да таго ж яны маюць розныя прыярэтэты, што часам патрабуе ўважліва ставіць дужкі, каб атрымаць пажаданы тып), асабліва аб’яўленняў указальнікаў на функцыі.

 Праблемы падтрымкі 

Мова C мае таксама і іншыя праблемы, якія не выклікаюць памылак напрамую, але замінаюць праграмісту ствараць надзейную, падтрымліваемую, пашыральную сістэму. Сярод такіх праблем ёсць наступныя:
 Крохкая сістэма ўключэння аб’яўленняў (#include), заснаваная на ўключэнні тэксту, што можа істотна павялічваць час кампіляцыі.

 Слабасць сістэмы тыпаў, пры якой можна скампіляваць адназначна памылковыя праграмы.

 Староннія інструменты для праверкі 

Для дапамогі праграмістам на C былі створаны праграмы, якія дапамагаюць пазбегнуць памылак у многіх выпадках. Аўтаматычная праверка зыходнага кода дапамагае незалежна ад мовы праграмавання, і для C існуюць такія праграмы, напрыклад Lint. Звычайна Lint выкарыстоўваецца для праверкі кода пры першым напісанні праграмы, і праграма кампілюецца пасля праверкі. Таксама існуюць бібліятэкі для праверкі індэксаў масіваў і абмежаванай формы зборкі смецця, аднак яны не ўваходзяць у стандартную бібліятэку C.

Трэба разумець, што нават такія інструменты не могуць дапамагчы ва ўсіх выпадках. Дзякуючы гібкасці мовы, шмат тыпаў памылак у C, сярод якіх памылкі, звязаныя з функцыямі са зменнай колькасцю аргументаў, выхад за межы масіву ці няслушнае выдзяленне памяці, немагчыма праверыць поўнасцю. Аднак нават у гэтых выпадках можна вылавіць найбольш тыповыя памылкі.

 Гісторыя 

 Раннія распрацоўкі 

Распрацоўка C пачалася ў AT&T Bell Laboratories паміж 1969 і 1973 гадамі; паводле слоў Рычы, найбольшая частка мовы была створана ў 1972 годзе. Мову назвалі C, бо многія рысы яна пераняла з ранейшай мовы B.

Дакладна невядома, адкуль пайшла назва «B»: ад мовы праграмавання Кена Томпсана BCPL ці ад яго ж мовы Bon, названай у гонар яго жонкі Боні.

Існуе шмат міфаў як пра задумку мовы C, так і пра звязаную з ёй аперацыйную сістэму UNIX.

У 1973 годзе мова C была ўжо дастаткова магутнай, каб напісаць на ёй большую частку ядра UNIX, якое было перапісана з мовы асэмблера PDP-11/20. Гэта было адно з першых ядзер аперацыйнай сістэмы, напісанае не на асэмблеры (раней былі створаны сістэма Multics на PL/I, TRIPOS на BCPL і MCP (Master Control Program) для Burroughs B5000 на мове ALGOL у 1961 годзе).

 K&R C 

У 1978 годзе Дэніс Рычы і Браян Кёрніган апублікавалі першую рэдакцыю кнігі The C Programming Language. Гэтая кніга, вядомая сярод праграмістаў на C як K&R, выкарыстоўвалася шмат год як нефармальная спецыфікацыя мовы. Версія C, апісаная ў гэтай кнізе, часта называецца K&R C. (У другой рэдакцыі кнігі апісваецца стандарт ANSI C, інфармацыя пра які змешчана ўнізе.)

Сярод рыс мовы, уведзеных у K&R C, былі наступныя:

 тып даных struct
 тып даных long int
 тып даных unsigned int
 Аператар =+ быў заменен на +=, і так далей (=+ ствараў памылкі для лексічных аналізатараў; напрыклад, i =+ 10 ў параўнанні з i = +10).

Часта лічаць, што K&R C — асноўная частка мовы, якую кампілятар мае падтрымліваць. Шмат год, нават пасля стварэння ANSI C, гэтая версія мовы лічыцца найбольш пераноснай, бо не ўсе кампілятары падтрымліваюць ANSI C, а адносна добра напісаны код на K&R C таксама слушны і для ANSI C.

У гэтай рэалізацыі C трэба аб’яўляць толькі тыя функцыі, якія вяртаюць значэнне не тыпу int. Функцыя без папярэдняга аб’яўлення лічылася функцыяй, якая вяртае значэнне тыпу int.
Прыклад выкліку з папярэднім аб’яўленнем:
long int SomeFunction();

int CallingFunction()
{
    long int ret;
    ret = SomeFunction();
}
Прыклад выкліку без папярэдняга аб’яўлення:
int SomeOtherFunction()
{
    return 0;
}

int CallingFunction()
{
    int ret;
    ret = SomeOtherFunction();
}

У прататыпах функцый K&R не было інфармацыі пра аргументы функцыі, таму большасць кампілятараў таго часу не выконвала праверку тыпаў параметраў, хоць некаторыя кампілятары папярэджвалі пра выклік функцыі з няправільнай колькасцю аргументаў.

Пасля публікацыі K&R C у мову было ўключана некалькі «неафіцыйных» дапаўненняў, якія падтрымліваліся кампілятарамі ад AT&T і некаторымі іншымі. Сярод іх наступныя:

 функцыі void і тып даных void *
 функцыі, што вяртаюць тыпы struct і union
 магчымасць выкарыстоўваць назвы палёў struct некалькі разоў у розных тыпах
 прысвойванне тыпаў даных struct
 ключавое слова const для аб’ектаў, прызначаных толькі для чытання
 стандартная бібліятэка C, функцыянальнасць якой была амаль аднолькавай у большасці кампілятараў
 пералічэнні
 тып float аднакратнай дакладнасці

 ANSI C і ISO C 

У пачатку 1970-х C пачала замяняць BASIC на пазіцыі вядучай мовы праграмавання для мікракамп’ютараў. У 1980-х яна была перанесена на IBM PC, і яе папулярнасць пачала хутка расці.
У той жа час Б’ёрн Страўструп і іншыя людзі з Bell Laboratories пачалі работу па ўключэнні аб’ектна-арыентаваных канструкцый у C.
Мова, якую яны стварылі, C++, цяпер з’яўляецца адной з самых ужывальных моў праграмавання для аперацыйнай сістэмы Microsoft Windows; C застаецца болей папулярнай у свеце UNIX. Яшчэ адной мовай на аснове C стала Objective-C, якая таксама дадае аб’ектна-арыентаваныя магчымасці ў мову C. Цяпер яна не так пашырана, як C++, аднак яна выкарыстоўвалася для распрацоўкі праграм Cocoa пад Mac OS X.

У 1983 годзе Амерыканскі нацыянальны інстытут стандартаў (American National Standards Institute, ANSI) сфарміраваў камітэт, X3J11, для стварэння спецыфікацыі C. Пасля доўгага  працэсу гэты стандарт быў створаны ў 1989 годзе і ратыфікованы як ANSI X3.159-1989 «Programming Language C». Гэтую версію мовы звычайна называюць ANSI C.
У 1990 годзе стандарт ANSI C (з некалькімі невялікімі зменамі) быў прыняты Міжнароднай арганізацыяй стандартызацыі (International Organization for Standardization, ISO) як ISO/IEC 9899:1990.

Адной з мэт працэсу стандартызацыі ANSI C было стварэнне такога надмноства K&R C, якое б уключала шмат неафіцыйных дапаўненняў.
Камітэт стандартаў таксама дадаў некалькі новых магчымасцей, такіх як прататыпы функцый (узятыя з C++) і больш развіты прэпрацэсар.

Цяпер мова ANSI C падтрымліваецца амаль усімі распаўсюджанымі кампілятарамі. Большасць кода, які пішацца сёння, заснавана на ANSI C. Любая праграма, напісаная толькі на стандартным C гарантавана выконваецца на любой платформе з рэалізацыяй C. Аднак шмат праграм пішуцца толькі для нейкай адной платформы, бо
 (i) яны выкарыстоўваюць нестандартныя бібліятэкі, напрыклад для вываду графікі,
 (ii) некаторыя кампілятары выкарыстоўваюць не ANSI C ці яе паслядоўнікаў у стандартным рэжыме,
 (iii) праграма абапіраецца на платформазалежныя тыпы даных і парадак байтаў у фізічнай памяці.

Каб падзяліць код на часткі ANSI і K&R, можа выкарыстоўвацца макрас __STDC__.
#if __STDC__
extern int getopt(int,char * const *,const char *);
#else
extern int getopt();
#endif

Часам выкарыстоўваецца #if __STDC__, як у кодзе вышэй, ці #ifdef __STDC__, бо некаторыя кампілятары азначаюць __STDC__ роўнай нулю, каб паказаць адсутнасць падтрымкі стандарту ANSI.

 C99 

Пасля працэсу стандартызацыі ANSI, мова C пэўны час захоўвалася адносна нязменнай, у той час як мова C++ развівалася далей. (У 1995 годзе была створана Нарматыўная папраўка 1, аднак гэтую версію не часта згадваюць.) Аднак у канцы 1990-х стандарт быў перагледжан, што прывяло да публікацыі ISO 9899:1999 у 1999 годзе. Гэты стандарт, як правіла, называюць C99. Ён быў прыняты як стандарт ANSI ў сакавіку 2000 года.

Сярод новых магчымасцей C99 ёсць наступныя:

 убудоўваемыя (inline) функцыі (пры кампіляцыі іхні код падстаўляецца на месца выкліку)
 зменныя можна аб’яўляць у любым месцы (як у C++), а не толькі пасля іншага аб’яўлення ці на пачатку састаўнога аператара (compound statement)
 некалькі новых тыпаў даных, у тым ліку long long int, тып даных boolean і тып complex для падтрымкі камплексных лікаў
 масівы са зменнай даўжынёй
 падтрымка аднарадковых каментараў, якія пачынаюцца з //, як у BCPL ці C++, якія і раней падтрымліваліся многімі кампілятарамі C як дапаўненне
 некалькі новых функцый у стандартнай бібліятэцы, такіх як snprintf()
 некалькі новых загаловачных файлаў, такіх як stdint.h

Зацікаўленасць у падтрымцы новых магчымасцей С99 розная. Калі GCC і некалькі іншых кампілятараў цяпер падтрымліваюць большасць магчымасцей C99, кампілятары ад Microsoft і Borland — не, і, здаецца, гэтыя кампаніі не зацікаўлены ва ўключэнні такой падтрымкі.

 Адносіны з C++ 

Першапачаткова мова праграмавання C++ была створана на аснове C. Аднак не кожная праграма на C з’яўляецца слушнай праграмай на C++. А як дзве гэтыя мовы развіваліся паасобку, то колькасць несумяшчальнасцей паміж дзвюма мовамі пастаянна нарастала . Апошні перагляд мовы C, C99, увёў шмат несумяшчальных з мовай C++ асаблівасцей. Адрозненні ствараюць складанасці ў напісанні праграм і бібліятэк, якія можна правільна скампіляваць на C і С++, і блытаюць тых, хто праграмуе на абедзвюх мовах. Акрамя таго, вельмі складана пераносіць адметнасці адной мовы ў другую.

Б’ёрн Страўструп, стваральнік C++, неаднаразова паўтараў , што несумяшчальнасць паміж C і C++ павінна быць як мага меншай, каб захаваць аперацыйную сумяшчальнасць паміж гэтымі мовамі. Аднак існуе пункт гледжання, што, раз мовы C і C++ — розныя, то сумяшчальнасць паміж імі зручная, але не абавязковая. У адпаведнасці з такім меркаваннем, намаганні дзеля скарачэння несумяшчальнасці не павінны перашкаджаць спробам палепшыць кожную мову паасобку.

У C99 з’явілася шмат магчымасцей, якія ўпершыню з’явіліся ў C++. Сярод іх наступныя:
 Абавязковыя аб’яўленні прататыпаў функцый
 Ключавое слова inline
 Неабходнасць аб’яўлення ўсіх тыпаў вяртаемых значэнняў, у тым ліку тыпу int

 Выкарыстанне C ў якасці прамежкавай мовы 
Некаторыя высокаўзроўневыя мовы праграмавання не кампілююцца ў аб’ектны ці машынны код напрамую, а пераўтвараюцца ў код мовы C, такім чынам выкарыстоўваючы яе ў якасці прамежкавай мовы. Далей такі перакладзены код падаюць на ўваход кампілятару мовы C, які стварае аб’ектны або машынны код. Гэта зроблена з мэтаю дасягнуць пераноснасці і аптымізацыі кода, бо кампілятары мовы C існуюць для пераважнай большасці працэсараў і аперацыйных сістэм, акрамя таго, большасць кампілятараў C ствараюць добра аптымізаваны аб’ектны і машынны код. У выніку, любая мова праграмавання, якая перакладаецца на C, адразу становіцца пераноснай і здатнай да вытворчасці аптымізаванага аб’ектнага і машыннага кода. Аднак C распрацоўвалася як мова праграмавання, а не мэтавая кампілятарная мова, і, такім чынам, не вельмі падыходзіць на роль прамежкавай мовы. Гэта прывяло да распрацоўкі C-падобных прамежкавых моў, такіх як, напрыклад, C--.

 Гл. таксама 
 bc
 C++
 Objective-C
 Стандартная бібліятэка мовы C
 Сінтаксіс мовы C
 Тыпы даных мовы C
 Аператары ў мовах C і C++
 Pascal і C
 C і C++

 Зноскі 

 Далейшае чытанне 
 Brian Kernighan, Dennis Ritchie: The C Programming Language. Also known as K&R — The original book on C.
 1st, Prentice Hall 1978; ISBN 0-13-110163-3. Pre-ANSI C.
 2nd, Prentice Hall 1988; ISBN 0-13-110362-8. ANSI C.
 ISO/IEC 9899. The official C:1999 standard, along with defect reports and a rationale.
 Samuel P. Harbison, Guy L. Steele: C: A Reference Manual. This book is excellent as a definitive reference manual, and for those working on C compilers. The book contains a BNF grammar for C.
 4th, Prentice Hall 1994; ISBN 0-13-326224-3.
 5th, Prentice Hall 2002; ISBN 0-13-089592-X.
 Derek M. Jones: The New C Standard: A Cultural and Economic Commentary, Addison-Wesley, ISBN 0-201-70917-1, online material
 Robert Sedgewick: Algorithms in C, Addison-Wesley, ISBN 0-201-31452-5 (Part 1-4) and ISBN 0-201-31663-3 (Part 5)
 William H. Press, Saul A. Teukolsky, William T. Vetterling, Brian P. Flannery: Numerical Recipes in C (The Art of Scientific Computing), ISBN 0-521-43108-5

 Спасылкі 

 C 
 comp.lang.c Frequently Asked Questions
 The Development of the C Language by Dennis M. Ritchie
 Programming in C (document collection at Lysator)
 International Obfuscated C Code Contest
 Programming C at Wikibooks
 The New C Standard: An economic and cultural commentary — an unpublished book about «detailed analysis of the International Standard for the C language»
 ISO/IEC 9899  (pdf)

 C99 
 Open source development using C99 — Is your C code up to standard? by Peter Seebach
 Are you Ready For C99?
 Article «Incompatibilities Between ISO C and ISO C++» by David R. Tribble

 Літаратура 


Катэгорыя:Мовы праграмавання