Pojdi na vsebino

Programski jezik C

Iz Wikipedije, proste enciklopedije
C
Text in light blue serif capital letters on white background and very large light blue sans-serif letter C.
Paradigmamultiparadigmatična: imperativna (proceduralna), strukturalna
Začetna izdaja1972 (1972)[a]
OblikovalDennis Ritchie
RazvijalecBellovi laboratoriji[d], Dennis Ritchie[1], Ameriški nacionalni inštitut za standardizacijo, Mednarodna organizacija za standardizacijo in Kenneth Thompson
Stabilna izdajaC23 (31. oktober 2024 (2024-10-31)[2])
Pregledna izdajaC2Y (N3096]) (21. februar 2024 (2024-02-21)[3])
Tipizacijastatična, šibka, manifestna, imenska
Večje implementacijeC++Builder, Clang, GCC, Intel C, K&R, LCC, MSVC, pcc, Watcom C/C++
DialektiCyclone, Unified Parallel C, Split-C, Cilk, C*
VpliviALGOL 68[d][4], B, zbirni jezik, Fortran, PL/I[d], CPL[d], BCPL[d], ALGOL 60[d] in ALGOL
Vplival namnoge: AMPL, AWK, csh, C++, C--, C#, Objective-C, BitC, D, Go, java, JavaScript, JS++, Julia, Limbo, LPC, nim, Perl, PHP, Pike, Processing, Python, Rust, Seed7, Swift, V (Vlang), Vala, Verilog (HDL),[5] zig
OSMicrosoft Windows in podoben Unixu[d]
Običajna končnica datoteke.c, .h
Spletna stran
Wikibooks logo C Programming na Wikibooks
Koda za program Pozdravljen, svet v urejevalniku Gedit

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]

Zasnova

[uredi | uredi kodo]

C 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 jezika

[uredi | uredi kodo]

Kot 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 (;), ter zavita oklepaja ({ }) za združevanje blokov stavkov.

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:

  • majhno fiksno število rezerviranih besed, ki vključuje polno množico primitivov nadzornega pretoka: if/else, for, while, switch in do/while. Obstaja en imenski prostor, imena, ki jih opredeli uporabnik, se ne morejo razlikovati od rezerviranih besed z nobeno vrsto sigle,
  • veliko število aritmetičnih, bitnih, logičnih (enočlenih, dvočlenih, tročlenih) in pozapisnih operatorjev, kot so npr.: +, -, !, *, &, ~, +=, ++, ||, <<, ? :, ., -> ipd.,
  • v enem stavku se lahko izvede več kot ena prireditev,
  • funkcije:
    • vrnjene vrednosti funkcij se lahko prezrejo, kadar se ne rabijo
    • funkcijski in podatkovni kazalci omogočajo osnovno obliko zaprtja (closure) in ad hoc izvajalnega polimorfizma,
    • funkcije se ne morejo opredeliti znotraj slovarskega področja drugih funkcij,
    • spremenljivke so lahko definirane znotraj funkcije z dosegom (scope),
    • funkcija lahko kliče samo sebe, zato je rekurzija podprta,
  • podatkovna tipizacija je statična, vendar prisiljeno šibka: vsi podatki imajo tip, lahko pa se izvede implicitno pretvorbo. Znaki se lahko na primer rabijo kot cela števila,
  • uporabniško definirani tipi (typedef) in sestavljeni tipi,
    • zapisi (records) ali skupki raznovrstnih podatkovnih tipov (struct), ki jih določi uporabnik, in omogočajo združevanje in upravljanje podobnih podatkovnih elementov v celoti. Vsebine celih struktur ni mogoče primerjati z enim samim vgrajenim operatorjem (elemente je treba primerjati posamezno),
    • unija je struktura s prekrivajočimi se člani; omogoča, da več tipov podatkov deli isto pomnilniško lokacijo.
    • indeksiranje polj je sekundarni zapis, opredeljen s pomočjo kazalčne aritmetike. Z razliko od unij polja niso prvorazredni objekti – ni jih moč prirediti ali primerjati s pomočjo posameznih vgrajenih operatorjev. Pri rabi ali opredelitvi ne obstaja rezervirana beseda »array«. Namesto tega oglata oklepaja ([ ]) nakazujeta polja skladenjsko, na primer mesec[11],
    • naštevni tipi (enumerated types) z rezervirano besedo enum. Niso opremljeni z označbami in so prosto medsebojno spremenljivi s celimi števili,
    • znakovni nizi niso posebni podatkovni tip, ampak so po navadi izvedeni kot ničelno omejena polja znakov z nadzornim ničelnim znakom '\0',[b]
  • nizkonivojski nepreverjeni dostop do pomnilnika s pomočjo pretvorbe strojnih naslovov v tipske kazalce,[c][18]
  • podprogrami, ki ne vračajo vrednosti, so posebni primer funkcij z netipskim vrnitvenim tipom void,
  • pomnilnik je mogoče dodeliti programu s klici knjižniških podprogramov,
  • uporaba predprocesorskega jezika, predprocesorja C, za naloge kot so določevanje makrojev, vključevanje večkratnih datotek z izvorno kodo in pogojno prevajanje,
  • osnovna oblika modularnosti: datoteke se lahko prevedejo samostojno in povežejo skupaj, z nadzorom katere funkcije in podatkovni objekti so vidni v drugih datotekah prek atributov static ali extern,
  • preprost jedrski jezik s pomembno kompleksno funkcionalnostjo, kot na primer upravljanje vhodnoizhodnih (V/I) nalog in delo z znakovnimi nizi, matematične funkcije ali delo z datotekami omogočajo knjižniški podprogrami,
  • preprost sistem podatkovnih tipov, ki obvaruje pred brezpredmetnimi operacijami,
  • slovarsko področje spremenljivk,
  • usmeritev na proceduralno programiranje z zmožnostjo programiranja v slogu strukturalnega programiranja,
  • označitvena skladnja posnema kontekst rabe. C nima rezervirane besede »define« in se namesto tega stavek, ki se začne z imenom tipa, vzame kot označitev. Ne obstaja rezervirana beseda »function«, namesto tega je funkcija naznačena z oklepajema, ki objemata seznam njenih parametrov,
  • parametri, ki prehajajo v funkcije po vrednosti in ne po sklicu (referenci),
  • zelo omejena raba ukaza else (načelo KISS),
  • ustvarjena koda ima po prevajanju razmeroma enostavne potrebe na osnovni platformi, zaradi česar je primerna za ustvarjanje operacijskih sistemov in za uporabo v vgrajenih sistemih.

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).

Razvoj

[uredi | uredi kodo]

Zgodnji razvoj

[uredi | uredi kodo]
Ken Thompson in Dennis Ritchie, glavna tvorca programskega jezika C
Časovnica razvoja jezika C[19]
leto neuradno
ime
uradni
standard
1972 prva izdaja
1978 K&R C, C78
1989,
1990
ANSI C, C89,
ISO C, C90
ANSI X3.159-1989
ISO/IEC 9899:1990
1999 C99, C9X ISO/IEC 9899:1999
2011 C11, C1X ISO/IEC 9899:2011
2018 C17, C18 ISO/IEC 9899:2018
2024 C23, C2X ISO/IEC 9899:2024
prihodnost C2Y
18-bitni miniračunalnik DEC PDP-7A (S#115) pred restavriranjem v Oslu leta 2005. Na takšnem stroju so leta 1969 Thompson, Ritchie, Ossana, Canaday in McIlroy v zbirnem jeziku razvili operacijski sistem Unix

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 rm, cat, cp) leta 1969 postal samozadosten, in razvoj se je lahko nadaljeval neodvisno na samem PDP-7. McIlroy je kmalu zatem tvoril prvi visokonivojski jezik sistema Unix, izvedbo prevajalnika prevajalnikov TMG Roberta M. McClurea. McIlroy in Bob Morris sta uporabila TMG za zgodnji prevajalnik jezika PL/I v operacijskem sistemu Multics. Thompson se je zaradi McIlroyjevega uspeha reproduciranja TMG odločil, da je Unix, ki verjetno tedaj še ni bil imenovan tako, potreboval lasten sistemski programski jezik.

Glavni članek: programski jezik B.

Pisanje 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 C

[uredi | uredi kodo]

Najprej 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 int in char. Dodani so bili tudi kazalci, zmožnost ustvarjanja kazalcev na druge tipe, polja vseh tipov in tipe, ki jih vračajo funkcije. Polja znotraj izrazov so postala kazalci. Napisan je bil nov prevajalnik in jezik se je leta 1972 preimenoval v C.[10] Postal je prevajalni in ne tolmačitveni jezik.

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 jedra

[uredi | uredi kodo]

Zač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 struct je C postal dovolj močan za prevod Unixovega jedra. Novi jezik je dobil ime »C«, ker so bili njegovi gradniki izvedeni iz jezika »B«, ta pa je bil po Thompsonovih besedah okleščena različica jezika BCPL.[28] Novo jedro z reimplementacijo jezika C je imela različica Version 4 Unix, izdana novembra 1973.[10]

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 #include in #define brez parametrov. Kmalu za tem ga je razširil predvsem Mike Lesk in nato John Reiser, da je vključeval makre z argumenti in pogojno prevajanje.[10]

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

[uredi | uredi kodo]
Naslovnica izvirne prve izdaje knjige Programski jezik C (The C Programming Language) iz leta 1978

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:

  • standardna vhodnoizhodna knjižnica
  • podatkovni tip long int
  • podatkovni tip unsigned int
  • sestavljeni prireditveni operatorji v obliki =op (kot npr. =-) so bili spremenjeni v obliko op= (to je -=) zaradi semantične dvoumnosti pri tvorjenju takšnih konstruktov kot npr. i=-10, kar je bilo tolmačeno kot i =- 10 (zmanjšaj i za vrednost 10) namesto možno predvidenega i = -10 (priredi i vrednost -10).

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 int. Funkcije, ki so se rabile brez predhodnih označitev (deklaracij), so privzeto vračale vrednosti int, če so uporabile njihove vrednosti. Na primer:

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 int, ki so izpuščeni, v K&R C ni bilo treba navajati, kasnejši standardi pa so jih zahtevali.

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 lint, ki so poleg drugih stvari lahko preverjala doslednost rabe funkcij križem v več datotekah izvorne kode.

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:

  • funkcije tipa void (funkcije, ki ne vračajo vrednosti)
  • funkcije, ki vračajo podatkovne tipe struct ali union (namesto kazalcev. Pred tem so funkcije lahko vračale le en kazalec, celo število int ali število s plavajočo vejico float)
  • prireditev za podatkovne tipe struct
  • naštevni tipi (enumerated types, pred tem so se rabile predporocesorske opredelitve za cele fiksne vrednosti, na primer #define dZELENA 3)

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 C

[uredi | uredi kodo]
Glavni članek: ANSI C.

V 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 void, podpora za mednarodni nabor znakov in lokalnih parametrov, ter predprocesorske izboljšave. Čeprav je bila skladnja za označitev parametrov povečana, da bi vključevala slog rabljen v C++, je bil vmesnik K&R zaradi združljivosti z obstoječo izvorno kodo še naprej dovoljen.

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 __STDC__, ki razdeli kodo na dele z ANSI C in K&R, ter tako prepreči prevajalnikom na osnovi K&R C uporabo gradnikov, razpoložljivih le v ANSI C.

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]

Glavni članek: C99.

Standard 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 long long int in complex za delo s kompleksnimi števili), polja s spremenljivo dolžino (variable-length arrays (VLA)),[36] prilagodljive člane polj (flexible array members),[36] izboljšano podporo aritmetike s plavajočo vejico IEEE 754, podporo variadičnih makrojev (variadic macros, makrojev s spremenljivo mestnostjo (arnostjo)) in podporo enovrstičnih komentarjev, ki se začnejo z dvema poševnicama //, kot v BCPL ali C++. Več teh gradnikov je kot razširitve tedaj že podpiralo nekaj prevajalnikov za C.

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 int. Standardni makro __STDC_VERSION__ je določen z vrednostjo 199901L in nakazuje, da je na voljo podpora C99. GCC, Solaris Studio in drugi prevajalniki za C sedaj podpirajo mnogo ali pa vse nove gradnike C-ja v standardu C99. Prevajalnik za C v Microsoft Visual C++ izpolnjuje standard C89 in tiste dele C99, ki so zahtevani za združljivost z različico standarda C++11.[37]

Poleg tega standard C99 zahteva podporo za označevalnike (identifikatorje) Unicode v obliki ubežnih znakov (escape characters) (na primer \u0040 ali \U0001f431) in predlaga podporo za surova imena Unicode.

Leta 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 __STDC_VERSION__ je opredeljen kot 201112L in tako označuje, da je na voljo podpora standardu C11.

C17 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 __STDC_VERSION__ je opredeljen kot 201710L in tako označuje podporo standardu C17.

C23 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 __STDC_VERSION__ je opredeljen kot 202311L in tako označuje podporo standardu C23.

C2Y 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 C

[uredi | uredi kodo]
Glavni članek: vgradni C.

Programiranje 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 main, opredelitev spremenljivk, označitev podatkovnih tipov, pogojni stavki (if, switch, case), zanke (while, for), funkcije, polja in znakovni nizi, strukture in unije, bitne operacije, makroji ipd.

Skladnja

[uredi | uredi kodo]
Glavni članek: skladnja jezika C.

C 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.

Komentarji

[uredi | uredi kodo]

Komentarji so lahko med razmejilnima znakoma /* in */ enovrstično ali mnogovrstično, od standarda C99 naprej pa tudi za razmejilnima znakoma // enovrstično do konca vrstice. Komentarji, ki jih razmejujeta /* in */, ne smejo biti vgnezdeni. Če se zaporedje teh dveh znakov pojavi znotraj znakovnega niza ali znakovnega črkovnega simbola, se ne obravnavata kot razmejilna znaka komentarja.[31]:192 Komentarji oblike:

/* ... */

/*
...
*/

/* ... // ... */

// ...

/*
... // ...
*/

so v redu, vgnezdeni enovrstično pa npr. ne:

/* /* ... */ ... */

ali vgnezdeni mnogovrstično:

/* 
/* ... 
*/ ... 
*/

saj drugo zaporedje /* ne velja za razmejilni znak komentarja, kot tudi ne drugo zaporedje */, ki je sedaj zunaj komentarja, in prevajalnik bi v obeh primerih javil napako.

Izrazi

[uredi | uredi kodo]

Datoteke 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: struct, union in enum, ali prirejajo tipe in morda rezervirajo pomnilniška mesta novim spremenljivkam, po navadi z zapisom tipa, ki mu sledi ime spremenljivke. Rezervirane besede, kot sta char in int, označujejo vgrajene tipe. Deli kode so obdani z zavitima oklepajema { in }, ki omejujeta označitveni doseg ali pa delujeta kot samostojni stavek za nadzorne strukture.

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 if ... [else], z iterativnimi izvršitvami (zankami) do ... while, while in for. Stavek for ima ločene dodelitvene izraze, izraze za preverjanje in izraze za ponovno dodelitev – vsak od njih pa se lahko opusti. Izraza break in continue se lahko rabita v zankah. break se rabi za zapustitev najbolj notranjega zančnega stavka, continue pa za preskočitev na njeno ponovno dodelitev (alokacijo). Obstaja tudi nestrukturalni stavek goto, ki preusmerja neposredno na imenovano oznako (label) znotraj funkcije. Oznake so tvorijo z imeni, ki jim sledi dvopičje – na primer oznaka:. switch izbere primer case, ki se izvede na podlagi vrednosti celoštevilskega izraza. Za razliko od mnogih drugih jezikov bo nadzorni pretok padel v naslednji primer case, razen če ga prekine prekinitev break.

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 (&&, ||, ?: in operator vejica). To dovoljuje visoko stopnjo optimizacije kode prevajalnika, vendar od programerjev v C zahteva večjo skrb za dosego zanesljivih rezultatov kot je potrebna pri drugih programskih jezikih.

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 znakov

[uredi | uredi kodo]

Osnovni 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 \uXXXX ali \UXXXXXXXX, kjer X označuje šestnajstiško zapisan znak.

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 besede

[uredi | uredi kodo]

Naslednje 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. INT, INt, InT, Int, iNT, iNt ali inT na primer ni enako kot int. V različnih izvedbah prevajalnikov za C se lahko pojavijo tudi druge rezervirane besede, npr. ada, asm, fortran, pascal, čeprav se tam običajno nestandardne besede začnejo z enim ali dvema podčrtajema, npr. __asm, _Cdecl ipd.[43] V primerjavi z drugimi velikimi jeziki je imel C majhno število rezerviranih besed. Ada jih ima na primer 62. Ena od značilnosti jezika C je, da lahko naredi veliko z relativno malo posebnimi simboli in rezerviranimi besedami.[41]

C99 je dodal pet rezerviranih besed (‡ označuje drugačen črkovalni vzdevek za rezervirano besedo C23):

inline
restrict
_Bool
_Complex
_Imaginary

C11 je dodal še sedem razerviranih besed (‡ označuje drugačen črkovalni vzdevek za rezervirano besedo C23):

_Alignas
_Alignof
_Atomic
_Generic
_Noreturn
_Static_assert
_Thread_local

C23 je dodal še 15 rezerviranih besed:

  • alignas
  • alignof
  • bool
  • constexpr
  • false
  • nullptr
  • static_assert
  • thread_local
  • true
  • typeof
  • typeof_unqual
  • _BitInt
  • _Decimal32
  • _Decimal64
  • _Decimal128

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 entry se je redko ali pa nikoli rabila in sedaj ni več rezervirana. V drugi izdaji knjige Programski jezik C sta Kernighan in Richie zapisala: »... [rezervirana beseda] entry, prej rezervirana, a nikoli uporabljena, ni več rezervirana.« in: »Mrtvorojena rezervirana beseda entry je umaknjena.« [31]:192, 259

Operatorji

[uredi | uredi kodo]
Glavni članek: operatorji v C in C++.

C 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 =, ki je v matematiki rezerviran za izražanje enakosti, za naznanitev prireditve po zgledu predhodnih jezikov Fortrana in PL/I, vendar z razliko od ALGOLa in njegovih izpeljank. Podobnost med cejevskima operatorjema za prireditev in za primerjanje enakosti (==) so kritizirali, saj se ju lahko preprosto zamenja. V mnogih primerih mora biti vsak rabljen v kontekstu drugega brez napak prevajalnika, čeprav nekateri prevajalniki tvorijo opozorila. Pogojni izraz if (a == b + 1) se na primer lahko napačno zapiše kot if (a = b + 1) in bo njegova vrednost po izvršitvi pravilna (true), če je a enak 0 po prireditvi.[44]

Operatorska prednost v C ni vedno intuitivna, saj se na primer operator == zvezuje tesneje (se izvrši pred) kot operatorja & (bitni IN (AND) in | (bitni ALI (OR) v izrazih, kot je x & 1 == 0, ki bi moral biti zapisan kot (x & 1) == 0, če je bil to namen programerja.[45]

Zgradba, oblika programa in slog v C

[uredi | uredi kodo]

Osnovni 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 ({ in }).

Zgleda programov

[uredi | uredi kodo]

Program Pozdravljen, svet

[uredi | uredi kodo]
Program »Hello, World!« Briana Wilsona Kernighana (1978)

Izvirni program Pozdravljen, svetHello, 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 printf. Navadno je standardni izhod povezan s terminalom ali z zaslonom monitorja, lahko pa je tudi datoteka ali kakšna druga strojna naprava. Program je napisan v skladu s standardom ANSI C.[d]

#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) #include. Pred samim prevajanjem programa predprocesor pregleda izvorno kodo in izvrši vse predprocesorske ukaze. Ti ukazi se vedno začnejo z znakom #. Ukaz #include povzroči, da se na njegovo mesto v izvorno kodo vključi datoteka stdio.h, ki vsebuje standardne vhodnoizhodne funkcije.

int main(void)

V naslednji vrstici se opredeli funkcija z imenom main. Ta funkcija ima poseben pomen v C programih. Pri začetku izvajanja programa se najprej kliče ta funkcija. Rezervirana beseda int na začetku vrstice pove, da funkcija main izvajalnemu sistemu vrne celo število, rezervirana beseda void pa, da funkcija ne sprejme nobenih parametrov. Funkcija main po standardu ANSI C vedno vrača vrednost, čeprav nekateri prevajalniki prevedejo kodo uspešno, četudi temu ni tako. Tudi v jeziku C++ je ta funkcija še vedno C-jevska. Funkcija main je lahko opredeljena s pomočjo naslednjih štirih oblik ob označitvi:[47][e][f][g][h]

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 main.

printf("Pozdravljen, svet!\r\n");

V tej vrstici se kliče funkcija printf, ki je označena v datoteki stdio.h. Pri tem klicu se funkciji printf poda en parameter v obliki znakovnega niza, ki je pomnilniški naslov prvega znaka v literalu znakovnega niza "Pozdravljen, svet\r\n". Literal znakovnega niza je neimenovano polje, ki jo samodejno nastavi prevajalnik, z elementi tipa char in končnim znakom NULL (vrednost ASCII 0), ki označuje konec polja, kar omogoča funkciji printf, da določi dolžino niza). Znak NULL se lahko zapiše tudi kot ubežno zaporedje \0. Dvoznakovni niz \n je standardno ubežno zaporedje, ki se v C prevede v znak nove vrstice (EOL, end of line). Znak je namenjen kot ukaz izhodni napravi, da postavi trenutno lego na začetek naslednje vrstice. Vrnjena vrednost funkcije printf je tipa int, vendar je tiho zavržena, ker se ne uporablja. Previdnejši program lahko preizkusi vrnjeno vrednost, da preveri, ali je funkcija printf uspela. Podpičje ; prekine stavek.

return 0;

Vrstica zaključi izvajanje funkcije main in vrne celo število 0.

}

Zaprti zaviti oklepaj pomeni zaključek opredelitve funkcije main. Po specifikaciji standarda C99 in novejših bo funkcija main (za razliko od vseh drugih funkcij) implicitno vrnila vrednost 0 preden doseže }, ki prekine funkcijo.[i] Vrnjeno vrednost 0 izvajalni sistem tolmači kot izhodno kodo, ki označuje uspešno izvedbo funkcije main.[48]

Najmanjši program

[uredi | uredi kodo]

Katera opredelitev funkcije main se uporabi v programu, je verjetno odvisno od posamezne izbire. Trenutni standard C vsebuje dva zgleda rabe main() in dva main(void), standard C++ pa rabi main(). Vrednost, ki jo main vrača (in mora biti tipa int), služi kot status prekinitve. Tega program vrne gostiteljevemu okolju.

Standard C opredeljuje vrnitveni vrednosti 0 (»nič napak« (zero errors)) in EXIT_SUCCESS, ki označujeta uspeh, ter EXIT_FAILURE za označevanje neuspešnega izvajanja programa. (EXIT_SUCCESS in EXIT_FAILURE sta opredeljeni v standardni zaglavni datoteki stdlib.h). Druge vrnitvene vrednosti služijo kot izvedbeni opredelitveni pomeni. V Linuxu na primer program, ki ga je prekinil ukaz signal, vrne kodo številske vrednosti signala plus 128.

Najmanjši pravilen program v C vsebuje prazno funkcijo main brez parametrov:

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. main(void) in ne main (void), z razliko od sizeof (), ki dejansko strogo ni funkcija, in je skladenjska rezervirana beseda za enočleni operator.[49] sizeof je lahko podobno kot return brez okroglih oklepajev. Pri return je to celo zaželeno, razen če je dvoumno.

Ker nista navedena return ali exit, funkcija main ob izhodu vrne 0.[48] To je poseben primer predstavljen v standardu C99, ki velja le za funkcijo main. Če se zahteva še ta navedba, ima najmanjši program obliko:

int main(void) { return 0; }

Funkcija main bo za uspešno delovanje programa po navadi klicala druge funkcije. Ni treba, da je na vrhu programa, tako da se program v C ne začne v prvi vrstici, ampak tam kjer je navedena funkcija main.[46] Funkcije main ni možno klicati ali jo zagnati s kakšno drugo funkcijo v programu. Kliče jo lahko le operacijski sistem in na ta način se zažene program v C. Funkcija main ni nikoli statična, in, če je navedena kot static int main(), bo prevajalnik običajno javil napako.

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 main.

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 #include, objekti knjižnice pa se povežejo v končno izvršno sliko. Določene knjižniške funkcije, kot sta na primer printf ali scanf, so opredeljene s standardom C, in se imenujejo standardne knjižniške funkcije.

Funkcija lahko vrne vrednost klicatelju – po navadi drugi funkciji C-ja, ali gostiteljevemu okolju za funkcijo main. Zgoraj omenjena funkcija printf vrne koliko znakov je bilo izpisano, vendar se ta vrednost običajno prezre.

Podatkovni tipi

[uredi | uredi kodo]
Glavni članek: podatkovni tipi v C.
Koncepti jezika C po standardu C99

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 (char) in naštevne tipe (enum). Celoštevilski tip char je pogosto rabi za enobajtne znake. C99 je z rezervirano besedo _Bool dodal Booleov podatkovni tip (Booleova spremenljivka). Obstajajo tudi izpeljani tipi, kot so: polja (tabele, arrays), kazalci (pointers), zapisi (records, struct) in nenaznačene unije (unions, union).

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 tipi

[uredi | uredi kodo]

V 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 (char, int, float in double), ter štirih izbirnih določilnikov (signed, unsigned, short in long). Vsi razpoložljivi osnovni aritmetični tipi so navedeni v razpredelnici:

podatkovni tip
in sopomenke
pojasnilo najmanjša velikost
[biti]
mejne vrednosti
v limits.h
formatno določilo pripona za desetiške konstante
char najmanjša naslovljiva enota stroja, ki lahko vsebuje osnovni nabor znakov. Njen tip je celoštevilski. Dejanski tip je lahko predznačen ali nepredznačen, kar je odvisno od izvedbe. Vsebuje CHAR_BIT bitov in njegov obseg je [−128, +127].[51] 8 CHAR_MIN /
CHAR_MAX
%c
signed char enaka velikost kot char, vendar je zagotovo predznačena in njegov obseg je [−128, +127].[51][j] 8 SCHAR_MIN /
SCHAR_MAX
%c (ali %hhi za numerični vnos)
unsigned char enaka velikost kot char, vendar je zagotovo nepredznačena. V dvojiškem zapisu je predstavljen brez zapolnjevalnih bitov, tako je njegov obseg točno [0, 2CHAR_BIT−1],[52] običajno [0, +255][53] 8 0 /
UCHAR_MAX
%c (ali %hhu za numerični vnos)
short
short int
signed short
signed short int
predznačeni celoštevilski tip short. Njegov obseg je vsaj [−32.768, +32.767].[54][51][j] 16 SHRT_MIN /
SHRT_MAX
  • %hi %hd
unsigned short
unsigned short int
podobno kot short, vendar nepredznačeno, in njegov obseg je vsaj [0, +65.535].[51] 16 0 /
USHRT_MAX
%hu
int
signed
signed int
osnovni predznačeni celoštevilski tip. Njegov obseg je vsaj [−32.768, +32.767].[54][51][j] 16 INT_MIN /
INT_MAX
  • %i %d
brez[55]
unsigned
unsigned int
podobno kot int, vendar nepredznačeno, in njegov obseg je vsaj [0, +65.535].[51] 16 0 /
UINT_MAX
%u u ali U[55]
long
long int
signed long
signed long int
predznačeni celoštevilski tip long. Njegov obseg je vsaj [−2.147.483.648, +2.147.483.647].[54][51][j] 32 LONG_MIN /
LONG_MAX
  • %li %ld
l ali L[55]
unsigned long
unsigned long int
podobno kot long, vendar nepredznačeno, in njegov obseg je vsaj [0, +4.294.967.295].[51] 32 0 /
ULONG_MAX
%lu obe u ali U in l ali L[55]
long long
long long int
signed long long
signed long long int
predznačeni celoštevilski tip long long. Njegov obseg je vsaj [−9.223.372.036.854.775.808, +9.223.372.036.854.775.807].[54][51][j] Uveden s standardom C99. 64 LLONG_MIN /
LLONG_MAX
  • %lli %lld
ll ali LL[55]
unsigned long long
unsigned long long int
podobno kot long long, vendar nepredznačeno in njegov obseg je vsaj [0, +18.446.744.073.709.551.615].[51] Uveden s standardom C99. 64 0 /
ULLONG_MAX
%llu obe u ali U in ll ali LL[55]
float tip za realna števila s plavajočo vejico in enojno točnostjo. Dejanske značilnosti niso navedene (razen spodnjih mej), vendar je na večini sistemov to dvojiški format števila s plavajočo vejico in enojno točnostjo IEEE 754 (32 bitov). Ta format zahteva izbirni Annex F »IEC 60559 floating-point arithmetic.«   FLT_MIN /
FLT_MAX
  • %f[k] %F
  • %g %G
  • %e %E
  • %a %A
f ali F
double tip za realna števila s plavajočo vejico in dvojno točnostjo. Dejanske značinosti niso navedene (razen spodnjih mej), vendar je na večini sistemov to dvojiški format števila s plavajočo vejico in dvojno točnostjo IEEE 754 (64 bitov). Ta format zahteva izbirni Annex F »IEC 60559 floating-point arithmetic.«   DBL_MIN /
DBL_MAX
  • %f[l]
  • %lf %lF
  • %lg %lG
  • %le %lE
  • %la %lA[m]
 
long double tip za realna števila s plavajočo vejico in razširjeno točnostjo. Dejanske značilnosti niso navedene. Z razliko od tipov float in double je lahko format števila s plavajočo vejico in razširjeno točnostjo x86 (80 bitov, tipično pa 96 ali 128 bitov v pomnilniku z zapolnjevanimi bajti), »double-double«, ki ni v skladu z IEEE (128 bitov), dvojiški format števila s plavajočo vejico in štirikratno točnostjo IEEE 754 (128 bitov), če je format s povišano točnostjo zagotovljen, ali drugače enako kot double. Za podrobnosti glej članek o long double.   LDBL_MIN /
LDBL_MAX
  • %Lf %LF
  • %Lg %LG
  • %Le %LE
  • %La  %LA[m]
l ali L

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 long long ni manjši od long, ki ni manjši od int, ta pa ne manjši od short. Ker je velikost char vedno najmanjši podprti podatkovni tip, vsi drugi podatkovni tipi ne morejo biti manjši.

Najmanjša velikost za char je 8 bitov, najmanjši velikosti za short in int sta 16 bitov, za long 32 bitov in za long long mora vsebovati vsaj 64 bitov.

Tip int mora biti celoštevilski tip, za katerega je ciljni procesor najbolj učinkovit. To omogoča veliko prilagodljivost, saj so lahko na primer vsi tipi 64-bitni. Vendar je priljubljeno več različnih shem celoštevilskih širin (podatkovnih modelov). To je zato ker podatkovni model opredeljuje kako različni programi med seboj komunicirajo, enotni podatkovni model se rabi znotraj uporabniškega vmesnika danega operacijskega sistema.[56]

Treba je omeniti, da je v praksi velikost tipa char po navadi 8 bitov, tipa short pa 16 bitov (kakor tudi njihova nepredznačena dvojnika). To velja za platforme pogoste v 1990-ih, kot so: SunOS 4 Unix, Microsoft MS-DOS, sodobni Linux in Microchip MCC18 za vgradne 8-bitne mikrokontrolerje PIC. POSIX zahteva, da je velikost char točno 8 bitov.

Tudi dejanska velikost in obnašanje tipov s plavajočo vejico se razlikuje v izvedbah. Edino zagotovilo je, da long double ni manjši od double, ki ni manjši od float. Če ju strojna oprema podpira, se po navadi rabita 32-bitna in 64-bitna dvojiška formata s plavajočo vejico IEEE 754.

Kazalci

[uredi | uredi kodo]

C 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 ptr kot označevalnik objekta naslednjega tipa:

  • kazalec, ki kaže na objekt tipa int

To se običajno navede bolj zgoščeno kot 'ptr je kazalec na int'. Pri tem znak »*« (zvezdica) pomeni unarni ali nomadski operator posrednosti (indirektnosti, indirection operator) ali dereferenčni operator (dereference operator).

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 *ptr;, predvsem zaradi nedvomljivosti pri hkratnih večkratnih opredelitvah:

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 ptr, veljaven. Zaradi tega včasih predlagajo, da se kazalcu eksplicitno dodeli vrednost ničelnega kazalca (null pointer value), ki je v C tradicionalno določena s standardiziranim makrojem NULL:[59]

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 s formatom izpisa podatkovnega tipa int %din formatom izpisa kazalcev %p v redu, izpis 'praznega' naslova, pa ne:

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 0, z ali brez eksplicitne opustitve na tip kazalca, ali kot makro NULL, ki je opredeljen v več standardnih zaglavnih datotekah, ali od standarda C23 naprej kot konstanta nullptr. V pogojnih zvezah imajo vrednosti ničelnih kazalcev napačno vrednost (false, vse druge vrednosti kazalcev pa pravilno (true).

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 malloc je običajno pretvorjen v podatkovni tip podatkov, ki jih je treba shraniti. Mnogo podatkovnih tipov, kot so na primer povezani seznami ali drevesa, je velikokrat izvedeno kot dinamično dodeljeni objekti struct, ki so med seboj povezani s kazalci. Kazalci na druge kazalce se pogosto uporabljajo v večrazsežnostnih poljih in poljih strukturnih objektov.

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 (void *) kažejo na objekte nedoločenega tipa, in se lahko zaradi tega uporabijo kot »generični« podatkovni kazalci. Ker velikost in tip nakazanih objektov nista znana, se prazni kazalci ne morejo dereferencirati, in tudi kazalčna aritmetika nad njimi ni dovoljena. Enostavno jih je tudi pretvoriti v in iz poljubnega tipa objektnega kazalca. V mnogih zvezah se jih dejansko implicitno pretvori.[48]

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.

Polja

[uredi | uredi kodo]

Podatkovni 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 malloc (calloc) in ga obravnavati kot polje. Cejevska združitev polj in kazalcev pomeni, da so označena polja in ta dinamično dodeljena simulirana polja dejansko zamenljiva med seboj.

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 polje se na primer lahko označi in uporabi na naslednje načine:

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 malloc in uporabo večrazsežnega indeksiranja polja za dostope (ki lahko uporablja preverjanje meja na mnogih prevajalnikih C):

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 kazalcev

[uredi | uredi kodo]

Zapis s spuščenimi indeksi x[i] (kjer x označuje kazalec) je skladenjski sladkor za *(x+i).[62] S prednostjo pomnilnikovega znanja o kazalčnem tipu naslov, na katerega kaže x + i, ni osnovni naslov (na katerega kaže x), povečan za i bitov, ampak je določen kot osnovni naslov, povečan z i pomnoženim z velikostjo elementa, na katerega kaže x. Tako x[i] označuje i+1-ti element polja.

V večini izraznih kontekstov, kjer je večja izjema operand sizeof, se naprej izraz tipa polja samodejno pretvori v kazalec na prvi element polja. To pomeni, da se polje nikoli v celoti ne skopira, ko je imenovan kot parameter funkciji, ampak se prenese le naslov njegovega prvega elementa. Čeprav klici funkcij v C uporabljajo semantiko klicev po vrednosti, so tako polja učinkovito prenesena po sklicu.

Skupna velikost polja x se lahko določi s pomočjo operatorja sizeof na izraz tipa polja. Velikost elementa se lahko določi z operatorjem sizeof na poljubni dereferenciran element x, kot v izrazu n = sizeof *x ali n = sizeof x[0], tako, da se število elementov v označenem polju x lahko določi kot sizeof x / sizeof x[0]. Zadnji izraz velja le za imena polj: spremenljivke označene s spuščenimi indeksi (int x[20]). Zaradi semantike C-ja ni mogoče določiti celotne velikosti polj prek kazalcev na polja ali polj tvorjenih z dinamično dodelitvijo (malloc). Tako izrazi, kot je sizeof vptr / sizeof vptr[0] (kjer vptr označuje kazalec), ne bodo delovali, saj prevajalnik privzame, da se zahteva velikost kazalca samega.[63][64] Ker parametri imena polja na sizeof niso pretvorjeni v kazalce, ne kažejo takšne nejasnosti. V polja tvorjena z dinamično dodelitvijo pa se dostopa s kazalci in ne kot prave spremenljivke polja, zato imajo enake težave z operatorjem sizeof kot kazalci na polja. Če se v kakšni funkciji potrebuje vrednost velikosti (enorazsežnega) poljavptr, jo je treba vključiti kot njen parameter (npr. velikost). Na primer:

/* ... */
{
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 more spremeniti, in na ime polja je nemogoče prirediti nov naslov. Vsebina polj pa se lahko kopira, na primer s pomočjo funkcije memcpy ali z dostopanjem do posameznih elementov.

Strukture in unije

[uredi | uredi kodo]

Struktura struct je označitev sestavljenega podatkovnega tipa (ali zapisa), ki opredeljuje fizično združen seznam spremenljivk pod enim imenom v bloku pomnilnika, kar omogoča dostop do različnih spremenljivk prek enega samega kazalca ali z označenim imenom strukture, ki vrne isti naslov. Podatkovni tip struct lahko vsebuje druge podatkovne tipe, zato se uporablja za zapise mešanega tipa podatkov, kot je vnos imenika trdega diska (dolžina datoteke, ime, pripona, fizični naslov itd.) ali druge zapise mešanega tipa (ime , naslov, telefon, stanje itd.).

Struktura se neposredno sklicuje na sosednji blok fizičnega pomnilnika, ki je običajno razmejen (po velikosti) z mejami dolžine besede. Ustreza funkciji s podobnim imenom, ki je na voljo v nekaterih zbirnikih za procesorje Intel. Ker je vsako polje v strukturi blok neprekinjenega pomnilnika, se od začetka nahaja na določenem fiksnem odmiku.

Ker je vsebina strukture shranjena v neprekinjenem pomnilniku, je treba za pridobitev števila bajtov, potrebnih za shranjevanje določene vrste strukture, uporabiti operator sizeof, tako kot se lahko uporablja za primitive. Poravnava določenih polj v strukturi (glede na meje besed) je specifična za izvedbo in lahko vključuje oblazinjenje, čeprav sodobni prevajalniki običajno podpirajo ukaz #pragma pack, ki spremeni velikost v bajtih, uporabljenih za poravnavo.[65]

V jeziku C++ je struktura enaka razredu C++, vendar ima drugačno privzeto vidnost: člani razreda so privzeto zasebni, medtem ko so člani strukture privzeto javni.

V C in C++ so neoznačene unije union izražene skoraj tako kot strukture (struct), le da se vsak podatkovni član začne na isti lokaciji v pomnilniku. Podatkovni člani, tako kot v strukturah, niso nujno primitivne vrednosti in so dejansko lahko strukture ali celo druge unije.

Glavna uporaba unije je omogočanje dostopa do skupne lokacije z različnimi podatkovnimi tipi, na primer dostop do vhodnoizhodne strojne opreme, skupna raba bitnih polj in besed, ali tipsko sprevračanje (type punning). Unije lahko zagotovijo tudi nizko stopnjo polimorfizma. Vendar ni tipskega preverjanja, zato mora programer zagotoviti, da se v različnih kontekstih dostopa do ustreznih polj. Ustrezno polje spremenljivke unije je običajno določeno s stanjem drugih spremenljivk, po možnosti v obdajajoči strukturi.

En pogost programski idiom, ki ga C uporablja z unijami za izvajanje tega, kar C++ imenuje reinterpret_cast, z dodelitvijo enemu polju unije in branjem iz drugega, kot je storjeno v kodi, ki je odvisna od neobdelane predstavitve vrednosti. Praktični primer je metoda računanja kvadratnih korenov z uporabo reprezentacije IEEE. Vendar to ni varna uporaba unij na splošno.

Strukture in unije imajo enako skladnjo. V C++ so unije načeloma bolj zaradi združljivosti za nazaj s C.[65]

Upravljanje pomnilnika

[uredi | uredi kodo]

Ena od najpomembnejših funkcij programskega jezika je zagotovitev pripomočkov za upravljanje pomnilnika in objektov, ki so shranjeni v njem. C zagotavlja tri glavne načine dodelitve pomnilnika objektom:[48]

  • statični: prostor za objekt je zagotovljen v dvojiški obliki med prevajanjem. Življenjska doba teh objektov je enaka času, ko je dvojiška datoteka, ki jih vsebuje, naložena v pomnilnik.
  • samodejni: začasni objekti se lahko shranijo na sklad, ta prostor pa se samodejno sprosti in je ponovno uporaben po tem ko se izstopi iz bloka v katerem so objekti označeni.
  • dinamični: bloki pomnilnika poljubne velikosti se lahko zahtevajo med izvajanjem s pomočjo funkcij, kot sta malloc ali calloc, iz dela pomnilnika, ki se imenuje kopica. Ti bloki obstajajo dokler se jih s klicanjem knjižničnih funkcij realloc ali free ne sprosti za ponovno rabo.

Ti trije pristopi so primerni v različnih razmerah in imajo različne izkupnine. Statična dodelitev pomnilnika ima majhno dodelitveno povprečnino, samodejna dodelitev jo lahko vsebuje nekaj več, dinamična dodelitev pomnilnika pa ima lahko precej večjo povprečnino tako za dodelitev kot za sprostitev. Trajna narava statičnih objektov je uporabna za ohranjanje stanja informacij med klici funkcij. Samodejna dodelitev je preprosta za uporabo, vendar je običajno prostor sklada tipično bolj omejen in prehoden tako od statičnega pomnilnika ali od prostora kopice. Dinamična dodelitev pomnilnika omogoča priročno dodelitev objektov, katerih velikost je znana le med izvajanjem. Večina programov v C s pridom uporablja vse tri načine.

Kjer je možno, sta samodejna in statična dodelitev najpreprostjši, ker pomnilnik upravlja prevajalnik, in programerju ni treba paziti na opravila, ki so potencialno dovzetna za napake ročnega dodeljevanja in sproščanja pomnilnika. Vendar se lahko veliko podatkovnih struktur med izvajanjem spreminja po velikosti, in, ker morajo imeti statične dodelitve (in samodejne dodelitve pred standardom C99) fiksno velikost med prevajanjem, je velikokrat potrebna dinamična dodelitev.[48] Pred standardom C99 so bila polja s spreminjajočo velikostjo običajen primer tega. (Glej članek o funkciji malloc za primer dinamično dodeljenih polj.) Z razliko od samodejne dodelitve, ki lahko med izvajanjem odpove z nepredvidljivimi posledicami, funkcije dinamične dodelitve vračajo pokazatelj (v obliki vrednosti ničelnega kazalca) kadar zahtevan pomnilnik ne more biti dodeljen. (Statično dodelitev, ki je prevelika, po navadi zazna povezovalnik ali nalagalnik (loader), preden program sploh začne izvajanje.)

Dokler ni posebej označeno, statični objekti vsebujejo vrednosti nič ali ničelnih kazalcev pri izvajanju programa. Samodejnim in dinamičnim dodeljenim objektom se dodeli vrednost le, če je začetna vrednost eksplicitno označena, drugače imajo na začetku nedoločene vrednosti (tipično, katerikoli bitni vzorec, ki se nahaja v pomnilniku, in lahko predstavlja tudi nepravilno vrednost za določeni tip). Če program poskuša dostopati do nedodeljene vrednosti, so rezultati nedoločeni. Veliko sodobnih prevajalnikov poskuša zaznati in opozoriti na ta problem, vendar se lahko pojavijo tako napačno pozitivni kot napačno negativni.

Drug problem je, da mora biti dodelitev pomnilnika s kopico sinhronizirana s svojo dejansko rabo v kateremkoli programu da se lahko ponovno uporabi kolikokrat je mogoče. Če na primer vrednost edinega kazalca na pomnilniško dodelitev s kopico uide iz svojega dosega ali se prepiše preko preden je klicana funkcija free, oziroma se izrecno ponovno dodeli, potem se tisti del pomnilnika ne more obnoviti za kasnejšo ponovno rabo in je dejansko za program izgubljen. Ta pojav je znan kot puščanje pomnilnika (memory leak). Možno je tudi obratno, da se pomnilnik sprosti, vendar se nanj še vedno sklicuje, kar lahko spet povzroči neprevidljive rezultate. Tipično se bodo simptomi pokazali v delu programa, ki je zelo oddaljen od dejanske napake, kar bo oteževalo sledenje problemu. Takšni problemi so izboljšani v jezikih s samodejnim čiščenjem pomnilnika.

Knjižnice

[uredi | uredi kodo]

Osnovno razširitev jezika C predstavljajo knjižnice. Knjižnica je v C množica funkcij zbrana v eni »arhivski« datoteki. Vsaka knjižnica ima po navadi zaglavno datoteko s prototipi funkcij, ki jih vsebuje knjižnica in se lahko rabijo v programu, in označitvami posebnih podatkovnih tipov in makro simbolov znotraj teh funkcij. Da lahko program rabi knjižnico, mora vsebovati njeno zaglavno datoteko, knjižnica pa mora biti povezana s programom, kar v mnogih primerih zahteva prevajalnikove zastavice (na primer -lm, okrajšano za »poveži z matematično knjižnico« (link the math library)).[48]

Najobičajnejša knjižnica C je standardna knjižnica jezika C, ki jo navajata standarda ISO in ANSI C. Standardna knjižnica je del vsake izvedbe C. Izvedbe, ki ciljajo na omejena okolja, kot so na primer vgradni sistemi, lahko zagotavljajo le podmnožico standardne knjižnice. Standardna knjižnica C podpira vhodnoizhodni tok, dodelitev pomnilnika, matematične funkcije in količine, znakovne nize in časovne vrednosti. Več ločenih standardnih zaglavnih datotek (na primer stdio.h) določa vmesnike zanje in druge pripomočke standardne knjižnice.

Druge običajne množice funkcij knjižnice C so tiste, ki se rabijo v aplikacijah, posebej prirejenih za Unix in sisteme podobne Unix, še posebej funkcije, ki zagotavljajo vmesnik za jedro. Te funkcije v podrobnosti navajajo standardi, kot sta POSIX in Single UNIX Specification.

Ker je veliko programov napisanih v C, so na voljo raznolike druge knjižnice. Knjižnice so običajno napisane v C, ker C-jevski prevajalniki tvorijo učinkovito objektno kodo. Programerji nato naredijo vmesnike h knjižnici, tako da se lahko njeni podprogrami rabijo v visokonivojskih jezikih, kot so java, Perl in Python.[48]

Obravnavanje datotek in tokov

[uredi | uredi kodo]

Datotečni vhod in izhod nista del samega jezika C, temveč ju upravljajo knjižnice (kot je standardna knjižnica C) in z njimi povezane zaglavne datoteke (na primer stdio.h). Obravnavanje datotek se na splošno izvaja prek V/I na visoki ravni, ki deluje prek tokov (streams). Tok je s tega vidika tok podatkov, ki je neodvisen od naprav, medtem ko je datoteka konkretna naprava. V/I na visoki ravni poteka prek povezave toka z datoteko. V standardni knjižnici C se medpomnilnik (pomnilniško območje ali čakalna vrsta) začasno uporablja za shranjevanje podatkov, preden se pošljejo na končni cilj. To skrajša čas čakanja na počasnejše naprave, na primer trdi disk ali negibljivi disk (SSD). Vhodnoizhodne funkcije nizke ravni niso del standardne knjižnice C, ampak so na splošno del »golega« programiranja (programiranja, ki je neodvisno od katerega koli operacijskega sistema, kot je večina vdelanega programiranja). Z nekaj izjemami izvedbe vključujejo nizkonivojski V/I.

Jezikovna orodja

[uredi | uredi kodo]

Za pomoč programerjem v C pri iskanju in popravljanju stavkov z neopredeljenim obnašanjem ali z možnimi napačnimi izrazi so razvili več orodij z večjo strogostjo, ki jo zagotavljajo prevajalniki. Med prvimi takšnimi orodji je bilo orodje lint. Njegov razvoj je vodil do nastanka mnogih drugih.

Avtomatizirano preverjanje izvorne kode in pregledovanje je koristno v vsakem jeziku. Za C obstaja več takšnih orodij, kot na primer lint. Običajna praksa je, da se z orodjem lint odkrije vprašljiva koda, ko je program prvič napisan. Ko program preveri lint, se ga s prevajalnikom za C prevede. Poleg tega lahko mnogi prevejalniki opcijsko opozorijo o skladenjsko pravilnih konstruktih, ki bodo verjetno dejansko napake. MISRA C je lastniška množica navodil v izogib takšni vprašljivi kodi, razvita za vgradne sisteme.[66]

Obstajajo tudi prevajalniki, knjižnice in mehanizmi na nivoju operacijskih sistemov za izvajanje procesov, ki niso standardni deli jezika C, kot na primer: preverjanje mej polj, odkrivanje prekoračitev medpomnilnika, serializacija, sledenje dinamičnemu pomnilniku in samodejno čiščenje pomnilnika.

Orodja, kot so pomnilniški razhroščevalniki: Purify, Valgrind, Insure++, dmalloc, Electric Fence, Inspector, libcwd, Memwatch, MTuner, Oracle Developer Studio, Splint, TotalView ali WinDbg skupaj s povezovanjem knjižnic, ki vsebujejo posebne različice funkcij dinamičnih dodelitev pomnilnika, lahko pomagajo odkrivati napake v rabi pomnilnika pri izvajanju.

Uporabe

[uredi | uredi kodo]

Sistemsko programiranje

[uredi | uredi kodo]
Nekatere aplikacije, jeziki, pogoni, knjižnice, vmesniki, kompleti orodij, spletni strežniki razviti iz ali v programskem jeziku C: Linuxovo jedro, AVR Libc, GCC, Python, PostgreSQL, SQLite, OpenGL, GTK, Apache, nginx

C se pogosto uporablja za sistemsko programiranje pri izvajanju operacijskih sistemov in vgrajenih sistemskih aplikacij.[67] To je iz več razlogov:

  • C omogoča dostop do platformske strojne opreme in pomnilnika s kazalci in tipsko sprevračanje, tako da je mogoče sistemsko specifične funkcije (npr. nadzornostatusne registre, vhodnoizhodne registre) konfigurirati in uporabljati s kodo, napisano v C – to popoln nadzor nad platformo, na kateri se izvaja.
  • koda, ustvarjena po prevajanju, ne zahteva veliko sistemskih gradnikov in jo je mogoče priklicati iz neke zagonske kode na enostaven način – preprosto jo je izvesti.
  • stavki in izrazi v C se običajno dobro preslikajo v zaporedja navodil za ciljni procesor in zato je sistemska poraba v času izvajanja nizka – izvajanje je hitro.
  • s svojim bogatim naborom operatorjev lahko C uporablja mnoge funkcije ciljnih procesorjev. Kjer ima določena CPE bolj ezoterična navodila, je mogoče sestaviti jezikovno različico z morda intrinzičnimi funkcijami za izkoriščanje teh navodil – uporablja lahko praktično vse funkcije ciljne CPE.
  • C omogoča preprosto prekrivanje struktur na bloke dvojiških podatkov, kar omogoča razumevanje podatkov, navigacijo in spreminjanje – lahko zapiše podatkovne strukture, celo datotečne sisteme.
  • C podpira bogat nabor operatorjev, vključno z bitno manipulacijo, za celoštevilsko aritmetiko in logiko ter morda različne velikosti števil s plavajočo vejico – lahko učinkovito obdela ustrezno strukturirane podatke.
  • C je dokaj majhen jezik z le peščico stavkov in brez preveč gradnikov, ki ustvarjajo obsežno ciljno kodo – je razumljiv.
  • C ima neposreden nadzor nad dodelitvijo in ponovno dodelitvijo (sprostitvijo) pomnilnika, kar zagotavlja razumno učinkovitost in predvidljiv časovni razpored za operacije obravnavanja pomnilnika, brez kakršnih koli skrbi za občasne dogodke zbiranja smeti ob zaustavitvi sveta – ima predvidljivo delovanje.
  • C dovoljuje uporabo in izvajanje različnih shem dodelitve pomnilnika, vključno s tipičnima funkcijama malloc in free, bolj izpopolnjen mehanizem z arenami ali različico za jedro operacijskega sistema, ki lahko ustreza neposrednemu dostopu do pomnilnika (DMA), uporabi znotraj obdelovalnikov prekinitev ali integrirano s sistemom navideznega pomnilnika.
  • odvisno od povezovalnika in okolja lahko koda C kliče tudi knjižnice, napisane v zbirnem jeziku, in se lahko kliče iz zbirnega jezika – dobro deluje z drugo nizkopnivojsko kodo.
  • C in njegovi dogovori o klicanju ter povezovalnikove strukture se običajno uporabljajo v povezavi z drugimi visokonivojskimi jeziki, pri čemer so podprti klici v C in iz C – dobro deluje z drugo visokonivojsko kodo.
  • C ima zelo zrel in širok ekosistem, vključno s knjižnicami, ogrodji, odprtokodnimi prevajalniki, razhroščevalniki in pripomočki, in je de facto standard. Verjetno gonilniki že obstajajo v C ali pa obstaja podobna arhitektura CPE kot zaledje prevajalnika C, zato je manj spodbude za izbiro drugega jezika.

Računsko zahtevne knjižnice

[uredi | uredi kodo]

C programerjem omogoča ustvarjanje učinkovitih izvedb algoritmov in podatkovnih struktur, ker je plast abstrakcije iz strojne opreme tanka, njeni stroški pa nizki, kar je pomembno merilo za računalniško intenzivne programe. Na primer, GNU Multiple Precision Arithmetic Library (GMP), GNU Scientific Library (GSL), Mathematica in MATLAB so v celoti ali delno napisani v C. Mnogi jeziki podpirajo klicanje knjižniških funkcij v C. Ogrodje NumPy, ki temelji na Pythonu, na primer uporablja C za vidike visoke zmogljivosti in interakcije s strojno opremo.

Računalniške igre so pogosto zgrajene iz kombinacije jezikov. C se je zelo izkazal, zlasti za tiste igre, ki poskušajo doseči najboljšo zmogljivost računalniških platform. Med te igre spada igra Doom iz leta 1993.[68]

C kot vmesni jezik

[uredi | uredi kodo]

C se včasih uporablja kot vmesni jezik pri izvedbah drugih jezikov. Ta pristop se lahko uporablja zaradi prenosljivosti ali udobja. Z uporabo C kot vmesnega jezika dodatni strojnospecifični generatorji kode niso potrebni. C ima nekaj gradnikov, kot so predprocesorski ukazi številk vrstic in neobvezne odvečne vejice na koncu seznamov dodeljevalnikov, ki podpirajo prevajanje ustvarjene kode. Vendar pa so nekatere pomanjkljivosti C-ja spodbudile razvoj drugih jezikov, ki temeljijo na C-ju in so posebej zasnovani za uporabo kot vmesni jeziki, kot je C--. Poleg tega imata sodobna glavna prevajalnika GCC in LLVM vmesno reprezentacijo, ki ni C, in ti prevajalniki podpirajo čelne dele za mnoge jezike, vključno s C.

Razvijanje drugih jezikov

[uredi | uredi kodo]

Posledica široke razpoložljivosti in učinkovitosti jezika C je, da so prevajalniki, knjižnice in tolmači drugih programskih jezikov pogosto izvedeni v C.[69] Na primer, referenčne izvedbe Pythona,[70] Perla,[71] Rubyja[72] in PHP[73] so napisane v C.

Spletni razvoj

[uredi | uredi kodo]

V preteklosti se je C včasih uporabljal za spletni razvoj z uporabo skupnega prehodnega vmesnika (CGI) kot »prehoda« za informacije med spletno aplikacijo, spletnim strežnikom in spletnim brskalnikom.[74] C je bil morda izbran namesto tolmačenih jezikov zaradi njegove hitrosti, stabilnosti in skoraj univerzalne dostopnosti.[75] Ni več običajna praksa, da se spletni razvoj izvaja v C,[76] zanj so priljubljeni mnogi drugi jeziki. Aplikacije, kjer se spletni razvoj na osnovi C nadaljuje, vključujejo konfiguracijske strani HTTP na usmerjevalnikih, napravah interneta stvari (IoT) in podobno, čeprav imajo tudi tukaj nekateri projekti dele v višjenivojskih jezikih, npr. uporaba jezika Lua znotraj odkprtokodnega projekta OpenWRT.

Razvoj spletnih strežnikov

[uredi | uredi kodo]

Dva najbolj priljubljena spletna strežnika, strežnik HTTP Apache in nginx, sta oba napisana v C. Ti spletni strežniki komunicirajo z operacijskim sistemom, poslušajo vrata TCP za zahteve HTTP in nato strežejo statično spletno vsebino ali povzročijo izvajanje obdelave drugih jezikov za 'upodabljanje' vsebine, kot je PHP, ki je sam primarno napisan v C. C-jev surovi pristop omogoča konstrukcijo teh visoko zmogljivih programskih sistemov.

Aplikacije za končne uporabnike

[uredi | uredi kodo]

C se pogosto uporablja tudi za izvajanje aplikacij za končne uporabnike.[7] Vendar pa je takšne aplikacije mogoče napisati tudi v novejših višjenivojskih jezikih.

Omejitve

[uredi | uredi kodo]

moč zbirnega jezika in priročnost ...
zbirnega jezika

— Dennis Ritchie[77]

Čeprav je bil C priljubljen, vpliven in izjemno uspešen, ima naslednje slabosti:

Za nekatere namene so bili sprejeti omejeni slogi C-ja, na primer MISRA C ali CERT C, da bi zmanjšali možnost hroščev. Podatkovne zbirke, kot je CWE, poskušajo prešteti načine, na katere ima C itd. ranljivosti, skupaj s priporočili za ublažitev.

Obstajajo orodja, ki lahko ublažijo nekatere pomanjkljivosti. Sodobni prevajalniki C vključujejo preverjanja, ki lahko ustvarijo opozorila za pomoč pri prepoznavanju mnogih morebitnih hroščev.

Nekatere od teh pomanjkljivosti so spodbudile izdelavo drugih jezikov.

Sorodni jeziki

[uredi | uredi kodo]
Primerjava jezikov C in C++ s kodo programa Pozdravljen, svet
Graf indeksa TIOBE kaže primerjavo med priljubljenostjo različnih programskih jezikov[84]

Razvoj jezika C je zelo vplival na druge programske jezike, še posebej na C++, ki se je razvil kot razširitev C-ja.[85] Mnogo kasnejših jezikov si je neposredno ali posredno sposodilo določene gradnike C-ja, na primer: C#, Unixova C shell, D, Go, java, JavaScript (vključno s prevajalniki od vira do vira), JS++, julia, Limbo, LPC, nim, Objective-C, Perl, PHP, pike, processing, Python, Ruby, Rust, seed7, Swift, Vala, Verilog, SystemVerilog (strojni opisni (HDL) in strojni preverljivostni jezik (HVL))[5] in zig. Najprodornejši vpliv je bil skladenjski. Ti jeziki so dobili mnogo svojih nadzornih struktur in drugih osnovnih gradnikov od C-ja. Večina od njih, kjer je Python največja izjema, je tudi skladenjsko zelo podobnih C-ju v splošnem in so usmerjeni na združevanje prepoznavnih izrazov in stavkov skladnje jezika C z osnovo sistemov tipa, podatkovnih modelov in semantiko, ki je lahko popolnoma drugačna. Skladnja pri teh jezikih včasih vključuje tudi enakovredne preproste nadzorne strukture.[86][p][87][88][q]

Java še zdaleč ni prvi poskus prenosljivosti. Ne pozabimo, da je bila prvotna motivacija za jezik C v zgodnjih 1970-ih ustvariti prenosni računalniški jezik. Teorija je bila, da bi lahko programer vzel program, napisan v C, in ga lahko pognal na različnih računalnikih preprosto s ponovnim prevajanjem izvorne kode. In v ta namen je bil C izjemno uspešen. Imam veliko programov, ki jih je mogoče prevesti in izvajati v sistemu Windows, na delovnih postajah Unix, ki temeljijo na Intelu, in celo na strežnikih Sun Ultra-SPARC. Ena od prednosti jave pred C-jem naj bi bila ta, da bi se programi lahko selili iz računalnika v računalnik, ne da bi jih bilo treba znova prevesti. Čeprav prenosljivost deluje večino časa, java ni in nikoli ne bo zamenjava za C ali njegovega naslednika C++.

Obstaja več tolmačev C ali skoraj C, vključno s Ch in CINT, ki jih je mogoče uporabiti tudi za skriptiranje.

Ko so objektno usmerjeni programski jeziki postali priljubljeni, sta bila C++ in Objective-C dve različni razširitvi jezika C, ki sta zagotavljali objektno usmerjene zmogljivosti. Oba jezika sta bila prvotno izvedena kot prevajalnika od vira do vira – izvorna koda je bila prevedena v C in nato prevedena s prevajalnikom C.[90]

Programski jezik C++ (prvotno imenovan »C z razredi«) je zasnoval Bjarne Stroustrup kot pristop k zagotavljanju objektno usmerjene funkcionalnosti s skladnjo, podobno C-ju.[91] C++ dodaja večjo moč tipizacije, doseg in druga orodja, uporabna pri objektno usmerjenem programiranju, ter omogoča generično programiranje prek predlog (templates). C++ je trenutno skoraj supermnožica C in podpira večino C, z nekaj izjemami.[91]

Objective-C je bil prvotno zelo »tanek« sloj na vrhu C-ja in ostaja stroga supermnožica C-ja, ki dovoljuje objektno usmerjeno programiranje z uporabo hibridne paradigme dinamične/statične tipizacije.[92][93][94] Objective-C izpeljuje svojo skladnjo iz C-ja in Smalltalka: skladnja, ki vključuje predprocesiranje, izraze, označitve funkcij in klice funkcij, je podedovana iz C-ja, skladnja za objektno usmerjene funkcije pa je bila prvotno vzeta iz Smalltalka.

Poleg C++ in Objective-C so Ch, Cilk in Unified Parallel C (UPC) skoraj supermnožice C-ja.

C se rabi tudi kot posredni jezik za druge jezike[95] ter za ustvarjanje standardnih knjižnic in izvajalnih sistemov za višjenivojske jezike, kot je npr. CPython.[96]

Glej tudi

[uredi | uredi kodo]

Opombe

[uredi | uredi kodo]
  1. Thompson je leta 1972 na kratko poskušal izdelati sistem, kodiran v zgodnji različici C (pred strukturami), vendar je zamisel opustil ( Ritchie (1993)).
  2. Izvedli so več poskusov, da bi bilo v C postopanje z znakovnimi nizi manj nagnjeno k napakam. Ena strategija je dodajanje varnejših in uporabnejših funkcij, kot sta strdup in strlcpy, in izogibanje nevarnim funkcijam, kot je npr. gets. Druga je dodajanje objektno usmerjene ovojnice okrog C-jevskih nizov, tako da so možna le varna klicanja.
  3. Z razliko od C-ja imata npr. Fortran in BASIC omejeni model, ki ne vsebuje kazalcev. V paskalu so kazalci dinamični objekti, sam jezik pa ne dovoljuje kazalčne aritmetike.
  4. Izvirna vzorčna koda se bo prevedla v večini sodobnih prevajalnikov, ki niso v načinu stroge standardne skladnosti, vendar ni v celoti v skladu z zahtevami C89 ali C99. C99 pravzaprav zahteva, da se ustvari diagnostično sporočilo.
  5. Funkcija main ima pravzaprav dva argumenta, int argc in char *argv[], ki ju je mogoče uporabiti za obdelavo argumentov ukazne vrstice. Standard ISO C (§ 5.1.2.2.1) zahteva veljavnost obeh oblik funkcije main, brez parametrov: int main() oziroma: int main(void), ali z dvema parametroma: int main(int argc, char *argv[]), kar je izjema le za to funkcijo.
  6. Če se navede funkcija main samo s prvim parametrom:
    int main(int argc) { /* ... */ }
    

    bo prevajalnik javil opozorilo, če pa se navede samo z drugim parametrom:

    int main(char *argv[]) { /* ... */ }
    

    bo javil napako, ker mora sicer funkcija main biti brez parametrov ali imeti strogo dva parametra, prvi parameter pa mora biti tipa int.

  7. V nekaterih operacijskih sistemih, še posebej v starejših različicah Unixa, ima lahko funkcija main tri parametre v obliki:
    int main(int argc, char *argv[], char *envp[]) { /* ... */ }
    

    kjer je tretji parameter kazalec na vektor predvsem imen spremenljivk okolja. Tretji parameter envp v funkciji main pa ni v skladu s standardom POSIX in se podatki o obstoječih spremenljivkah okolja pridobivajo z drugimi funkcijami, na primer getenv. Prevajalnik gcc za Linux na primer to možnost podpira.

  8. Argumentoma funkcije main se lahko dodatno določi konstantnost:
    int main(const int argc, const char **argv) { /* ... */ }
    int main(const int argc, const char * const *argv) { /* ... */ }
    

    To je priporočljivo, saj je možno zapisat na primer:

    strcpy(argv[0], "brezveze");
    

    kar želi spremeniti vrednost prvega znakovnega niza drugega argumenta, označenega kot konstantnega (./ime_programa).

  9. Pred standardom C99 je bil na koncu funkcije main zahtevan eksplicitni ukaz return 0.
  10. 10,0 10,1 10,2 10,3 10,4 Najmanjši obsegi do (na primer [−127, +127]) izhajajo iz različnih predstavitev celih števil, ki jih je dovolil standard (eniški komplement, predznačna velikost, dvojiški komplement).(Glej ISO/IEC 9899:1999 specification, TC3 (PDF) (v angleščini), str. 22, § 5.2.4.2.1 Sizes of integer types <limits.h>) Vendar pa večina platform uporablja dvojiški komplement, kar za te implementacije implicira obseg v obliki do za , na primer [−128, +127] (SCHAR_MIN = −128 in SCHAR_MAX = +127) za 8-bitni signed char. Od standarda C23 naprej je edina dovoljena predstavitev dvojiški komplement, zato se vrednosti gibljejo od do .(Glej ISO/IEC 9899:2023 specification draft (PDF) (v angleščini), str. 41, § 6.2.6 Representations of types)
  11. Samodejno razglašen na double v funkciji printf().
  12. %lf za funkcijo scanf().
  13. 13,0 13,1 Velike črke se v izpisu razlikujejo od malih. Določilniki za velike črke ustvarijo vrednosti v velikih črkah, za male črke pa v malih (%A, %E, %F, %G ustvarijo vrednosti, kot so INF, NAN in E (eksponent) v velikih črkah).
  14. Mnogi dereferenčni operator * pišejo levostično s podatkovnim tipom in ne z imenom kazalca, pa redkeje tudi nestično od obeh:
    podatkovni_tip* ime_kazalca;
    podatkovni_tip * ime_kazalca;
    
  15. Koda za funkcijo print_array (ni prikazana) se tudi rahlo razlikuje.
  16. Če se pozna programski jezik C, se pozna ukaz for. Ukaz for v jeziku Perl je skladenjsko enakovreden ukazu for v C-ju.
  17. Brez kazalcev bi C izgledal skoraj enako kot PHP

Sklici

[uredi | uredi kodo]
  1. http://www.bell-labs.com/usr/dmr/www/chist.html
  2. ISO/IEC JTC1/SC22/WG14 (5. april 2023), C - Project status and milestones (v angleščini), pridobljeno 9. avgusta 2023
  3. »N3221 – Editor's Report, Post January 2024 Strasbourg France Meeting«, ISO/IEC JTC1/SC22/WG14 (v angleščini), Open Standards, 21. februar 2024, pridobljeno 24. maja 2024
  4. http://cm.bell-labs.com/cm/cs/who/dmr/chist.html
  5. 5,0 5,1 Verilog HDL (and C) (PDF) (v angleščini), The Research School of Computer Science at the Australian National University, 3. junij 2010, arhivirano iz prvotnega spletišča (PDF) dne 6. novembra 2013, pridobljeno 19. avgusta 2013, 1980s: ; Verilog prvič predstavljen ; na Verilog je vplival programski jezik C
  6. 6,0 6,1 Lawlis (1997).
  7. 7,0 7,1 Munoz, Daniel, »After All These Years, the World is Still Powered by C Programming«, Toptal Engineering Blog (v angleščini), pridobljeno 17. novembra 2023
  8. »C Language Drops to Lowest Popularity Rating«, Developer.com (v ameriški angleščini), 9. avgust 2016, arhivirano iz prvotnega spletišča dne 22. avgusta 2022, pridobljeno 1. avgusta 2022
  9. Stewart (2000a).
  10. 10,0 10,1 10,2 10,3 10,4 10,5 10,6 10,7 10,8 Ritchie (1993).
  11. Giannini; Code Fighter, Inc.; Univerza Columbia (2004), str. 164.
  12. 12,0 12,1 Programming Language Popularity (v angleščini), 2009, arhivirano iz prvotnega spletišča dne 16. januarja 2009, pridobljeno 16. januarja 2009
  13. 13,0 13,1 TIOBE Programming Community Index (v angleščini), 2009, arhivirano iz prvotnega spletišča dne 2. julija 2013, pridobljeno 6. maja 2009
  14. Ward (1983).
  15. Prinz; Crawford (2005), str. 3.
  16. 16,0 16,1 »WG14-N3132 : Revised C23 Schedule« (PDF), open-std.org (v angleščini), 4. junij 2023, arhivirano (PDF) iz spletišča dne 9. junija 2023
  17. TIOBE Index for September 2024 (v angleščini), arhivirano iz spletišča dne 18. septembra 2024, pridobljeno 20. septembra 2024
  18. Johnson; Kernighan (1983), str. 52.
  19. 19,0 19,1 »History of C«, en.cppreference.com (v angleščini), 19. avgust 2014, arhivirano iz spletišča dne 29. maja 2018, pridobljeno 10. novembra 2014
  20. 20,0 20,1 Stewart (2000b).
  21. Van der Linden (1994).
  22. Ritchie (1979).
  23. Ritchie, Dennis, BCPL to B to C (v angleščini), arhivirano iz spletišča dne 12. decembra 2019, pridobljeno 10. septembra 2019
  24. 24,0 24,1 24,2 24,3 24,4 Jensen (2020).
  25. 25,0 25,1 Johnson; Ritchie (1978).
  26. McIlroy (1987).
  27. Stallings (2012), str. 91.
  28. A Brief History of C (v angleščini)
  29. 29,0 29,1 29,2 Kernighan; Ritchie (1978).
  30. »C manual pages«, FreeBSD Miscellaneous Information Manual (v angleščini) (FreeBSD 13.0 izd.), 30. maj 2011, arhivirano iz spletišča dne 21. januarja 2021, pridobljeno 15. januarja 2021 [1] Arhivirano 2021-01-21 na Wayback Machine.
  31. 31,0 31,1 31,2 31,3 Kernighan; Ritchie (1988).
  32. Stroustrup (2002).
  33. Rationale for American National Standard for Information Systems – Programming Language – C (v angleščini), arhivirano iz prvotnega spletišča dne 17. julija 2024, pridobljeno 17. julija 2024
  34. C Integrity (v angleščini), Mednarodna organizacija za standardizacijo, 30. marec 1995, arhivirano iz spletišča dne 25. julija 2018, pridobljeno 24. julija 2018
  35. »JTC1/SC22/WG14 – C«, Domača stran (v angleščini), ISO/IEC, arhivirano iz spletišča dne 12. februarja 2018, pridobljeno 2. junija 2011
  36. 36,0 36,1 Lesser known C features (v angleščini), pridobljeno 30. decembra 2014
  37. Binstock (2011).
  38. »WG14-N3220 : Working Draft, C2y« (PDF), open-std.org (v angleščini), 21. februar 2024, arhivirano (PDF) iz spletišča dne 26. februarja 2024
  39. »TR 18037: Embedded C« (PDF), open-std.org (v angleščini), ISO / IEC, 4. maj 2006, arhivirano (PDF) iz spletišča dne 25. februarja 2021, pridobljeno 26. julija 2011
  40. Harbison; Steele (2002).
  41. 41,0 41,1 Kelley; Pohl (1984), str. 61.
  42. WG14 N1570 Committee Draft — April 12, 2011 (PDF) (v angleščini), pridobljeno 13. oktobra 2014
  43. 43,0 43,1 »C Language Reference« (PDF), www.openwatcom.org (v angleščini), arhivirano iz prvotnega spletišča (PDF) dne 13. maja 2011, pridobljeno 31. decembra 2010
  44. 10 Common Programming Mistakes in C++ (v angleščini), Cs.ucr.edu, pridobljeno 26. junija 2009
  45. Schultz (2004), str. 20.
  46. 46,0 46,1 »The form of a C program«, The GNU C Programming Tutorial (v angleščini), pridobljeno 10. oktobra 2014
  47. WG14 N1256 Committee Draft — September 7, 2007 (ISO/IEC 9899:TC3) (PDF) (v angleščini), pridobljeno 13. oktobra 2014 Str. 12, klavzula 1, § 5.1.2.2.1 Program startup.
  48. 48,0 48,1 48,2 48,3 48,4 48,5 48,6 48,7 Klemens (2014).
  49. Larson (1996b).
  50. Feuer; Gehani (1982).
  51. 51,00 51,01 51,02 51,03 51,04 51,05 51,06 51,07 51,08 51,09 ISO/IEC 9899:1999 specification, TC3 (PDF) (v angleščini), str. 22, § 5.2.4.2.1 Sizes of integer types <limits.h>
  52. WG14 N1256.... Str. 37, klavzula 3, § 6.2.6 Representations of types — §6.2.6.1 General.
  53. ISO/IEC 9899:1999 specification, TC3 (PDF) (v angleščini), str. 37, § 6.2.6.1 Representations of types – General
  54. 54,0 54,1 54,2 54,3 WG14 N1256.... Str. 22, klavzula 1, § 5.2.4.2.1 Sizes of integer types <limits.h>.
  55. 55,0 55,1 55,2 55,3 55,4 55,5 ISO/IEC 9899:1999 specification, TC3 (PDF) (v angleščini), str. 56, § 6.4.4.1 Integer constants
  56. 64-Bit Programming Models: Why LP64? (v angleščini), The Open Group, 1997, pridobljeno 9. novembra 2011
  57. WG14 N1256.... Str. 114, klavzula 1, § 6.7.5 Declarators.
  58. WG14 N1256.... Str. 126, klavzula 10, § 6.7.8 Initialization.
  59. WG14 N1256.... Str. 254, klavzula 3, § 7.17 Common definitions <stddef.h>: »NULL..., ki se razširi na izvedbeno določeno kazalčno konstanto...«.
  60. gcc ima za ta namen na primer makro _FORTIFY_SOURCE. Security Features: Compile Time Buffer Checks (FORTIFY_SOURCE) (v angleščini), fedoraproject.org, arhivirano iz spletišča dne 7. januarja 2007, pridobljeno 5. avgusta 2012
  61. Aimsiriwong (2016).
  62. Raymond (1996), str. 432.
  63. Summit (1995b), Question 6.23.
  64. Summit (1995b), Question 7.28.
  65. 65,0 65,1 »Struct memory layout in C«, Stack Overflow (v angleščini), 1. maj 2010
  66. »Man Page for lint (freebsd Section 1)«, unix.com (v angleščini), 24. maj 2001, pridobljeno 15. julija 2014[mrtva povezava]
  67. Dale; Weems (2014).
  68. »Development of Doom«, DoomWiki.org (v angleščini), 2. marec 2025, pridobljeno 2. marca 2025
  69. »C - the mother of all languages«, ICT Academy at IITK (v ameriški angleščini), 13. november 2018, arhivirano iz spletišča dne 31. maja 2021, pridobljeno 11. oktobra 2022
  70. »1. Extending Python with C or C++ — Python 3.10.7 documentation«, docs.python.org (v angleščini), arhivirano iz spletišča dne 5. novembra 2012, pridobljeno 11. oktobra 2022
  71. »An overview of the Perl 5 engine | Opensource.com«, opensource.com (v angleščini), arhivirano iz spletišča dne 26. maja 2022, pridobljeno 11. oktobra 2022
  72. »To Ruby From C and C++«, www.ruby-lang.org (v angleščini), arhivirano iz spletišča dne 12. avgusta 2013, pridobljeno 11. oktobra 2022
  73. »What is PHP? How to Write Your First PHP Program«, freeCodeCamp.org (v angleščini), 3. avgust 2022, arhivirano iz spletišča dne 4. avgusta 2022, pridobljeno 11. oktobra 2022
  74. Kim (1995).
  75. Dowling (2005).
  76. Perkins (2013).
  77. Metz (2011).
  78. Internet Security Research Group, »What is memory safety and why does it matter?«, Prossimo (v angleščini), pridobljeno 3. marca 2025
  79. corob-msft (2022).
  80. »Pragmas (The C Preprocessor)«, gcc.gnu.org (v angleščini), arhivirano iz spletišča dne 17. junija 2002, pridobljeno 24. septembra 2022
  81. »Pragmas«, Intel (v angleščini), arhivirano iz spletišča dne 10. aprila 2022, pridobljeno 10. aprila 2022
  82. »In praise of the C preprocessor«, apenwarr.ca (v angleščini), pridobljeno 9. julija 2023
  83. Roberts (1989).
  84. McMillan, Robert (1. avgust 2013). »Is Java Losing Its Mojo?«. Wired (v angleščini). Arhivirano iz spletišča dne 15. februarja 2017. Pridobljeno 5. junija 2017.
  85. O'Regan (2015).
  86. More Control Structures (v angleščini)
  87. Simpson (2013).
  88. Learn C (v angleščini), arhivirano iz prvotnega spletišča dne 13. septembra 2015
  89. Garfinkel (2001).
  90. Rauchwerger (2004).
  91. 91,0 91,1 Stroustrup (1993).
  92. Write Objective-C Code (v angleščini), apple.com, 23. april 2013, pridobljeno 22. decembra 2013
  93. Dawkins (2006).
  94. Lee (2013).
  95. »How EiffelStudio Compiles«, docs.eiffel.com (v angleščini), pridobljeno 2. februarja 2015
  96. »Extending Python with C or C++«, docs.python.org (v angleščini), pridobljeno 2. februarja 2015

Nadaljnje branje

[uredi | uredi kodo]

Zunanje povezave

[uredi | uredi kodo]

Spletni prevajalniki

[uredi | uredi kodo]
url
 
barvanje
skladnje
različni
urejevalniki
preverjanje
prevoda
izvajanje
kode
podprti
jeziki
izhod v zbirnem
jeziku
prevajalnik(i)
 
opombe
 
codepad.org Da Da Ne Da C, C++, ... Ne (angleško)
godbolt.org Da Ne Da Ne C, C++ Da gcc, clang (angleško)
ideone.com Da Ne Da Da C, C++ Ne gcc, clang (angleško)
repl.it Da Ne Da Da C, C++, C#, ... Ne gcc (angleško)
rextester.com Da Da Da C, C++, C#, ... Ne gcc, clang, vs C (angleško)
stacked-crooked.com Da Ne Da Da C, C++ Ne gcc, g++ (angleško)
comeaucomputing.com Arhivirano 2016-04-16 na Wayback Machine. C, C++ [mrtva povezava]
compilr.com Arhivirano 2016-03-16 na Wayback Machine. [mrtva povezava]
delorie.com [mrtva povezava]
melpon.org Arhivirano 2016-04-11 na Wayback Machine. Da Da Da C, C++, C#, ... Ne gcc, clang [mrtva povezava]