Programski jezik C
![]() Prográmski jêzik C ali kar C (izgovorjava [cé] ali po izvirniku [sí]) je nizkonivojski imperativni standardizirani računalniški programski jezik tretje generacije (3GL) za splošno rabo. Podpira strukturalno programiranje, leksično območje spremenljivk in rekurzijo, statični sistem tipov pa preprečuje mnogo nenameravanih operacij. Zasnova jezika C omogoča konstrukte, ki se učinkovito preslikujejo v tipične strojne ukaze. Po zasnovi gradniki C jasno odražajo zmogljivosti ciljnih procesorjev. Zaradi tega se je C začel rabiti v aplikacijah, ki so bile prej razvite v zbirnem jeziku, še posebej v sistemski programski opremi, kot je npr. računalniški operacijski sistem Unix.[6] Našel je trajno uporabo v kodi operacijskih sistemov (zlasti v jedrih[7]), gonilnikih naprav in protokolskih skladih, vendar se njegova uporaba v aplikacijski programski opremi zmanjšuje.[8] C se pogosto uporablja v računalniških arhitekturah v različnih uporabniških programskih opremah od največjih superračunalnikov do najmanjših mikrokrmilnikov in vgradnih sistemov. Programski jezik C je prvotno razvil Dennis Ritchie med letoma 1969 in 1973 v AT&T Bellovih laboratorijih.[9][10][11] Ritchie in Ken Thompson sta najprej razvila prevajalnik za operacijski sistem Unix, ki je bil v osnovi napisan v zbirnem jeziku. Unixovo jedro je bilo tako prvo napisano v drugem jeziku in ne v zbirnem. Kasneje se je C hitro pojavil tudi na drugih operacijskih sistemih. V 1980-ih je C postopoma pridobival na priljubljenosti. Postal je eden najbolj razširjenih programskih jezikov[12][13] s prevajalniki C, ki so na voljo za praktično vse sodobne računalniške arhitekture in operacijske sisteme. Pred uradnim standardom za C se je mnogo uporabnikov in izvršiteljev opiralo na neuradno specifikacijo, opisano v knjigi Programski jezik C Briana Wilsona Kernighana in Ritchieja.[14][15] Ta različica je v splošnem znana kot »K&R« C. Leta 1989 je Ameriški državni inštitut za standarde (ANSI) objavil standard za C (v splošnem imenovan »ANSI C« ali »C89«). Naslednje leto sta to specifikacijo potrdili Mednarodna organizacija za standardizacijo (ISO) in Mednarodna komisija za elektrotehniko (IEC) kot mednarodni standard (v splošnem imenovan »C90«). ISO in IEC sta kasneje izdali razširitev standarda za podporo internacionalizacije leta 1995 in popravljeni standard (znan kot »C99«) leta 1999. Ta standard je do sedaj najbolj razširjen. Trenutna različica standarda (sedaj imenovana »C23«) je ISO kot ISO/IEC 9899:2014 potrdila 31. oktobra 2024.[16] ZasnovaC je imperativni proceduralni jezik. Izdelan je bil za prevajanje na relativno preprostem prevajalniku in da bi zagotavljal nizkonivojski dostop do pomnilnika, jezikovne konstrukte, ki bi se učinkovito preslikali v tipične strojne ukaze in, da bi potreboval najmanjšo podporo izvajanja. Zaradi tega se je C začel rabiti v mnogih aplikacijah, ki so bile prej razvite z zbirnem jeziku, še posebej v sistemskem programiranju. Navkljub svojim nizkonivojskim zmožnostim se je jezik C razvil v smislu boljšega programiranja na mnogih platformah. Program zapisan v C, ki je v skladu s standardom in napisan prenosljivo, se lahko prevede za zelo širok nabor računalniških platform in operacijskih sistemov z zelo malo spremembami v izvorni kodi. Tako je C postal razpoložljiv na mnogih platformah od vgrajenih mikrokrmilnikov do superračunalnikov. C velja za učinkovit jezik in je primeren za sistemska opravila, ni pa najprimernejši za učenje programiranja, čeprav se pogosto pojavlja v izobraževanju. Je tudi eden od najbolj razširjenih programskih jezikov, skupaj z javo, C++ ali PHP, od leta 2008 pa mu priljubljenost celo rahlo narašča.[12][13] Od leta 2000 se C stalno uvršča med prve štiri jezike v indeksu TIOBE, ki meri priljubljenost programskih jezikov.[17] Obstaja zelo malo arhitektur in operacijskih sistemov za katere ni na voljo prevajalnika za C. C se veliko rabi tudi za razvoj prenosljive uporabniške programske opreme.[6] Pregled in osnovne značilnosti jezikaKot večina imperativnih jezikov v tradiciji ALGOLa je C zmožen strukturalnega programiranja in omogoča leksično območje spremenljivk ter rekurzijo, statični sistem tipov pa preprečuje mnogo nenameravanih operacij. C je dokaj skop programski jezik, ki deluje blizu strojne opreme, in je za razliko od večine programskih jezikov bolj podoben zbirniku. Včasih ga imenujejo »prenosljivi zbirnik«, kar tudi označuje njegovo pomembno razliko od zbirniškega jezika. Izvorno kodo, napisano v C, se da prevesti in pognati na skoraj vsakem stroju. Tega ne zmore skoraj noben obstoječ programski jezik in tudi kodo, zapisano v zbirniku, se lahko požene le na določenih vrstah strojev. C po navadi imenujejo nizkonivojski ali srednjenivojski jezik, kar označuje kako blizu strojne opreme lahko deluje. V C-ju je vsa izvršna koda vsebovana znotraj podprogramov, imenovanih »funkcije«, čeprav ne v strogem pomenu funkcionalnega programiranja. Parametri funkcij se prenašajo po vrednosti. Polja se prenašajo kot kazalci, to je kot naslov prvega elementa v polju. Prenos po sklicu se v C simulira z eksplicitnim prenašanjem vrednosti kazalcev na objekt, na katerega se sklicuje. Izvorno besedilo programa v C je v prostem formatu in za končnik stavkov rabi podpičje ( C so naredili zaradi enega samega pomembnega namena, kar ni slučajnost, da bi bilo moč pisati velike programe z manj napakami v proceduralnem programiranju in, da pisec programov ne bi nosil bremena tvorjenja prevajalnika za C, ki ga otežujejo zapleteni gradniki jezika. V tem smislu ima C naslednje pomembne značilnosti:
C ne vsebuje nekaterih gradnikov, ki so sestavni deli novejših, sodobnejših visokonivojskih jezikov, kot na primer objektne usmerjenosti ali samodejnega čiščenja pomnilnika, tako da ima ročno upravljanje pomnilnika. Ti gradniki se lahko velikokrat izvedejo ali emulirajo s pomočjo zunanjih knjižnic (na primer s knjižnico GLib Object System ali z Boemovim čiščenjem pomnilnika). RazvojZgodnji razvoj![]()
![]() Izvor jezika C je tesno povezan z razvojem operacijskega sistema Unix, ki so ga izvirno razvili v zbirnem jeziku na miniračunalniku DEC PDP-7 Ritchie, Thompson, Joe Ossana, Rudd Canaday in Doug McIlroy v AT&T Bellovih laboratorijih, in vključili več zamisli svojih kolegov.[20] V začetku Thompson niti ni programiral na samem PDP-7, ampak je rabil množico makrojev za zbirni jezik GEMAP na 32-bitnem osrednjem stroju General Electric GE-635. Nato je poprocesor tvoril papirni trak, ki ga je PDP-7 lahko prebiral.[10] Kmalu je Unix s preprostim jedrom, zbirnim jezikom, preprosto lupino (tolmačem ukazov) in z nekaterimi pripomočki (kot npr. Unixovi ukazi BPisanje v zbirnem jeziku novonastalega Unixa je bilo nerodno. Za pisanje programskih struktur se je porabilo več časa, kodo je bilo težje razhroščevati in jo razumeti.[21] Thomson je želel imeti prednosti visokonivojskega jezika vendar ne v smislu operacijskega sistema Multics, napisanega v PL/I in zbirnem jeziku. Bil je vodja neformalne skupine v AT&T, ki je začela pregledovati možnosti drugih operacijskih sistemov in višjenivojskih jezikov. Najprej je s pomočjo TMG leta 1970 neuspešno hotel uporabiti Fortran,[22] nato pa je ustvaril jezik B s poenostavitvijo raziskovalnega sistemskega jezika BCPL, ki ga je leta 1967 razvil Martin Richards, tako da je lahko njegov tolmač šel v 8 kB 18-bitni besedni pomnilnik računalnika PDP-7. Uradni opis jezika BCPL tedaj ni bil na voljo.[23] Thompson je spremenil njegovo skladnjo z manj besedami in podobno poenostavljenemu Algolu, znanemu kot SMALGOL.[24] Jezik je imenoval B in ga opisal kot »semantika BCPL z veliko skladnje SMALGOL.«[10][24] Tako kot BCPL je imel B zagonski prevajalnik za lažji prenos na nove stroje.[24] Vendar je bilo le nekaj pripomočkov na koncu napisanih v B, ker je bil prepočasen in ni mogel izkoristiti funkcij PDP-11, kot je na primer bajtno naslovljivost. B se je izkazal za zelo počasnega in nezmožnega za sistemsko programiranje v samem Unixu. Tako kot BCPL je bil brez tipov. Pri novem zmogljivejšem računalniku DEC PDP-11 je bil jezik brez tipov neizvršljiv. Njegov procesor je podpiral podatkovne tipe različnih velikosti in z jezikom B tega ni bilo moč izraziti. Problem je bilo tudi izvajanje. Thompson in Ritchie sta se odločila prenesti operacijski sistem na PDP-11. Prva različica PDP-11 je imela 16 kB pomnilnika za operacijski sistem in 8 kB za uporabniške programe.[20] Tudi njegov operacijski sistem je bil napisan v zbirnem jeziku.[10] Želela sta napisati operacijski sistem v jeziku B. Novi B in prva izdaja CNajprej je zaradi nezmožnosti rabe nekaterih prednosti računalnika PDP-11, še posebej bajtno naslovljivost, v letu 1971 nastal »novi B« (NB), ki je rešil te probleme.[19][24] Pomemben dodatek je bil znakovni podatkovni tip. Thompson je začel uporabljati NB za pisanje Unixovega jedra in njegove zahteve so oblikovale smer razvoja jezika.[24][25] Do leta 1972 so bili jeziku NB dodani bogatejši tipi: NB je imel polja tipov Prevajalnik C in nekateri pripomočki, izdelani z njim, so bili vključeni v različico Version 2 Unix, ki je znana tudi kot Research Unix.[26] Strukture in ponovni zapis Unixovega jedraZačetni razvoj jezika C je po Ritchiejevih besedah potekal v AT&T Bellovih laboratorijih med letoma 1969 in 1973.[10] Najbolj kreativno obdobje je bilo leta 1972. Tedaj je bila večina Unixa ponovno napisana v C-ju.[27] Do leta 1973 z dodatkom podatkovnega tipa zapisov Predprocesor je bil predstavljen okoli leta 1973 na poziv Alana Snyderja in tudi kot priznanje uporabnosti mehanizmov za vključitev datotek, ki so na voljo v BCPL in PL/I. Njegova izvirna različica je vsebovala samo vključene datoteke in preproste zamenjave znakovnih nizov (stringov): makrov Tako je bilo Unixovo jedro prvo jedro kakšnega operacijskega sistema napisano v drugem jeziku od zbirnega. Pred tem sta bila na primer še operacijska sistema Multics (napisan v PL/I) in MCP (Master Control Program) za sistem Burroughs B5000 (napisan v ALGOLu) leta 1961. Leta 1977 sta Ritchie in Stephen Curtis Johnson dodatno spremenila jezik C za pospešitev prenosljivosti operacijskega sistema Unix. Johnsonov Portable C Compiler (PCC) je služil kot osnova za več izvedb C-ja na novih platformah.[25] K&R C![]() Leta 1978 sta Kernighan in Ritchie objavila prvo izdajo knjige Programski jezik C (The C Programming Language).[29] Ta knjiga, pri programerjih v C-ju znana kot »bela knjiga« (white book), oziroma »K&R«,[10] je bila mnogo let neformalna specifikacija jezika. Različica jezika C, ki ga knjiga opisuje, se navadno imenuje »K&R C«. Ker je bila izdana leta 1978, se včasih imenuje tudi C78.[30] Druga izdaja knjige[31] pokriva kasnejši standard ANSI C, opisan spodaj. K&R je uvedla nekaj jezikovnih gradnikov:
Tudi po objavi standarda ANSI leta 1989, je K&R C še vedno mnogo let veljala za »najmanjši skupni imenovalec« na katerega so se programerji v C-ju omejili, kadar je bila zaželena največja možna prenosljivost, saj je bilo v rabi še vedno več starejših prevajalnikov, skrbno napisana koda v K&R C pa je bila lahko tudi veljavni standard za C. V zgodnejših različicah C-ja je bilo treba pred opredelitvijo (definicijo) uvesti le funkcije, ki so vračale vrednosti razen tipov long neka_funkcija(); /* To je označitev funkcije, tako da */
/* prevajalnik lahko pozna ime in tip */
/* vrednosti, ki jo funkcija vrača */
/* int */ druga_funkcija(); /* Druga označitev funkcije. Ker je */
/* to zgodnja različica C, je tukaj */
/* implicitni tip 'int'. Komentar kaže,*/
/* kje bi bil v kasnejših različicah */
/* potreben izrecni določilnik tipa */
/* 'int'. */
/* int */ klicna_funkcija() /* To je opredelitev funkcije, ki */
/* vključuje njeno telo s kodo med */
/* { zavitima oklepajema }. Ker ni */
/* določen tip vračane vrednosti, */
/* funkcija implicitno vrača tip 'int' */
/* v tej zgodnji različici C. */
{
long test1;
register /* int */ test2; /* Tukaj navedba tipa 'int' ni */
/* potrebna. Določilnik tipa 'int' v */
/* komentarju bi bil potreben v */
/* kasnejših različicah C. Ključna */
/* beseda 'register' nakazuje */
/* prevajalniku, da se naj ta */
/* spremenljivka idealno shrani v */
/* register v nasprotju znotraj okvirja*/
/* sklada. */
test1 = neka_funkcija();
if (test1 > 0) {
test2 = 0;
}
else {
test2 = druga_funkcija();
}
return test2;
}
Določilnike (specifikatorje) tipa Ker označitev funkcij v K&R C ni vsebovala nobenega podatka o parametrih funkcije, se njihovo preverjanje ni izvedlo, čeprav je nekaj prevajalnikov javljalo opozorilno sporočilo, če je bila lokalna funkcija klicana z napačnim številom parametrov, ali, če je več klicev zunanje funkcije rabilo različno število tipov parametrov. Razvili so več ločenih orodij, kot na primer Unixov pripomoček Po objavi K&R C so k jeziku dodali več gradnikov, ki so jih podpirali prevajalniki, na primer od AT&T (še posebej PCC[32]) in od nekaterih drugih ponudnikov. Med njimi so:
Veliko število razširitev, pomanjkanje dogovora o standardni knjižnici, priljubljenost jezika in dejstvo, da tudi na operacijskem sistemu Unix prevajalniki niso dosledno izvrševali specifikacije K&R, je vodilo do potrebe za standardizacijo.[33] ANSI C in ISO CV poznih 1970-ih in 1980-ih so izvedli različice C-ja za širok razpon osrednjih računalnikov, miniračunalnikov in mikroračunalnikov, vključno z osebnim računalnikom IBM PC, saj se je tedaj njegova priljubljenost zelo povečala. Leta 1983 je Ameriški državni inštitut za standarde (ANSI) ustanovil odbor X3J11 za ustanovitev standardne specifikacije jezika C. X3J11 je osnoval standard za C na Unixovi izvedbi; vendar so neprenosljivi del Unixove knjižnice C predali delovni skupini 1003 IEEE, ki je postala osnova za standard POSIX leta 1988. Leta 1989 so standard C potrdili kot ANSI X3.159-1989 »Programming Language C«. Ta različica jezika se po navadi imenuje ANSI C, Standard C ali včasih C89. Leta 1990 je standard ANSI C (z oblikovnimi spremembami) sprejela Mednarodna organizacija za standardizacijo (ISO) kot ISO/IEC 9899:1990, kar se včasih imenuje C90. Tako se izraza »C89« in »C90« nanašata na isto različico programskega jezika. Kakor druge nacionalne ustanove za standarde ANSI ni več razvijal standarda C neodvisno, ampak ga je odložil k mednarodnemu standardu C, ki ga vzdržuje delovna skupina ISO/IEC JTC1/SC22/WG14. Nacionalna usvojitev posodobitve mednarodnega standarda se po navadi izvede še v letu publikacije ISO. Eden od ciljev procesa standardizacije jezika C je bilo tvorjenje supermnožice K&R C, ki bi vključevala več predhodno uvedenih neuradnih gradnikov. Standardizacijski odbor je vključil tudi več dodatnih gradnikov, kot so npr. prototipi funkcij (izposojeno od C++), kazalci C89 podpirajo trenutni prevajalniki za C, večina kode v C-ju, ki je zapisana sedaj, temelji na njem. Vsak program, zapisan le v ANSI C in brez privzetkov odvisne strojne opreme, se bo izvajal pravilno na katerikoli platformi v skladu z izvedbo C-ja v mejah svojih virov. Brez takšnih previdnosti se lahko programi prevedejo le na določenih platformah ali z določenim prevajalnikom zaradi na primer rabe nestandardnih knjižnic, kot so knjižnice grafičnih uporabniških vmesnikov (GUI), ali oslonitev na računalniške ali specifične platformske atribute, kot je točna velikost podatkovnih tipov in ureditev bajtov (endianost, endianness). V primerih, ko mora biti koda prevedljiva tako v skladu s standardom ali s prevalniki na osnovi K&R C, se lahko rabi makro Po standardizacijskem procesu ANSI/ISO je specifikacija jezika C več let ostala statična. Leta 1995 so k standardu C iz leta 1990 dodali Normative Amendment 1 (ISO/IEC 9899/AMD1:1995, neformalno znan kot C95). Z njim so popravili nekaj podrobnosti in dodali obsežnejšo podporo mednarodnega nabora znakov.[34] C99Standard C so v poznih 1990-ih na novo popravili, kar je leta 1999 vodilo do objave standarda ISO/IEC 9899:1999, ki je v splošnem znan kot »C99«. Od tedaj so ga s tehniškimi popravki dopolnili trikrat.[35] C99 je uvedel več novih gradnikov. Na primer vrinjene funkcije (inline functions), nove podatkovne tipe (na primer C 99 je večinoma nazaj združljiv s C90, vendar je na nekaterih mestih strožji. Še posebej, označitev, ki je brez določilnika tipa, implicitno privzeto ni več tipa Poleg tega standard C99 zahteva podporo za označevalnike (identifikatorje) Unicode v obliki ubežnih znakov (escape characters) (na primer C11Leta 2007 so se začela dela na novi različici standarda C, ANSI C11 ali ISO/IEC 9899:2011, do objave 8. decembra 2011 neformalno imenovane »C1X«. Standardizacijski odbor C je sprejel smernice za omejitev usvojitve novih gradnikov, ki jih niso preskusili z obstoječimi izvedbami. V standardu C11 se je pojavilo več novih gradnikov jezika C in knjižnic, na primer: makroji rodovnih tipov, anonimne strukture, izboljšana podpora kodirnega standarda Unicode, atomske operacije, mnogonitnost in funkcije s preverjanjem mej. Neketeri deli obstoječe knjižnice C99 so postali izbirni, izboljšana je bila tudi združljivost s C++. Standardni makro C17C17 je neuradno ime za standard programskega jezika C, objavljen junija 2018 kot ISO/IEC 9899:2018. Ne uvaja novih jezikovnih gradnikov, le tehnične popravke in pojasnila za napake v C11. Standardni makro C23C23 je neformalno ime za trenutno večjo standardno različico jezika C. Med celotnim razvojem je bila neuradno znana kot »C2X«. C23 je bila objavljena 31. oktobra 2024 kot ISO/IEC 9899:2024.[16] Standardni makro C2YC2Y je neuradno ime za naslednjo večjo standardno različico jezika C po C23 (C2X), ki bo verjetno objavljena v kasnejših 2020-ih. Zato nosi ime '2' in »C2Y«. Delovna skupina ISO/IEC JTC1/SC22/WG14 je 21. februarja 2024 objavila zgodnji delovni osnutek kot N3220.[38] Vgradni CProgramiranje z vgradnim C-jem (embedded C) zgodovinsko zahteva nestandardne razširitve k jeziku C, da se lahko podpirajo C-ju tuji gradniki, kot so npr.: aritmetika s fiksno vejico, mnogokratne različne pomnilniške vrstice in osnovne vhodnoizhodne operacije. Leta 2008 je standardizacijski pododbor SC 22 objavil tehniško poročilo z razširitvijo jezika C,[39] ki se je nanašala na probleme med razširitvami jezika C za različne vgradne sisteme, in tako zagotovila splošni standard za vse ustrezne izvedbe. Vključuje več gradnikov, ki v normalnem C niso na voljo: aritmetika s fiksno vejico, prostori z imenovanimi naslovi in osnovno vhodnoizhodno naslavljanje strojne opreme. Vgradni C rabi večino skladnje in semantike standardnega C-ja: funkcija SkladnjaC ima formalno slovnico, ki jo določa standard C.[40] Z razliko od nekaterih drugih jezikov, kot je npr. FORTRAN 77, je izvorna koda C-ja proste oblike, kjer se znaki za prazni prostor lahko rabijo poljubno, koda pa ni odvisna od stolpčnih ali besedilno-vrstičnih omejitev. Konci vrstic v C na splošno niso pomembni, meje vrstic pa so drugače med predprocesorsko fazo pomembne. KomentarjiKomentarji so lahko med razmejilnima znakoma /* ... */
/*
...
*/
/* ... // ... */
// ...
/*
... // ...
*/
so v redu, vgnezdeni enovrstično pa npr. ne: /* /* ... */ ... */
ali vgnezdeni mnogovrstično: /*
/* ...
*/ ...
*/
saj drugo zaporedje IzraziDatoteke s C-jevsko izvorno kodo vsebujejo označitev (deklaracijo) in opredelitev (definicijo) funkcij. Opredelitve funkcij po vrsti vsebujejo označitve in stavke. Označitve opredeljujejo nove podatkovne tipe z rezerviranimi besedami, kot so: C kot imperativni jezik za označitev dejanj rabi stavke. Najbolj razširjeni stavek je izrazni stavek, ki vsebuje izraz, kateremu je treba določiti vrednost, izrazu pa sledi podpičje. Zaradi še večje preglednosti se podpičje velikokrat razmakne od označitvenih/ukaznih besed z dodatnim presledkom. Na primer: int a; | int a ;
for (i=0; i<max; i++) | for (i = 0 ; i < max ; i++)
Kot stranski pojav določitve vrednosti se funkcije lahko kličejo in spremenljivkam se lahko priredijo nove vrednosti. Za spreminjanje normalne zaporedne izvršitve stavkov ima C več stavkov za nadzor pretoka, ki jih označujejo rezervirane besede. Strukturalno programiranje je podprto s pogojno izvršitvijo Izrazi lahko rabijo različne vgrajene operatorje in lahko vsebujejo klice funkcij. Vrstni red po katerem se določijo vrednosti parametrom funkcij in operandom večine operatorjev ni določen. Določevanje vrednost je lahko tudi vloženo vmes. Vendar se bodo vsi stranski pojavi (vključno s pomnilnikom do spremenljivk) pojavili pred naslednjo »zaporedno točko«. Zaporedne točke vsebujejo zaključek vsakega izraznega stavka ter vstop in vračanje iz vsakega klica funkcije. Zaporedne točke se pojavijo tudi med določevanjem vrednosti izrazov, ki vsebujejo določene operatorje ( Kernighan in Ritchie v uvodu knjige Programski jezik C pravita: »Kakor drugi jeziki ima C svoje hibe. Nekateri od operatorjev imajo napačno prednost; nekateri deli skladnje bi lahko bili boljši.«[29]:3 Standard C ni poskušal popraviti veliko od teh hib zaradi vpliva takšnih sprememb na že obstoječo programsko opremo. Nabor znakovOsnovni nabor znakov izvorne kode jezika C vsebuje:
Nova vrstica označuje konec besedilne vrstice. Ni treba da odgovarja dejanskemu posameznemu znaku, čeprav je v C zaradi prikladnosti to posamezen znak. Lahko se rabijo tudi dodatni mnogobitno zakodirani znaki v prikazu literalov znakovnih nizov, vendar niso povsem prenosljivi. Od standarda C99 se lahko prenosljivo vgradijo mnogonacionalni znaki kodirnega standarda Unicode v besedilo izvorne kode C s pomočjo kodiranj Osnovni izvršni nabor znakov C obsega enake znake, skupaj s prikazom opozorila (alert), povratnega znaka (backspace) in pomika na začetek vrstice (carriage return). Podpora izvajalnemu času (runtime) za razširjene nabore znakov se je z vsako različico standarda za C povečevala. Rezervirane besedeNaslednje besede (keywords; reserved words) so v jeziku C rezervirane in imajo strog pomen kot posamezni znaki (tokens).[41][42] C89 ima 32 rezerviranih besed. Ne smejo se na novo opredeliti ali se rabiti kot označevalniki v drugih kontekstih: Besede so občutljive na male ali velike črke. C99 je dodal pet rezerviranih besed (‡ označuje drugačen črkovalni vzdevek za rezervirano besedo C23): C11 je dodal še sedem razerviranih besed (‡ označuje drugačen črkovalni vzdevek za rezervirano besedo C23):
C23 je dodal še 15 rezerviranih besed:
Večina nedavno rezerviranih besed se začne s podčrtajem, ki mu sledi velika začetnica, ker so bili označevalniki te oblike predhodno rezervirani s standardom C za uporabo samo v izvedbah. Ker obstoječa izvorna koda programa ne bi smela uporabljati teh označevalnikov, na to ne bi vplivalo, ko bi izvedbe C začele podpirati te razširitve programskega jezika. Nekatere standardne zaglavne datoteke opredeljujejo bolj priročne sinonime za podčrtane označevalnike. Nekatere od teh besed so bile dodane kot rezervirane besede z običajnim črkovanjem v C23, ustrezni makri pa so bili odstranjeni. Na začetku je imel C manj rezerviranih besed, na primer 29, sedaj pa jih pozna 63. Beseda OperatorjiC podpira bogat nabor operatorjev, ki so simboli znotraj izraza za določevanje potrebnih opravil med njegovim izvajanjem. Njegovi operatorji so razdeljeni na osemnajst kategorij. C ima operatorje za:
C rabi operator Operatorska prednost v C ni vedno intuitivna, saj se na primer operator Zgradba, oblika programa in slog v COsnovni gradnik programa v C je funkcija.[46] Vsak program v C je zbirka ene ali več funkcij. Funkcije sestavljajo označitve spremenljivk in stavki, ali zapleteni ukazi, obkrožata pa jih zavita oklepaja ( Zgleda programovProgram Pozdravljen, svet![]() Izvirni program Pozdravljen, svet (»Hello, World!«) se je pojavil v prvi izdaji knjige Programski jezik C in je postal model za uvodni program v večini učbenikov programskih jezikov:[29]:6 main()
{
printf("hello, world\n");
}
Spodnji zgled izpiše znakovni niz »Pozdravljen, svet!« na standardni izhod s funkcijo #include <stdio.h>
int main(void)
{
printf("Pozdravljen, svet!\r\n");
return 0;
}
Sledi analiza programa po vrsticah: #include <stdio.h>
Prva vrstica v programu je predprocesorski ukaz (navodilo, direktiva) int main(void)
V naslednji vrstici se opredeli funkcija z imenom int main() { /* ... */ }
int main(void) { /* ... */ }
int main(int argc, char *argv[]) { /* ... */ }
int main(int argc, char **argv) { /* ... */ }
oziroma prototipno: int main();
int main(void);
int main(int, char *[]);
int main(int, char **);
Prvi dve opredelitvi sta enakovredni (in združljivi s C++). {
Odprti zaviti oklepaj pomeni začetek opredelitve funkcije printf("Pozdravljen, svet!\r\n");
V tej vrstici se kliče funkcija return 0;
Vrstica zaključi izvajanje funkcije }
Zaprti zaviti oklepaj pomeni zaključek opredelitve funkcije Najmanjši programKatera opredelitev funkcije Standard C opredeljuje vrnitveni vrednosti Najmanjši pravilen program v C vsebuje prazno funkcijo int main(void){}
Lahko se zapiše tudi v več vrsticah: int main(void)
{ /* začetek telesa funkcije v prvem */
} /* stolpcu vrstice */
ali redkeje sicer v skladu z dobrim slogom: int /* podatkovni tip vrnjene vrednosti */
main(void) /* funkcije. Ime funkcije v prvem */
{ /* stolpcu vrstice. Začetek telesa */
} /* funkcije v prvem stolpcu vrstice */
Sploh pa je slaba praksa pisati neporavnano, kot npr: int
main
/*
**
* * * **/ (
void
)
{ }
ali: int
main/*
** * * *
**/(void){ }
oziroma poravnano brez reda: int main /*
** * *
***/ ( void
) { }
Preveden program bo sicer deloval, izvorna koda pa je že v preprostem zgledu nepregledna. Seveda je treba rezervirane besede, imena spremenljivk in funkcij pisati brez presledkov. Okrogla oklepaja (), ki sledita imenu funkcije, morata biti vključena, saj na ta način C razlikuje funkcije od navadnih spremenljivk. Načeloma pri funkcijah za imenom funkcije in odprtim oklepajem ni presledka, npr. Ker nista navedena int main(void) { return 0; }
Funkcija Nekatere izvedbe niso izvedljive, po navadi zaradi tega, ker niso mišljene za rabo z operacijskim sistemom. Takšne izvedbe se v standardu C imenujejo prostostoječe (free-standing). V prostostoječi izvedbi ni podrobnega opisa kako naj obravnava izvajanje programa. V programu še posebej ni treba opredeliti funkcije Druge funkcije se lahko imenujejo poljubno v skladu s skladnjo jezika. Lahko jih napiše programer sam ali pa se rabijo obstoječe iz knjižnic. Vmesniki za knjižniške funkcije so običajno navedeni z vključitvijo zaglavnih datotek s predprocesorskim ukazom Funkcija lahko vrne vrednost klicatelju – po navadi drugi funkciji C-ja, ali gostiteljevemu okolju za funkcijo Podatkovni tipi![]() C ima statični šibki sistem tipov, ki je deloma podoben sistemom tipov potomcev ALGOLa, kot je npr. paskal, vendar C sam ni potomec Algola.[50] V C obstajajo vgrajeni tipi za cela števila različnih velikosti, tako predznačena in nepredznačena, števila s plavajočo vejico (npr. racionalna števila), znake ( C se velikokrat rabi v programiranju nizkonivojskih sistemov, kjer morda ni potrebe za sistemom tipov. Prevajalnik poskuša zagotoviti pravilnost tipov večine izrazov, vendar lahko programer zaobide preverbe na več načinov, ali s pomočjo tipske opustitve (type cast), kjer ekscplicitno pretvori vrednost iz enega tipa v drugega, ali s pomočjo kazalcev ali unij, kjer se osnovni biti podatkovnega objekta na novo tolmačijo na kakšen drug način. Za nekatere je označitvena skladnja v C neintuitivna, še posebej kazalci na funkcije. (Ritchiejeva zamisel je bila, da se določilniki označijo v kontekstu njihove rabe: »označitev zrcali rabo.«)[31]:122 C-jevske običajne aritmetične pretvorbe dovoljujejo tvorjenje učinkovite kode, vendar lahko včasih pride do nepričakovanih rezultatov. Primerjava predznačenih in nepredznačenih celih števil enake širine na primer zahteva pretvorbo predznačene vrednosti v nepredznačeno. To lahko povzroči nepričakovane rezultate, če je predznačena vrednost negativna. Osnovni podatkovni tipiV jeziku C je več osnovnih podatkovnih tipov. Večina od njih se tvori iz enega od štirih osnovnih aritmetičnih določilnikov tipov v C (
Dejanska velikost celoštevilskih tipov se v izvedbah razlikuje. Standar zahteva le velikostne povezave med podatkovnimi tipi in najmanšimi vrednostmi za vsak podatkovni tip: Zahteve za povezave so, da Najmanjša velikost za Tip Treba je omeniti, da je v praksi velikost tipa Tudi dejanska velikost in obnašanje tipov s plavajočo vejico se razlikuje v izvedbah. Edino zagotovilo je, da KazalciC podpira rabo kazalcev, vrsto sklica, ki zapisuje naslov ali mesto objekta ali funkcije v pomnilniku. Naslov objekta je odvisen od sistema. Kazalci v C so izpeljani podatkovni tipi in se lahko dereferencirajo za dostop podatkov, ki so shranjeni na naslovu na katere kažejo, ali kličejo funkcije na katere kažejo. S kazalci se lahko upravlja s pomočjo prirejanja ali kazalčne aritmetike. Predstavitev vrednosti kazalca pri izvajanju je običajno surov pomnilniški naslov (mogoče povečan z izravnavo znotraj besedilnega polja), vendar ker kazalčni tip vsebuje tip objekta na katerega kaže, se lahko izrazi, ki vključujejo kazalce, preverijo glede na tip že med prevajanjem. Kazalčna aritmetika se samodejno skalira z velikostjo podatkovnega tipa na katero se kaže. Osnovna skladnja za opredelitev kazalca v C je:[57][n] podatkovni_tip *ime_kazalca;
Na primer: int *ptr;
To označi
To se običajno navede bolj zgoščeno kot ' Slogov zapisa kazalcev je lahko več. Na primer: int* ptr;
int *ptr;
int * ptr;
Običajno se rabi zapis operatorja posrednosti brez presledka pred imenom kazalca int *ptr1, *ptr2, a, b;
Ker C ne opredeljuje implicitne dodelitve za objekte s samodejnim pomnilniškim trajanjem,[58] je treba biti velikokrat previden pri zagotavljanju, da je naslov, na katerega kaže int *ptr = NULL;
Sicer v tem primeru kazalec še ne 'kaže nikamor', razen da ima vrednost ničelnega kazalca. Pri tem je npr. izpis 'neobstoječe' vrednosti (ničelnega kazalca), kamor naj bi kazal kazalec v funkciji printf("\n%d %p", ptr, ptr); /* program izpiše 0 in npr. (nil) */
printf("\n%p", *ptr); /* program javi napako sklica na */
/* naslov pomnilniškega mesta */
Vrednost ničelnega kazalca tako eksplicitno ne kaže na nobeno veljavno mesto v pomnilniku. Dereferenciranje njegove vrednosti je nedoločeno, kar velikokrat povzroča segmentacijsko odpoved (segmentation fault). Vrednosti ničelnih kazalcev so uporabne pri nakazovanju posebnih primerov, kot so brez »naslednjega« kazalca v končnem vozlišču povezanega seznama, ali kot naznanitev napake iz funkcij, ki vračajo kazalce. V ustreznih kontekstih izvorne kode, kot je prirejanje kazalčne spremenljivke, se lahko konstanta ničelnega kazalca zapiše kot Kazalci se v C rabijo za več namenov. Z besedilnimi znakovnimi nizi se običajno upravlja s pomočjo kazalcev v polja znakov. Dinamična dodelitev pomnilnika se izvaja s kazalci. Rezultat funkcije Kazalci na funkcije (funkcijski kazalci) so uporabni za prenašanje funkcij kot parametrov funkcij višjega reda, kot na primer qsort ali bsearch, v tabelah prekinitev (dispatch tables), ali kot povratni klici (callbacks), ki ga izvedejo obdelovalniki dogodkov (event handlers).[48] Prazni kazalci ( Neprevidna raba kazalcev je potencialno nevarna. Ker se njihov tip ne preverja, lahko kazalčna spremenljivka kaže na poljubno mesto v pomnilniku, kar lahko povzroči nezaželene učinke. Čeprav pravilno rabljeni kazalci kažejo na varna mesta, lahko kažejo na nevarna mesta, če se zanje rabi nepravilna kazalčna aritmetika. Objekti na katera kažejo se lahko ponovno dodelijo in uporabijo (obviseli kazalci) (dangling pointers) – lahko se uporabijo brez, da bi se jim dodelila vrednost (divji kazalci (wild pointers)), ali pa se jim lahko neposredno dodeli nevarno vrednost s pomočjo opustitve, unije ali prek drugega pokvarjenega kazalca. V splošnem C dopušča upravljanje in pretvarjanje med tipi kazalcev, čeprav običajno prevajalniki preskrbijo možnosti za različne nivoje preverjanja. Nekateri drugi programski jeziki te probleme rešujejo z bolj omejevalnimi tipi sklicev. PoljaPodatkovni tipi polj imajo v C tradicionalno fiksno, statično velikost med prevajanjem. Standard C99 dovoljuje tudi obliko polj s spremenljivo dolžino. Možno je tudi dodeliti blok pomnilnika (poljubne velikosti) pri izvajanju s pomočjo standardne knjižniške funkcije Ker se do polj (v bistvu) vedno dostopa prek kazalcev, se dostopi polj tipično ne preverjajo za vezano velikost polja, čeprav nekateri prevajalniki lahko preskrbijo preverjanje mej kot možnost.[60][61] Zaradi tega so možne prekršitve mej polj in te so kar vsakdanje v neprevidno napisani kodi. Lahko vodijo do različnih neugodnih stranskih pojavov, kot so: nepravilni dostopi do pomnilnika, popačenje podatkov, prekoračitve medpomnilnika in izjeme med izvajanjem. Če je zahtevano preverjanje mej, mora biti izvedeno ročno. C nima posebnega predpisa za označitev mnogorazsežnih polj, in se raje zanaša na rekurzijo znotraj sistema tipov tako da označuje polja polj, ki učinkovito opravljajo enako stvar. Vrednosti indeksov nastalega »mnogorazsežnega polja« se lahko obravnava kot (linearno) povečevanje ureditve po vrsticah. Mnogorazsežna polja se velikokrat rabijo v numeričnih algoritmih, večinoma iz uporabne linearne algebra za hranjenje podatkov matrik. Zgradba cejevskega polja je za to posebno nalogo zelo primerna. Ker se polja večinoma prenašajo kot kazalci, morajo biti, sploh v zgodnjih različicah jezika C, njihove meje fiksne vrednosti ali pa jih je treba izrecno prenesti v poljubni podprogram, ki jih zahteva. Dinamično oblikovana polja polj se ne morejo dostopati s pomočjo dvojnega indeksiranja. To se lahko naredi z dodelitvijo polja z dodatnim »vrstičnim vektorjem« kazalcev k stolpcem. Polje int polje[5]; /* označi 5 sosednjih celih števil */
int *vptr = polje; /* polja se lahko rabijo kot kazalci */
vptr[0] = 1; /* kazalci se lahko indeksirajo s */
/* skladnjo polj */
*(polje + 1) = 2; /* polja se lahko dereferencirajo s */
/* skladnjo kazalcev */
*(1 + polje) = 2; /* kazalčno seštevanje je komutativno */
2[polje] = 4; /* operator indeksa je komutativen */
Standard C99 je uvedel »polja s spremenljivo dolžino« (variable-length array, VLA), ki rešujejo nekatere, vendar ne vse, probleme običajnih cejevskih polj. Naslednji zgled z uporabo sodobnega jezika C (C99 ali novejši) prikazuje dodelitev dvorazsežnega polja na kopico s pomočjo funkcije int func(int N, int M)
{
float (*p)[N][M] = malloc(sizeof *p);
if (p == 0) {
return -1;
}
for (int i = 0; i < N; i++) {
for (int j = 0; j < M; j++) {
(*p)[i] [j] = i + j;
}
}
print_array(N, M, p);
free(p);
return 1;
}
In tukaj je podobna izvedba z uporabo gradnika samodejnega VLA standarda C99:[o] int func(int N, int M)
{
/* Pozor: preveriti je treba, da */
/* N*M*sizeof(float) NE presega */
/* omejitev za samodejne VLA in je */
/* znotraj razpoložljive velikosti */
/* sklada. */
float p[N][M]; /* Samodejni VLA se hrani na skladu in */
/* se spremeni v velikost, ko je */
for (int i = 0; i < N; i++) { /* funkcija priklicana */
for (int j = 0; j < M; j++) {
p[i] [j] = i + j;
}
}
print_array(N, M, p);
/* ni potrebe za funkcijo free(p), saj */
/* bo objekt pri izhodu iz funkcije */
/* func() izginil, vključno s */
return 1; /* preostankom okvirja sklada */
}
Izmenljivost polj in kazalcevZapis s spuščenimi indeksi V večini izraznih kontekstov, kjer je večja izjema operand Skupna velikost polja /* ... */
{
int *vptr = NULL;
static const int velikost = 10;
vptr = (int *) malloc(velikost * sizeof(vptr));
funkcija(vptr, velikost);
free(vptr);
/* ... */
}
funkcija(int *v, int n) /* funkcija ima (vsaj) dva parametra – */
{ /* kazalec na polje in njegovo velikost*/
/* (n) */
/* ... */
}
Podobno velja za mnogorazsežna polja. Navkljub tej navidezni enakosti med polji in kazalčnimi spremenljivkami je tako med njimi še vedno razlika. Čeprav se v večini izraznih kontekstih ime polja pretvori v kazalec (na njegov prvi element), kazalec sam ne zajema nobega dela pomnilnika – ime polja ni l-vrednost in njegov naslov je konstanta, z razliko od kazalčne spremenljivke. Zaradi tega se področje »na katerega kaže polje, ne |