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

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

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

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

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

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

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

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

### Прыклад «''hello, world''»
Гэта простая праграма ўпершыню зьявілася ў першай рэдакцыі *[[The C Programming Language (кніга)|K&R]]* і стала стандартнай уводнай праграмай у большасьці падручнікаў па праграмаваньні, незалежна ад мовы. Гэтая праграма выводзіць «hello, world» у стандартны вывад, звычайна, у тэрмінал ці на дысплей.


```c
#include <stdio.h>

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


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


```c
#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`.

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

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

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

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

У *C* таксама ёсьць падтрымка шматмерных масіваў. Сэмантычна гэтыя масівы выглядаюць як масівы масіваў, аднак фізычна яны захоўваюцца як адзін аднамерны масіў.

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

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

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

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

### Сынтаксіс
У адрозьненьне ад моваў тыпу *[[Fortran 77]]*, у *C* праграміст можа выкарыстоўваць прагалы і пераводы радкоў там, дзе ён жадае. Камэнтары знаходзяцца або паміж сымбалямі `/*` і `*/`, або (у *C99*) пасьля `//` да канца радку.

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

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

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

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

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

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

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

### Функцыі са зьменнай колькасьцю аргумэнтаў
Яшчэ адной праблемай зьяўляюцца функцыі, якія прымаюць зьменную колькасьць аргумэнтаў. У адрозьненьне ад большасьці функцыяў *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]]*.

Дакладна невядома, адкуль пайшла назва «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&amp;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`.
Прыклад выкліку з папярэднім аб’яўленьнем:

```c
long int SomeFunction();

int CallingFunction()
{
    long int ret;
    ret = SomeFunction();
}
```

Прыклад выкліку без папярэдняга аб’яўленьня:

```c
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 (API)|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) праграма апіраецца на канкрэтныя тыпы зьвестак і парадак байт.

Вы можаце выкарыстоўваць макрас `__STDC__`, каб падзяліць код на часткі *ANSI* і *K&R*.

```c
#if __STDC__
extern int getopt(int,char * const *,const char *);
#else
extern int getopt();
#endif
```


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

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

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

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

Зацікаўленасьць у падтрымцы новых магчымасьцей *С99* розны. Калі *[[GNU Compiler Collection|GCC]]* і некалькі іншых кампілятараў цяпер падтрымліваюць большасьць магчымасьцей C99, кампілятары ад «Майкрасофта» і *[[Borland]]а* — не, і, здаецца, гэтыя кампаніі не зацікаўленыя ў даданьні такой падтрымкі.

## Адносіны з ''C++''
Мова праграмаваньня *[[C++]]* была створана на аснове C. Аднак ня кожная праграма на *C* зьяўляецца вернай праграмай на *C++*. Так як дзьве гэтых мовы разьвіваліся асобна, колькасьць несумяшчальнасьцяў паміж дзьвюма мовамі пастаянна павялічвалася. Апошні перагляд *C*, *C99*, стварыў шмат канфліктуючых характарыстык. Адрозьненьні прыводзяць да складнасьці пісаць праграмы і бібліятэкі, якія можна правільна скампіляваць на *C* і *С++* і зьбіваюць тых, хто праграмуе на абедзьвюх мовах. Акрамя таго, вельмі складна пераносіць рысы адной мовы ў другую.

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

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

## Крыніцы

## Літаратура
* Brian Kernighan, Dennis Ritchie: *[[The C Programming Language (book)|The C Programming Language]]*. Also known as K&amp;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'' Дэніс Рычы
* 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»

### 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


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