Прве верзије оперативног система UNIX су биле, због неопходне брзине извршавања, писане користећи асемблер, али се овај пројекат брзо приближавао свом крају јер је програмски код асемблера изразито велик и тежак за одржавање. Архитектама UNIX-а је био потребан програмски језик који би задржао ефикасност асемблера, али истовремено пружио и комфор већ постојећих виших програмских језика, попут Паскала, Кобола и др. Идеја је била начинити програмски језик који би имао све ово, али чија архитектура и логика не би много одударала од асемблерске. Због тога C-ове контролне структуре наликују на асемблерске, а постоје и изрази који непосредно позивају одговарајуће асемблерске инструкције. Тако, израз x++ позива асемблерску инструкцију INC. Језик је био потпун успех, и користио се за писање свих будућих верзија UNIX-a. У градњи пројекта је учествовао као главни пројектант Денис Ричи, који је уосталом био и један од водећих пројектаната за оперативни систем UNIX.
Програмски језик C је језик опште намене. Иако је развијан као „језик за системско програмирање”, подједнако добро се користи за писање значајних програма у различитим областима. C је такође веома утицао на многе друге популарне програме, посебно на C++, који је оригинално развијан као надградња C-а.
K&R C
Брајан Керниген (енгл.Brian Kernighan) и Денис Ричи (енгл.Dennis Ritchie) су 1978. објавили прво издање књиге Програмски језик C (енгл.The C Programming Language). Ова књига, позната C програмерима као „K&R”, служила је много година као незванична спецификација језика. Верзија С-а коју она описује често се назива „K&R С”. Друго издање ове књиге покрива каснији С89 ANSI C стандард. Амерички национални институт за стандарде (American National Standard Institute (ANSI)) je 1983 формирао комитет, X3J11, да установи стандард за C. Стандард је ратификован 1989 као ANSI X3.159-1989 „Programming Language C”. Ова верзија С-а се често назива ANSI C, Стандардни C, или понекад С89.
ANSI C стандард је са неколико мањих модификација 1990. прихватила „Међународна организација за стандардизацију” (ISO) као ISO/IEC 9899:1990. Ова верзија се понекад назива С90. Због тога се појмови „C89” и „C90” односе на суштински исти језик.
ANSI C и ISO C
Током касних 70-их и 80-их, верзије C-а су имплементиране за различите mainframe рачунаре, минирачунаре и микрорачунаре, укључујући IBM PC, тако да је његова популарност значајно порасла. Године 1999. објављен је стандард ISO/IEC 9899:1999. Овај стандард се често назива С99.
С99 уводи неколико нових особина, од којих су многе већ биле имплементиране у неколико компајлера:
нове функције стандардне библиотеке, као што је snprintf
подршку за коментаре у једној линији који почињу знацима //
побољшана подршка за IEEE 754 рачунање у покретном зарезу — израчунавање реалних бројева може бити урађено и са једноструком тачношћу
C је независан од конкретне архитектуре рачунарског система. Стандард чини све аспекте преносивости експлицитним.
Подршка компајлера
GCC и други C компајлери сада имају подршку за многе нове особине С99 стандарда. Ипак, постоји слабија подршка произвођача као што су Мајкрософт и Борланд који су се углавном концентрисали на С++, који обезбеђује слична функционална побољшања.
Упркос значајној подршци за C99, GCC није потпуно сагласна имплементација стандарда; неколико кључних особина није имплементирано или не раде коректно[1].
Према тврдњама компаније Sun Microsystems, Sun Studio (који је бесплатан за преузимање) сада подржава комплетни С99 стандард[2].
Откривање (детекција) верзије
Стандардни макро__STDC_VERSION__ је дефинисан са вредношћу 199901L која указује да је подржан C99 стандард. Као и __STDC__ макро за C90, __STDC_VERSION__ макро може бити коришћен да се напише код који ће се компајлирати различито за С90 и С99 компајлере. Пример:
-{
#if __STDC_VERSION__ >= 199901L/* "inline" је кључна реч */#else# define inline /* ништа */#endif
Примена
Денис Ричи је C оригинално створио и применио на UNIX оперативном систему на DEC-овом PDP-11. Оперативни систем, C компајлер и сви UNIX-ови апликативни програми су написани на C-у. Језик се након своје примарне употребе проширио на све сфере програмирања. Прославивши се својом брзином, постао је језик избора за многе будуће пројекте, попут различитих верзија Windows-а и UNIX-а, али и у апликативном програмирању за многобројне примене током деценија. И данас се користи у великој мери, првенствено за системско програмирање. Још једна последица широке прихваћености и ефикасности C-а је та да су компајлери, библиотеке и интерпретатори других програмских језика високог нивоа често написани на C-у.
Организација језика
Организационе целине C-а су функције. Почетна тачка извршавања програма је тзв. „главна функција” (енгл.main), а целокупан програм се организује у одређени број других функција, у зависности од потребе. Функције се посматрају одвојеним, изолованим целинама чиме се постиже модуларност кода који је као такав лак за одржавање и даље развијање.
Свака функција има свој именски простор и нема директан приступ променљивама других функција. Посредан приступ променљивама других функција је обезбеђен механизмом показивача и аргумената функције.
Тело функције је ограничено витичастим заградама, како би јој се означили почетак и крај. Унутар функција делови кода могу бити одвојени у функцијске подблокове.
Програм се извршава почев од прве линије кода функције main. Она може позивати друге функције или саму себе (види рекурзија). По завршетку функције main, читав програм се завршава и ослобађа из меморије.
Уз стандардни C се дистрибуирају и стандардне библиотеке. То су датотеке које садрже мноштво корисних алата који се користе у свакодневном раду. Програмер може да укључи једну или више ових библиотека у свој програм, а може и сам да пише библиотеке за своје потребе.
C подржава богат скуп оператора. C има аритметичке операторе, операторе поређења (релацијске), логичке, битске (операторе за манипулисање битовима), операторе доделе, увећања и умањења за један, референцирања и дереференцирања, условни оператор, оператор промене типа (конверзије)…
Оператор Пример Опис/Значење
(typecast) (double)n cast(оп. конверзије); У овом примеру конвертује вредност n у double
() f() Позив функције
[] n[10] Референца низа
-> s->x Приступ одређеном члану структуре или уније
. s.x Приступ одређеном члану структуре или уније
+ [унарни] +x Вредност операнда x
- [унарни] -x Негативна вредност операнда x
* [унарни] *x Индирекција или дереферентни оператор
& [унарни] &x Операција адресирања;Адреса операнда x
++ [префикс] ++x Вредност операнда x после инкрементирања
++ [постфикс] x++ Вредност операнда x пре инкрементирања
-- [префикс] --x Вредност операнда x после декрементирања
-- [постфикс] x-- Вредност операнда x пре декрементирања
sizeof sizeof (t1) Величина операнда у бајтовима објекта типа t1
sizeof sizeof e Величина операнда у бајтовима објекта типа израза e
+ [бинарни] x + y x плус y
- [бинарни] x - y x минус y
* [бинарни] x * y x пута y
/ x / y x подељено са y
% x % y Остатак при дељењу x/y
>> x >> y Померање садржаја операнда x за у битова удесно
<< x << y Померање садржаја операнда x за у битова улево
< x< y Оператор поређења- мање;1 ако је x< y; 0 иначе
> x> y Оператор поређења- веће;1 ако је x> y; 0 иначе
<= x<= y Оператор поређења- мање или једнако;1 ако је x <= y; 0 иначе
>= x >= y Оператор поређења- веће или једнако;1 ако је x >= y; 0 иначе
== x == y Оператор поређења- једнако;1 ако је x једнако y; 0 иначе
!= x != y Оператор поређења- различито;1 ако x није једнако y; 0 иначе
& [битски] x & y Битско AND над битовима операнада x и y
| [битски] x | y Битско OR над битовима операнада x и y
^ [битски] x ^ y Битско XOR (ексклузивно OR) над битовима операнада x и y
~ ~x Комплементирање сваког бита операнда x
&& x && y Логичко AND над x и y ( даје вредност 0 или 1)
|| x || y Логичко OR операнада x и y (даје вредност 0 или 1)
! !x Логичко NOT (логичка негација) операнда x (даје вредност 0 или 1)
?: x ? e1 : e2 Условни оп.; Израз e1 ако x није нула (истинит је); иначе израз e2
= x = y Оператор придруживања; x, након што му је додељен у
+= x += y x плус y (додељено операнду x)
-= x -= y x минус y (додељено операнду x)
*= x *= y x пута y (додељено операнду x)
/= x /= y x подељено са y (додељено операнду x)
%= x %= y Остатак при дељењу x/y (додељено операнду x)
>>= x >>= y x, померен садржај операнда x за у битова удесно(додељено операнду x)
<<= x <<= y x, померен садржај операнда x за у битова улево(додељено операнду x)
&= x &= y x AND y (додељено операнду x)
|= x |= y x OR y (додељено операнду x)
^= x ^= y x XOR y (додељено операнду x)
, e1,e2 e2 (прво се израчуна е1)
Приоритет и асоцијативност оператора:
Категорија Оператор Асоцијативност
Постфикс () [] -> . ++ -- слева надесно
Унарни ! ~ ++ -- + - * & (type) sizeof здесна налево
Аритметички * / % слева надесно
Аритметички + - слева надесно
Померања << >> слева надесно
Релациони < <= > >= слева надесно
Једнакости == != слева надесно
Битски AND & слева надесно
Битски XOR ^ слева надесно
Битски OR | слева надесно
Логички AND && слева надесно
Логички OR || слева надесно
Условни ?: здесна налево
Доделе = += -= *= /= %= >>= <<= &= ^= |= здесна налево
Зарез , слева надесно
Примери
Ово је најмањи могући C програм.
main(){}
Пример који следи је кратак Hello World програм, написан на програмском језику C. Овај пример је постао модел за уводни пример за многе програмске језике. Штампа поруку „Zdravo svete”.
#include<stdio.h>/* укључујемо библиотеку која нам омогућава рад са тастатуром и екраном */intmain(void){/* започињемо функцију main */printf("Zdravo svete\n");/* користећи функцију printf из библиотеке stdio.h, штампамо поруку на екран */return0;/* програм се завршава */}
Сваки C програм се састоји из функција и променљивих. main функција је посебна — сваки програм мора садржати ову функцију јер се програм извршава од почетка main функције.
Прва линија програма #include <stdio.h> је претпроцесорска директива која говори да треба усвојити информацију о стандардној улазно-излазној библиотеци. Коришћењем ознака /* */ смо убацили коментаре у програм. main функција позива printf функцију из стандардне библиотеке, којом се исписује порука на екран.
Како функција printf после завршеног исписа не прелази аутоматски у нови ред, претходни програм можемо написати и на следећи начин:
Можемо приметити да када желимо да пређемо у нови ред, користимо посебни знак \n. На крају, наредбом return 0, main функција враћа вредност нула у позивни програм.
Контролне структуре
Програм се извршава инструкцију по инструкцију, почевши од прве а завршавајући се последњом инструкцијом функције main. Програм се може завршити и раније, користећи кључну реч return или библиотечку функцију exit. Поред императивних инструкција, у програмском језику C постоје и инструкције које контролишу ток програма, тзв. контролне структуре. Оне се деле на гранања и петље.
Гранање
Пошто се програм увек позива у другачијем окружењу (другачије вредности променљивих, другачији систем на коме се програм извршава, моменат извршавања и жеље корисника програма), постоји потреба да се у зависности од одређених услова изврше различите инструкције. Операција гранања представља инструкцију да се под одређеним условима изврши један блок инструкција, под другим условима други итд. Постоје две начина имплементирања оваквог понашања: користећи контролну структуру if...else и користећи контролну структуру switch.
if...else на енглеском језику значи „ако…у супротном”. Следи пример:
if(n<10){printf("n је мање од 10");}else{printf("n је веће или једнако 10");}
Ако је n у тренутку извршавања било мање од 10, биће одштампана само прва порука („n је мање од 10”). У супротном, ако је n било веће или једнако 10, биће одштампана само друга порука.
switch се најчешће користи код вишеструких гранања, тј. у ситуацијама када разликујемо неколико услова под којим се извршавају различите инструкције. Следи пример за ову контролну структуру:
switch(n){case1:printf("n је једнако један");break;case2:printf("n је једнако два");break;default:printf("n није једнако ни један ни два");}
Петље
Петље се користе у ситуацијама када је потребно да се одређени део кода извршава више пута. Саставни делови петље су услов петље и блок петље — док је услов тачан, блок петље се понавља. Када задати услов постане нетачан, понављање се прекида и програм наставља са извршавањем. Постоје и петље код којих се блок кода понавља све док је одређени услов нетачан. Када постане тачан, понављање се прекида. У програмском језику C постоје следеће врсте петљи:
while петља
do...while петља
for петља
Синтакса while петље:
while(izraz)iskaz;
У овој петљи се израчунава израз. Ако вредност израза није нула, исказ се извршава и израз се поново израчунава. Циклус се наставља све док израз не постане нула, а онда се излази из циклуса и извршавање се наставља после исказа.
Синтакса do...while петље:
doiskaz;while(izraz);
Док while и for петље тестирају услов на почетку петље, па се може догодити да се исказ не изврши ниједном, петља do...while тестира услов после проласка кроз тело петље, па се исказ бар једном извршава.
Синтакса for петље:
for(izraz1;izraz2;izraz3)iskaz;
Три компоненте ове петље су изрази. Најчешће су израз1 и израз3 позиви функција или додељивања, а израз2 је релациони израз. Било који од ова три дела може недостајати, чак и сва три. Горња for петља је еквивалентна са:
izraz1;while(izraz2){iskaz;izraz3;}
Следи пример бесконачне петље:
for(;;){...}
Ова петља може бити прекинута нпр. наредбама break или return.
Коришћењем наредбе continue у for петљи прескаче се тренутна итерација петље и прелази се на следећу, док се у while петљи прелази на испитивање услова петље.
Програмски језик C такође садржи наредбу goto која представља наредбу безусловног скока.
Синтакса ове наредбе је:
gotolabela;...labela:naredba;
У овом примеру, labela представља ознаку наредбе на коју се скаче коришћењем наредбе goto. Наредбе које се налазе између labela и goto се прескачу, или ће се потенцијално поново извршити ако се labela у коду налази пре наредбе goto. Иако ова наредба може бити корисна у једноставним задацима и уколико је потребно изаћи из угнеждених петљи, углавном се избегава њено коришћење у пракси. Разлог за то је непрегледност програма написаних помоћу ове команде и њихово тешко преправљање.
У програмском језику C, типови променљивих се деле на следеће:
елементарни типови
низови
типови које дефинише корисник и
показивачке променљиве
Елементарни типови
Елементарни типови променљивих се деле по различитим критеријумима. По врсти података које чувају деле се на следеће:
целобројне (означавају се кључном речју int)
знаковне (char)
реалне (означавају се кључним речима float — реални бројеви у покретном зарезу са једноструком тачношћу и double — реални бројеви у покретном зарезу са двоструком тачношћу)
По количини меморије коју заузимају, а самим тим и по распону вредности, деле се на:
кратке (означавају се кључном речју short) и
дуге (long)
По могућности постојања негативних вредности, деле се на следеће:
означене (кључна реч signed, која се не мора писати јер се подразумева) и
неозначене (кључна реч unsigned)
Кључне речи за означавање типова се могу комбиновати, у зависности од тога којој групи, према различитим критеријумима, тип променљиве припада. signed и unsigned могу бити придружени целобројном и знаковном типу. Тако, једна променљива x може бити целобројна по врсти података, дуга по величини меморије и неозначена по могућности постојања негативних вредности; у том случају би се означавала на следећи начин:
unsignedlongintx;
Реч int се може изоставити у декларацијама овог типа.
Највеће и најмање вредности променљивих одређеног типа зависе од рачунара. Зато постоје системска заглавља <limits.h> и <float.h> и којима су дефинисане граничне вредности.
Низови представљају сложене типове података, и базирају се на осталим типовима променљивих. Низови представљају скупине елемената истог типа обележене једним именом. Низ у C-у је увек једнодимензионалан и, традиционално, фиксне, статичке величине дефинисане у време компилације. (новији С90 стандард такође допушта и низове променљиве дужине.) Ипак, коришћењем malloc функције из стандардне библиотеке, могуће је алоцирати блок меморије у време извршавања програма. За динамичку алокацију меморије може се користити и функција calloc. Меморију алоцирану овим функцијама неопходно је ослободити (деалоцирати) функцијом free.
Сваком елементу низа се може приступити преко његовог целобројног индекса, односно редног броја у низу. Индекси почињу редним бројем 0. Следи пример декларације и коришћења низа целих бројева:
intniz[40];/* декларишемо статички низ величине 40 */intx;x=niz[0];/* променљивој x додељујемо вредност првог елемента низа */
Могу се направити низови променљивих било ког типа, укључујући све типове из набројане листе типова променљивих, али сви елементи у једном низу морају бити истог типа.
Карактеристичност C-a је и његов однос према низовима и показивачима. Низови се у C-у имплементирају преко показивача. Запис x[i] може такође бити коришћен када је x показивач; тада овај запис означава да се приступа од неколико узастопних објеката података на које показује x, где је објекат на који показује x (то је x[0]) први елемент низа.
Формално, x[i] је исто што и *(x+i). Како је компајлеру у време компајлирања познат тип показивача који се користи, адреса на коју показује x+i није адреса на коју показује x увећана за i бајтова, већ увећана за i помножено величином елемента на који показује. Величина ових елемената може бити одређена оператором sizeof.
Следи занимљива демонстрација могућности узајамне замене показивача и низова. Четири доделе су еквивалентне и свака представља тачан C код. Приметимо да последња линија садржи чудан код који има наизглед замењен положај индексне променљиве i и низовне променљиве n.
x[i]=1;*(x+i)=1;*(i+x)=1;i[x]=1;/* чудно, али тачно: -{i[x]}- је еквивалентно -{*(i+x)}-*/
Ипак, морамо направити разлику између низовних и показивачких променљивих. Иако се име низа у већини израза конвертује у показивач (на његов први елемент), овај показивач сам по себи не заузима меморијски простор. Дакле, не може се променити оно на шта низ показује, и немогуће је доделити га низу. (Низови се ипак могу копирати коришћењем функције memcpy, на пример.)
Типови које дефинише корисник
Корисник дефинише своје типове користећи кључну реч struct, и такви типови се називају структурама. Свака структура заправо представља скуп једне или више променљивих чији тип такође мора бити један од три набројана (елементарни, кориснички или показивачки). Структуре имају све могућности као елементарни типови, осим могућности коришћења оператора +, -, *, /, %, &, &&, |, ||, <, >, <<, >>, !, != и ?:, као ни њихових деривата +=, -=, ++, --, *=, >>=, итд. Оператор доделе (=) је могућ, али се додела врши „бајт по бајт”, што у случају динамичке меморије није довољно. Такође, структуре уводе и још два оператора: тачку — оператор за приступ појединачним елементима структуре (.) и „стрелицу” — оператор за приступ елементима структуре преко показивача на структуру (стрелица се заправо састоји од знака минуса и знака веће — ->).
Структуре, попут елементарних типова, могу бити прослеђиване као аргументи функција, могу бити мерене оператором sizeof, бити одредиште показивача итд. Следи пример декларација и коришћења структуре која садржи године, пол, стручну спрему и име и презиме радника:
/* дефинишемо тип Radnik */structRadnik{shortintgodine;/* године се представљају целобројном вредношћу, и због малог опсега вредности их ограничавамо кључном речју short */charpol;/* пол може бити мушки и женски, па ћемо их обележавати знаковима ‘m’ и ‘z’ */charstr_sprema[30],ime_i_prezime[40];/* стручну спрему и име и презиме представљамо низовима знаковних променљивих од по тридесет и четрдесет елемената, тим редом */};/* декларишемо променљиву r типа struct Radnik */structRadnikr;/* додељујемо вредности припадајућим елементима структуре */r.godine=44;r.pol='m';strcpy(r.str_sprema,"Srednja strucna sprema");strcpy(r.ime_i_prezime,"Petar Petrovic");/* исписујемо податке о раднику */printf("%s \"%s\" ima %d godina, i ima strucnu spremu \"%s\"",r.pol=='m'?"Radnik":"Radnica",r.ime_i_prezime,r.godine,r.str_sprema);
Свака променљива декларисана у програмском језику C има своју меморијску адресу. Ту адресу можемо добити унарним оператором &. Оператор & се односи само на објекте у меморији — променљиве и елементе поља. Не може се применити на изразе, константе или register променљиве.
Показивачке променљиве представљају специјалан тип променљивих које имају за основну сврху да чувају вредност адресе других променљивих у програму (тј. показивач је променљива која садржи адресу друге променљиве). Показивачке променљиве се деле према типу променљивих чије адресе чува односно на које показује. Тако, могу постојати целобројни показивачи (односно показивачи на целобројне променљиве), реални, структурни итд. Сваки показивач мора да показује на одређену врсту објекта. Изузетак је „показивач на void”.
Показивачи се у програмском језику C означавају знаком звездице (*), како при декларацији, тако и при приступу вредностима на које показују. Унарни оператор * је индирекција или дереферентни оператор. Када се примени на показивач, он приступа објекту на који показивач указује. Следи пример декларације целобројне вредности n, показивачке променљиве np која показује на n и коришћења:
intn=2;/* декларација целобројне променљиве n са вредношћу 2 */int*np;/* декларација показивача np који може да показује само на целобројне променљиве */np=&n;/* додела адресе променљиве n показивачу np */printf("%d",*np);/* штампање вредности променљиве n користећи показивач np */
Управљање меморијом
Једна од најбитнијих функција програмског језика је да обезбеди могућност руковања меморијом и објектима који су сачувани у меморији. C обезбеђује три различита начина да се алоцира (додели, резервише) меморија за објекте:
Статичка алокација меморије
Аутоматска алокација меморије
Динамичка алокација меморије
Сваки од ова три прилаза представља одговарајуће решење у различитим ситуацијама и има многобројне примене.
Када је то могуће, аутоматска или статичка алокација су пожељније јер је управљање меморијом остављено компајлеру, што ослобађа програмера од могућности да направи грешку при алоцирању и ослобађању меморије. Ипак, многе структуре података се увећавају током извршавања програма, и како статичка алокација (и аутоматска алокација у С89 и С90 стандардима) треба да имају фиксирану величину у време компајлирања, постоје многе ситуације у којима је неопходна динамичка алокација.
Кључне речи
Један број речи, тзв. кључне речи, имају посебно значење у језику
и зато се не могу користити као идентификатори. Кључне речи према C90
стандарду су:
auto extern sizeof
break float static
case for struct
char goto switch
const if typedef
continue int union
default long unsigned
do register void
double return volatile
else short while
enum signed
У одређеним верзијама језика постоје и следеће (нестандардне) кључне речи:
ada far near
asm fortran pascal
entry huge
Имена која почињу знаком _ треба избегавати јер су резервисана за променљиве и функције из стандардне библиотеке.
Библиотеке
Програмски језик C користи библиотеке као основни начин проширивања своје функционалности. У C-у, библиотека је скуп функција. Свака библиотека обично има заглавље (header file), које садржи прототипове функција, садржаних у библиотеци, које може да користи програм, и декларацију специјалних типова података и макроа које користе ове функције. Да би програм користио библиотеку, мора да укључи заглавље библиотеке, и библиотека мора бити повезана (linked) са програмом.
Најпознатија C библиотека је C стандардна библиотека, која је одређена ISO и ANSI C стандардима и укључена је у сваку C имплементацију. Функције, типови и макрои стандардне библиотеке су декларисани у стандардним заглављима:
Заглављу можемо приступити на следећи начин:
#include<заглавље>
Још један познат скуп C библиотечних функција је онај намењен програмима које раде под UNIX-ом, посебно функција које обезбеђују интерфејс са кернелом. Ове функције су описане у различитим стандардима као што су POSIX и Single UNIX Specification.
Како постоји велики број програма написаних у C-у, постоји велики број доступних библиотека. Библиотеке су често написане на C-у зато што C компајлери генеришу ефикасан објектни код.
Утицај
Синтакса C-a је имала велики утицај на велики број програмских језика који су настајали после њега. Тако, данас постоје следећи програмски језици који су, посредно или непосредно, преузели синтаксу од C-а:
Често се због тога каже да је C „победио” у програмерском свету, при чему се мисли на Паскал, Кобол и друге како процедуралне тако и непроцедуралне програмске језике у време његовог настанка. Овакав епитет C дугује својој архитектури која омогућава велику брзину програма, али и великим новинама које је увео у свет програмирања што се тиче краткоће кода (оператори ++, --, витичасте заграде за ограничавање блока за разлику од Паскалових кључних речи BEGIN и END итд.), интензивне употребе показивача итд.