C (ohjelmointikieli)

Kokeneet kirjoittajat eivät ole vielä tarkistaneet sivun nykyistä versiota, ja se voi poiketa merkittävästi 4. elokuuta 2022 tarkistetusta versiosta . tarkastukset vaativat 3 muokkausta .
C
Kieliluokka menettelyllinen
Toteutustyyppi koottu
Esiintyi 1972
Tekijä Dennis Ritchie
Kehittäjä Bell Labs , Dennis Ritchie [1] , US National Standards Institute , ISO ja Ken Thompson
Tiedostotunniste _ .c— kooditiedostot, .h— otsikkotiedostot
Vapauta ISO/IEC 9899:2018 ( 5. heinäkuuta 2018 )
Tyyppijärjestelmä staattinen heikko
Tärkeimmät toteutukset GCC , Clang , TCC , Turbo C , Watcom , Oracle Solaris Studio C, Pelles C
Murteet "K&R" C ( 1978 )
ANSI C ( 1989 )
C99 ( 1999 )
C11 ( 2011 )
Vaikutettu BCPL , B
vaikutti C++ , Objective-C , C# , Java , Nim
OS Microsoft Windows ja Unix kaltainen käyttöjärjestelmä
 Mediatiedostot Wikimedia Commonsissa
ISO/IEC 9899
Tietotekniikka — Ohjelmointikielet — C
Kustantaja Kansainvälinen standardointijärjestö (ISO)
Verkkosivusto www.iso.org
Toimikunta (kehittäjä) ISO/IEC JTC 1/SC 22
komitean verkkosivuilla Ohjelmointikielet, niiden ympäristöt ja järjestelmäohjelmistoliitännät
ISS (ICS) 35.060
Nykyinen painos ISO/IEC 9899:2018
Edelliset painokset ISO/IEC 9899:1990/COR2:1996
ISO/IEC 9899:1999/COR3:2007
ISO/IEC 9899:2011/COR1:2012

C ( latinan kirjaimesta C , englannin kieli ) on yleiskäyttöinen käännetty staattisesti kirjoitettu ohjelmointikieli , jonka Bell Labsin työntekijä Dennis Ritchie kehitti vuosina 1969-1973 Bee -kielen kehitykseksi . Se kehitettiin alun perin toteuttamaan UNIX - käyttöjärjestelmää , mutta on sittemmin siirretty monille muille alustoille. Suunnittelultaan kieli sopii tiiviisti tyypillisiin koneen ohjeisiin ja on löytänyt käyttöä projekteissa, jotka olivat syntyperäisiä assembly-kielelle , mukaan lukien sekä käyttöjärjestelmät että erilaiset sovellusohjelmistot erilaisille laitteille supertietokoneista sulautettuihin järjestelmiin . C-ohjelmointikielellä on ollut merkittävä vaikutus ohjelmistoteollisuuden kehitykseen, ja sen syntaksista tuli perusta sellaisille ohjelmointikielille kuin C++ , C# , Java ja Objective-C .

Historia

C-ohjelmointikieli kehitettiin vuosina 1969-1973 Bell Labsissa , ja vuoteen 1973 mennessä suurin osa UNIX - ytimestä , joka oli alun perin kirjoitettu PDP-11 /20-asentajalla, oli kirjoitettu uudelleen tälle kielelle. Kielen nimestä tuli looginen jatko vanhalle kielelle " Bi " [a] , jonka monet piirteet otettiin perustaksi.

Kielen kehittyessä se standardoitiin ensin nimellä ANSI C , ja sitten ISO :n kansainvälinen standardointikomitea hyväksyi tämän standardin nimellä ISO C, joka tunnetaan myös nimellä C90. C99-standardi lisäsi kieleen uusia ominaisuuksia, kuten muuttuvapituisia taulukoita ja rivifunktioita. Ja C11 -standardissa kieleen lisättiin streamien toteutus ja tuki atomityypeille. Siitä lähtien kieli on kuitenkin kehittynyt hitaasti, ja vain C11-standardin bugikorjaukset pääsivät C18-standardiin.

Yleistä tietoa

C-kieli suunniteltiin järjestelmän ohjelmointikieleksi, jolle voitiin luoda yhden vaiheen kääntäjä . Myös vakiokirjasto on pieni. Näiden tekijöiden seurauksena kääntäjiä on suhteellisen helppo kehittää [2] . Siksi tämä kieli on saatavilla useilla alustoilla. Lisäksi kieli on matalatasoisuudestaan ​​huolimatta keskittynyt siirrettävyyteen. Eri tietokonearkkitehtuureille voidaan kääntää kielistandardin mukaisia ​​ohjelmia.

Kielen tavoitteena oli helpottaa suurten ohjelmien kirjoittamista minimaalisilla virheillä assembleriin verrattuna, noudattaen prosessiohjelmoinnin periaatteita , mutta välttäen kaikkea, mikä aiheuttaisi korkean tason kielille ominaisia ​​lisäkustannuksia.

C:n tärkeimmät ominaisuudet:

Samaan aikaan C:stä puuttuu:

Osa puuttuvista ominaisuuksista voidaan simuloida sisäänrakennetuilla työkaluilla (esimerkiksi korutiineja voidaan simuloida käyttämällä setjmpjalongjmp -toimintoja ), osa lisätään kolmannen osapuolen kirjastojen avulla (esimerkiksi moniajo- ja verkkotoimintojen tukemiseksi voit käyttää kirjastot pthreads , sockets ja vastaavat, on kirjastoja, jotka tukevat automaattista roskienkeräystä [3] ), osa on toteutettu joissakin kääntäjissä kielilaajennuksina (esimerkiksi sisäkkäiset funktiot GCC :ssä ). On olemassa hieman hankala, mutta varsin toimiva tekniikka, joka mahdollistaa OOP -mekanismien toteuttamisen C:ssä [4] , joka perustuu C:n osoittimien todelliseen polymorfismiin ja osoittimien tukeen tämän kielen funktioihin. Tähän malliin perustuvat OOP-mekanismit on toteutettu GLib -kirjastossa ja niitä käytetään aktiivisesti GTK+ -kehyksessä . GLib tarjoaa perusluokan GObject, mahdollisuuden periä yhdestä luokasta [5] ja toteuttaa useita rajapintoja [6] .

Käyttöönoton jälkeen kieli otettiin hyvin vastaan, koska se mahdollisti kääntäjien nopean luomisen uusille alustoille ja antoi ohjelmoijille mahdollisuuden olla melko tarkkoja ohjelmiensa suorittamisessa. Koska C-ohjelmat olivat lähellä matalan tason kieliä, ne toimivat tehokkaammin kuin monilla muilla korkean tason kielillä kirjoitetut, ja vain käsin optimoitu kokoonpanokielikoodi pystyi ajamaan vielä nopeammin, koska se antoi täyden hallinnan koneeseen. Tähän mennessä kääntäjien kehitys ja prosessorien monimutkaisuus ovat johtaneet siihen, että käsin kirjoitetulla kokoonpanokoodilla (lukuun ottamatta ehkä hyvin lyhyitä ohjelmia) ei ole käytännössä mitään etua kääntäjien luomaan koodiin verrattuna, kun taas C on edelleen yksi parhaista. tehokkaita korkean tason kieliä.

Syntaksi ja semantiikka

Tokenit

Kielten aakkoset

Kielessä käytetään kaikkia latinalaisten aakkosten merkkejä , numeroita ja joitain erikoismerkkejä [7] .

Aakkosten koostumus [7]
Latinalaisen aakkosten merkit

A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y, Z
a, b, c, , , , , , d_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _efghijklmnopqrstuvwxyz

Numerot 0, 1, 2, 3, 4, 5, 6, 7, 8,9
Erikoissymbolit , (pilkku) , ;, . (piste) , +, -, *, ^, & (et-merkki) , =, ~ (tilde) , !, /, <, >, (, ), {, }, [, ], |, %, (heittomerkki)? , (lainausmerkit) , (kaksoispiste) , ( alaviiva ) ) , ,' " : _ \#

Tokenit muodostetaan kelvollisista merkeistä -  ennalta määritetyistä vakioista , tunnisteista ja operaatiomerkeistä . Lekseemit puolestaan ​​ovat osa ilmaisuja ; ja lausekkeet ja operaattorit koostuvat lausekkeista .

Kun ohjelma käännetään C:ksi, ohjelmakoodista poimitaan lekseemejä, joiden pituus on enimmäispituus ja jotka sisältävät kelvollisia merkkejä. Jos ohjelma sisältää virheellisen merkin, leksikaalinen analysaattori (tai kääntäjä) tuottaa virheen ja ohjelman kääntäminen on mahdotonta.

Symboli #ei voi olla osa mitään merkkiä, ja sitä käytetään esiprosessorissa .

Tunnisteet

Kelvollinen tunniste  on sana, joka voi sisältää latinalaisia ​​merkkejä, numeroita ja alaviivoja [8] . Tunnisteet annetaan operaattoreille, vakioille, muuttujille, tyypeille ja funktioille.

Avainsanatunnisteita ja sisäänrakennettuja tunnisteita ei voi käyttää ohjelmaobjektin tunnisteina. On myös varattu tunnisteita, joille kääntäjä ei anna virheitä, mutta joista voi tulevaisuudessa muodostua avainsanoja, mikä johtaa yhteensopimattomuuteen.

On vain yksi sisäänrakennettu tunniste - __func__, joka määritellään vakiomerkkijonoksi, joka on implisiittisesti ilmoitettu jokaisessa funktiossa ja joka sisältää sen nimen [8] .

Literaalivakiot

Erityisesti muotoiltuja C:n literaaleja kutsutaan vakioiksi. Literaalivakiot voivat olla kokonaislukuja, reaalilukuja, merkkiä [9] ja merkkijonoa [10] .

Kokonaisluvut asetetaan oletuksena desimaaleina . Jos etuliite on määritetty 0x, se on heksadesimaali . 0-etuliite osoittaa, että numero on oktaali . Suffiksi määrittää vakiotyypin vähimmäiskoon ja määrittää myös, onko numero etumerkitty vai etumerkitmätön. Lopullinen tyyppi on pienin mahdollinen, jossa annettu vakio voidaan esittää [11] .

Tietotyyppien määritysjärjestys kokonaislukuvakioihin niiden arvon mukaan [11]
Suffiksi Desimaalille Oktaali- ja heksadesimaalilukuille
Ei int

long

long long

int

unsigned int

long

unsigned long

long long

unsigned long long

utaiU unsigned int

unsigned long

unsigned long long

unsigned int

unsigned long

unsigned long long

ltaiL long

long long

long

unsigned long

long long

unsigned long long

utai Uyhdessä ltaiL unsigned long

unsigned long long

unsigned long

unsigned long long

lltaiLL long long long long

unsigned long long

utai Uyhdessä lltaiLL unsigned long long unsigned long long
Esimerkkejä reaaliluvun kirjoittamisesta 1.5
Desimaali

muoto

Eksponentin kanssa Heksadesimaali

muoto

1.5 1.5e+0 0x1.8p+0
15e-1 0x3.0p-1
0.15e+1 0x0.cp+1

Reaalilukuvakiot ovat oletusarvoisesti tyyppisiä double. Suffiksia määritettäessä ftyyppi määritetään vakiolle ja floatmääritettäessä or-  . Vakiota pidetään todellisena, jos se sisältää pistemerkin tai kirjaimen tai jos kyseessä on heksadesimaalimerkintä, jossa on etuliite . Desimaalimerkintä voi sisältää eksponentin kirjainten tai . Heksadesimaalimerkinnässä eksponentti määritetään kirjainten jälkeen tai on pakollinen, mikä erottaa todelliset heksadesimaalivakiot kokonaisluvuista. Heksadesimaalimuodossa eksponentti on 2:n potenssi [12] . lLlong doublepP0xeEpP

Merkkivakiot on suljettu lainausmerkkeihin ( '), ja etuliite määrittää sekä merkkivakion tietotyypin että koodauksen, jossa merkki esitetään. C:ssä merkkivakio ilman etuliitettä on tyyppiä int[13] , toisin kuin C++ , jossa merkkivakio on char.

Merkkivakioetuliitteet [13]
Etuliite Tietotyyppi Koodaus
Ei int ASCII
u char16_t 16-bittinen monitavuinen merkkijonokoodaus
U char32_t 32-bittinen monitavuinen merkkijonokoodaus
L wchar_t Leveä merkkijonokoodaus

Merkkijonoliteraalit on suljettu lainausmerkkeihin ja niihin voidaan liittää merkkijonon tietotyyppi ja koodaus. Merkkijonoliteraalit ovat tavallisia taulukoita. Kuitenkin monitavuisissa koodauksissa, kuten UTF-8 , yksi merkki voi sisältää useamman kuin yhden taulukkoelementin. Itse asiassa merkkijonoliteraalit ovat const [14] , mutta toisin kuin C++, niiden tietotyypit eivät sisällä muuntajaa const.

Merkkijonovakioetuliitteet [15]
Etuliite Tietotyyppi Koodaus
Ei char * ASCII tai monitavuinen koodaus
u8 char * UTF-8
u char16_t * 16-bittinen monitavuinen koodaus
U char32_t * 32-bittinen monitavuinen koodaus
L wchar_t * Leveä merkkijonokoodaus

Useita peräkkäisiä välilyönnillä tai rivinvaihdoilla erotettuja merkkijonovakioita yhdistetään käännöksen yhteydessä yhdeksi merkkijonoksi, jota käytetään usein merkkijonon koodin tyylistämiseen erottamalla merkkijonovakion osia eri riveillä luettavuuden parantamiseksi [16] .

Nimetyt vakiot Vakioiden asettamismenetelmien vertailu [17]
Makro #define BUFFER_SIZE 1024
Anonyymi
luettelointi
enum { BUFFER_SIZE = 1024 };
Muuttuja vakiona
_
const int puskurin_koko = 1024 ; extern const int puskurin_koko ;

C-kielessä vakioiden määrittämiseen on tapana käyttää makromäärityksiä, jotka on ilmoitettu preprocessor-direktiivillä [17] : #define

#define vakionimi [ arvo ]

Tällä tavalla lisätty vakio on voimassa laajuudessaan vakion asettamishetkestä ohjelmakoodin loppuun asti tai kunnes annetun vakion vaikutus kumotaan käskyllä #undef:

#undef vakio nimi

Kuten minkä tahansa makron kohdalla, nimetyn vakion tapauksessa vakion arvo korvataan automaattisesti ohjelmakoodissa aina, kun vakion nimeä käytetään. Siksi makron sisällä kokonaislukuja tai reaalilukuja määritettäessä saattaa olla tarpeen määrittää tietotyyppi eksplisiittisesti käyttämällä sopivaa kirjaimellista loppuliitettä, muuten luku on oletuksena tyyppi int, jos kyseessä on kokonaisluku tai tyyppi double , jos kyseessä on todellinen.

Kokonaisluvuille on toinen tapa luoda nimettyjä vakioita - operaattorienummien enum[17] avulla . Tämä menetelmä soveltuu kuitenkin vain tyypeille, jotka ovat pienempiä tai yhtä suuria kuin tyyppi , eikä sitä käytetä vakiokirjastossa [18] . int

On myös mahdollista luoda vakioita muuttujiksi tarkenteen constavulla, mutta toisin kuin kahdessa muussa menetelmässä, tällaiset vakiot kuluttavat muistia, niihin voidaan osoittaa, eikä niitä voida käyttää käännöshetkellä [17] :

  • määrittääksesi bittikenttien koon,
  • asettaaksesi taulukon koon (paitsi vaihtelevan pituiset taulukot),
  • asettaaksesi luetteloelementin arvon,
  • operaattorin arvona case.
Avainsanat

Avainsanat  ovat tunnisteita, jotka on suunniteltu suorittamaan tietty tehtävä käännösvaiheessa tai antamaan vihjeitä ja ohjeita kääntäjälle.

C-kielen avainsanat [19]
Avainsanat Tarkoitus Vakio
sizeof Objektin koon saaminen käännöshetkellä C89
typedef Vaihtoehtoisen nimen määrittäminen tyypille
auto,register Kääntäjän vihjeitä muuttujien tallennuspaikalle
extern Kääntäjän käskeminen etsimään objektia nykyisen tiedoston ulkopuolelta
static Staattisen objektin ilmoittaminen
void Ei arvomerkkiä; osoittimissa tarkoittaa mielivaltaista dataa
char... short_ int_long Kokonaislukutyypit ja niiden koon muuttajat
signed,unsigned Kokonaislukutyyppiset muokkaimet, jotka määrittelevät ne allekirjoitetuiksi tai allekirjoittamattomiksi
float,double Todelliset tietotyypit
const Tietotyypin muuntaja, joka kertoo kääntäjälle, että kyseisen tyypin muuttujat ovat vain luku -tilassa
volatile Kääntäjän käsky muuttamaan muuttujan arvoa ulkopuolelta
struct Tietotyyppi, määritetty rakenteeksi, jossa on joukko kenttiä
enum Tietotyyppi, joka tallentaa yhden kokonaislukuarvojen joukosta
union Tietotyyppi, joka voi tallentaa tietoja eri tietotyyppien esityksiin
do. for_while Loop-lauseet
if,else Ehdollinen operaattori
switch. case_default Operaattorin valinta kokonaislukuparametrin mukaan
break,continue Loop Break -lauseet
goto Ehdoton hyppyoperaattori
return Paluu funktiosta
inline Sisäinen funktion ilmoitus C99 [20]
restrict Osoittimen ilmoittaminen, joka viittaa muistilohkoon, johon mikään muu osoitin ei viittaa
_Bool[b] boolen tietotyyppi
_Complex[c] ,_Imaginary [d] Kompleksilukulaskelmissa käytetyt tyypit
_Atomic Tyyppimuuntaja, joka tekee siitä atomisen C11
_Alignas[e] Tietotyypin tavutasauksen nimenomainen määrittäminen
_Alignof[f] Tietyn tietotyypin kohdistuksen hakeminen käännöshetkellä
_Generic Yhden arvojoukon valitseminen käännöshetkellä ohjatun tietotyypin perusteella
_Noreturn[g] Osoittaa kääntäjälle, että funktio ei voi päättyä normaalisti (eli return)
_Static_assert[h] Käännöshetkellä tarkistettavien väitteiden määrittäminen
_Thread_local[i] Säikeen paikallismuuttujan ilmoittaminen
Varatut tunnisteet

Avainsanojen lisäksi kielistandardi määrittelee varatut tunnisteet, joiden käyttö voi johtaa yhteensopimattomuuteen standardin tulevien versioiden kanssa. Kaikki paitsi avainsanasanat, jotka alkavat alaviivalla ( _), jota seuraa joko iso kirjain ( A- Z) tai toinen alaviiva [21] , on varattu . C99- ja C11-standardeissa joitain näistä tunnisteista käytettiin uusien kielten avainsanoihin.

_Tiedoston laajuudessa alaviivalla ( ) [21] alkavien nimien käyttö on varattu , eli se on sallittua nimetä tyyppejä, vakioita ja muuttujia, jotka on ilmoitettu käskylohkon sisällä, esimerkiksi funktioiden sisällä, alaviivalla.

Varattuina tunnisteina ovat myös kaikki vakiokirjaston makrot ja linkitysvaiheessa linkitettävät nimet [21] .

Varattujen tunnisteiden käyttö ohjelmissa on standardin mukaan määrittelemätön käyttäytyminen . Jos yritetään peruuttaa mikä tahansa vakiomakro vialla #undef, seurauksena on myös määrittelemätön toiminta [21] .

Kommentit

C - ohjelman teksti voi sisältää fragmentteja , jotka eivät ole osa ohjelmakoodia - kommentteja . Kommentit on merkitty erityisellä tavalla ohjelman tekstiin ja ohitetaan kokoamisen aikana.

Aluksi C89- standardissa oli saatavilla tekstin sisäisiä kommentteja, jotka voitiin sijoittaa merkkijonojen /*ja merkkijonojen väliin */. Tässä tapauksessa on mahdotonta upottaa kommenttia toiseen, koska ensimmäinen havaittu sekvenssi */lopettaa kommentin ja */kääntäjä näkee merkintää välittömästi seuraavan tekstin ohjelman lähdekoodina.

Seuraava standardi, C99 , esitteli vielä toisen tavan merkitä kommentteja: kommentiksi katsotaan teksti, joka alkaa merkkijonolla //ja päättyy rivin loppuun [20] .

Kommentteja käytetään usein lähdekoodin itsedokumentoimiseen, monimutkaisten osien selittämiseen, tiettyjen tiedostojen tarkoituksen kuvaamiseen ja tiettyjen funktioiden, makrojen, tietotyyppien ja muuttujien käytön ja käytön sääntöjen kuvaamiseen. On jälkiprosessoreita, jotka voivat muuntaa erityisesti muotoillut kommentit dokumentaatioksi. Tällaisten C-kielen jälkiprosessorien joukossa Doxygen- dokumentaatiojärjestelmä voi toimia .

Operaattorit

Lausekkeissa käytetyt operaattorit ovat operandeille suoritettuja operaatioita, jotka palauttavat lasketun arvon - operaation tuloksen. Operandi voi olla vakio, muuttuja, lauseke tai funktiokutsu. Operaattori voi olla erikoismerkki, erikoismerkkijoukko tai erikoissana. Operaattoreita erottaa mukana olevien operandien lukumäärä, nimittäin ne erottavat toisistaan ​​yksi-, binääri- ja kolmioperaattorit.

Unaarioperaattorit

Unaarioperaattorit suorittavat toiminnon yhdelle argumentille ja niillä on seuraava operaatiomuoto:

[ operaattori ] [ operandi ]

Postfixin lisäys- ja vähennysoperaatioilla on käänteinen muoto:

[ operandi ] [ operaattori ] Unary C -operaattorit [22]
+ yksipuolinen plus ~ Palautuskoodin ottaminen & Osoitteen ottaminen ++ Etuliitteen tai jälkiliitteen lisäys sizeof Muistissa olevan objektin käyttämien tavujen lukumäärän saaminen; voidaan käyttää sekä operaattorina että operaattorina
- yksipuolinen miinus ! loogista kieltämistä * Osoittimen viittauksen poisto -- Etuliitteen tai jälkiliitteen vähennys _Alignof Haetaan tasausta tietylle tietotyypille

Inkrementi- ja vähennysoperaattorit muuttavat operandinsa arvoa, toisin kuin muut unaarioperaattorit. Etuliiteoperaattori muuttaa ensin arvoa ja palauttaa sen sitten. Postfix palauttaa ensin arvon ja vasta sitten muuttaa sitä.

Binäärioperaattorit

Binäärioperaattorit sijaitsevat kahden argumentin välissä ja suorittavat niille toiminnon:

[ operandi ] [ operaattori ] [ operandi ] Binääriperusoperaattorit [23]
+ Lisäys % Ottaa loput divisioonasta << Bittisuuntainen vasen vaihto > Lisää == Yhtä
- Vähennyslasku & Bitittain JA >> Hieman siirto oikealle < Vähemmän != Ei tasa-arvoista
* Kertominen | Bittikohtaisesti TAI && looginen JA >= Suurempi tai yhtä suuri
/ Division ^ Bitittainen XOR || Looginen TAI <= Pienempi tai yhtä suuri

Myös C:n binäärioperaattorit sisältävät vasemmanpuoleisia osoitusoperaattoreita, jotka suorittavat toiminnon vasemmalle ja oikealle argumentille ja asettavat tuloksen vasemmanpuoleiseen argumenttiin.

Vasemmanpuoleiset binäärioperaattorit [24]
= Oikean argumentin arvon määrittäminen vasemmalle %= Vasemman operandin jakamisen jäännös oikealla ^= Oikean operandin bittikohtainen XOR vasempaan operandiin
+= Lisäys oikean vasempaan operandiin /= Vasemman operandin jako oikealla <<= Vasemman operandin bittisiirto vasemmalle oikean operandin antamalla bittimäärällä
-= Vähennys oikeanpuoleisesta operandista &= Bittikohtaisesti JA oikea operandi vasemmalle >>= Vasemman operandin bittisiirto oikealle oikean operandin määrittämän bittimäärän verran
*= Vasemman operandin kertominen oikealla |= Oikean operandin bittikohtainen TAI vasemmalle
Kolmiosaiset operaattorit

C:ssä on vain yksi kolmiosainen operaattori, lyhennetty ehdollinen operaattori, jolla on seuraava muoto:

[ ehto ] ?[ lauseke1 ] :[ lauseke2 ]

Lyhyellä ehdollisella operaattorilla on kolme operandia:

  • [ ehto ] - looginen ehto, jonka totuus tarkistetaan,
  • [ lauseke1 ] - lauseke, jonka arvo palautetaan toiminnon tuloksena, jos ehto on tosi;
  • [ lauseke2 ] on lauseke, jonka arvo palautetaan operaation tuloksena, jos ehto on epätosi.

Operaattori on tässä tapauksessa merkkien ?ja yhdistelmä :.

Lausekkeet

Lauseke on vakioiden, muuttujien ja funktioiden järjestetty joukko operaatioita. Lausekkeet sisältävät operandeista ja operaattoreista koostuvia operaatioita . Toimintojen suoritusjärjestys riippuu tietuemuodosta ja toimintojen tärkeydestä. Jokaisella lausekkeella on arvo  - tulos kaikkien lausekkeeseen sisältyvien toimintojen suorittamisesta. Lausekkeen arvioinnin aikana operaatioista riippuen muuttujien arvot voivat muuttua ja funktioita voidaan myös suorittaa, jos niiden kutsuja esiintyy lausekkeessa.

Lausekkeiden joukossa erotetaan vasemmanpuoleisten lausekkeiden luokka  - lausekkeet, jotka voivat olla tehtävämerkin vasemmalla puolella.

Operaatioiden suorittamisen prioriteetti

Toimintojen prioriteetti on määritelty standardissa ja se määrittelee toimintojen suoritusjärjestyksen. Toiminnot C:ssä suoritetaan alla olevan tärkeysjärjestystaulukon mukaisesti [25] [26] .

Prioriteetti rahakkeita Operaatio Luokka Assosiatiivisuus
yksi a[indeksi] Viittaus indeksillä postfix vasemmalta oikealle →
f(argumentteja) Toimintokutsu
. Kenttäpääsy
-> Kentälle pääsy osoittimella
++ -- Positiivinen ja negatiivinen lisäys
(tyypin nimen ) {alustus} Yhdistetty literaali (C99)
(tyypin nimen ) {alustus,}
2 ++ -- Positiiviset ja negatiiviset etuliitteen lisäykset yksipuolinen ← oikealta vasemmalle
sizeof Koon saaminen
_Alignof[f] Hanki tasaus ( C11 )
~ Bitittain EI
! Loogista EI
- + Merkkimerkintä (miinus tai plus)
& Osoitteen saaminen
* Osoittimen viite (poisviittaus)
(tyypin nimi) Tyyppivalu
3 * / % Kerto-, jako- ja jakojäännös binääri vasemmalta oikealle →
neljä + - Yhteen-ja vähennyslasku
5 << >> Vaihto vasemmalle ja oikealle
6 < > <= >= Vertailutoiminnot
7 == != Tasa-arvon tai eriarvoisuuden tarkistaminen
kahdeksan & Bitittain JA
9 ^ Bitittainen XOR
kymmenen | Bittikohtaisesti TAI
yksitoista && looginen JA
12 || Looginen TAI
13 ? : Kunto kolmiosainen ← oikealta vasemmalle
neljätoista = Arvon määritys binääri
+= -= *= /= %= <<= >>= &= ^= |= Toiminnot vasemman arvon muuttamiseen
viisitoista , Jaksollinen laskenta vasemmalta oikealle →

Operaattoreiden prioriteetit C:ssä eivät aina oikeuta itseään ja johtavat joskus intuitiivisesti vaikeasti ennakoitaviin tuloksiin. Esimerkiksi koska unaarisilla operaattoreilla on oikealta vasemmalle assosiatiivisuus, lausekkeen arviointi *p++johtaa osoittimen lisäykseen, jota seuraa viittaus ( *(p++)), osoittimen lisäyksen ( (*p)++) sijaan. Siksi vaikeasti ymmärrettävissä tilanteissa on suositeltavaa ryhmitellä lausekkeet selkeästi hakasulkeilla [26] .

Toinen tärkeä C-kielen ominaisuus on, että funktiokutsuun siirrettyjen argumenttiarvojen arviointi ei ole peräkkäistä [27] , eli argumentteja erottava pilkku ei vastaa peräkkäistä arviointia etusijataulukosta. Seuraavassa esimerkissä toiselle funktiolle argumenteiksi annetut funktiokutsut voivat olla missä tahansa järjestyksessä:

int x ; x = laske ( get_arg1 (), get_arg2 ()); // kutsu ensin get_arg2().

Et voi myöskään luottaa operaatioiden ensisijaisuuteen lausekkeen arvioinnin aikana ilmenevien sivuvaikutusten yhteydessä, koska tämä johtaa määrittelemättömään käyttäytymiseen [27] .

Sekvenssipisteet ja sivuvaikutukset

Kielistandardin liite C määrittelee joukon sekvenssipisteitä , joilla ei takuulla ole jatkuvia sivuvaikutuksia laskelmista. Toisin sanoen järjestyspiste on laskutoimitusvaihe, joka erottaa lausekkeiden arvioinnin keskenään siten, että ennen sarjakohtaa tapahtuneet laskelmat sivuvaikutukset mukaan lukien ovat jo päättyneet ja sekvenssipisteen jälkeen eivät ole vielä alkaneet [28 ] . Sivuvaikutus voi olla muuttujan arvon muutos lausekkeen arvioinnin aikana. Laskennassa mukana olevan arvon muuttaminen yhdessä sivuvaikutuksen kanssa, joka aiheutuu saman arvon muuttamisesta seuraavaan sekvenssipisteeseen, johtaa määrittelemättömään käyttäytymiseen. Sama tapahtuu, jos laskennassa on mukana kaksi tai useampia sivumuutoksia samaan arvoon [27] .

Standardin [27] määrittelemät sekvenssipisteet
Reittipiste Tapahtuma ennen Tapahtuman jälkeen
Toimintokutsu Osoittimen laskeminen funktioon ja sen argumentteihin Toimintokutsu
Loogiset AND-operaattorit ( &&), OR ( ||) ja peräkkäinen laskenta ( ,) Ensimmäisen operandin laskeminen Toisen operandin laskenta
Pikakirjoitusoperaattori ( ?:) Ehdona toimivan operandin laskenta 2. tai 3. operandin laskenta
Kahden täydellisen lausekkeen välillä (ei sisäkkäisiä) Yksi täydellinen ilmaisu Seuraava täydellinen lauseke
Valmis täydellinen kuvaus
Juuri ennen paluuta kirjastotoiminnosta
Jokaisen muotoiltuun I/O-määritteeseen liittyvän muunnoksen jälkeen
Välittömästi ennen ja välittömästi jokaisen vertailutoiminnon kutsun jälkeen sekä vertailufunktion kutsun ja vertailufunktiolle välitetyille argumenteille suoritettujen liikkeiden välillä

Täydelliset lausekkeet ovat [27] :

  • alustus, joka ei ole osa yhdistelmäliteraalia;
  • eristetty ilmentyminen;
  • lauseke, joka on määritetty ehdollisen lausekkeen ( if) tai valintalausekkeen ( switch) ehdoksi;
  • lauseke, joka on määritetty silmukan ehdolla while, jossa on ennakko- tai jälkiehto;
  • jokainen silmukkaparametri for, jos sellainen on;
  • operaattorilauseke return, jos sellainen on määritetty.

Seuraavassa esimerkissä muuttujaa muutetaan kolme kertaa sekvenssipisteiden välillä, jolloin tuloksena on määrittelemätön:

int i = 1 ; // Kuvaaja on ensimmäinen sekvenssipiste, koko lauseke on toinen i += ++ i + 1 ; // Täysi lauseke - kolmas sekvenssipiste printf ( "%d \n " , i ); // Voi tulostaa joko 4 tai 5

Muita yksinkertaisia ​​esimerkkejä määrittelemättömästä käyttäytymisestä, jota tulee välttää:

i = i ++ + 1 ; // määrittelemätön käyttäytyminen i = ++ i + 1 ; // myös määrittelemätön käyttäytyminen printf ( "%d, %d \n " , -- i , ++ i ); // määrittelemätön toiminta printf ( "%d, %d \n " , ++ i , ++ i ); // myös määrittelemätön käyttäytyminen printf ( "%d, %d \n " , i = 0 , i = 1 ); // määrittelemätön toiminta printf ( "%d, %d \n " , i = 0 , i = 0 ); // myös määrittelemätön käyttäytyminen a [ i ] = i ++ ; // määrittelemätön käyttäytyminen a [ i ++ ] = i ; // myös määrittelemätön käyttäytyminen

Valvontalausekkeet

Ohjauskäskyt on suunniteltu suorittamaan toimintoja ja ohjaamaan ohjelman suorituskulkua. Useat peräkkäiset lauseet muodostavat lausesarjan .

Tyhjä lausunto

Yksinkertaisin kielikonstrukti on tyhjä lauseke, jota kutsutaan tyhjäksi lauseeksi [29] :

;

Tyhjä lause ei tee mitään, ja se voidaan sijoittaa mihin tahansa ohjelmaan. Käytetään yleisesti silmukoissa, joista puuttuu runko [30] .

Ohjeet

Ohje on eräänlainen perustoiminto:

( ilmaisu );

Tämän operaattorin tehtävänä on suorittaa operaattorin rungossa määritetty lauseke.

Useat peräkkäiset käskyt muodostavat käskysarjan .

Ohjelohko

Ohjeet voidaan ryhmitellä seuraavan muodon erityisiin lohkoihin:

{

( ohjesarja )

},

Lausuntolohko, jota joskus kutsutaan myös yhdistetyksi lauseeksi, on rajattu vasemmalla aaltosuljetuksella ( {) alussa ja oikealla aaltosulkeella ( }) lopussa.

Funktioissa lausekelohko ilmaisee funktion rungon ja on osa funktion määritelmää. Yhdistelmälauseketta voidaan käyttää myös silmukka-, ehto- ja valintalausekkeissa.

Ehdolliset lauseet

Kielessä on kaksi ehdollista operaattoria, jotka toteuttavat ohjelman haarautumisen:

  • lause if, joka sisältää yhden ehdon testin,
  • ja lausunto switch, joka sisältää useita tarkistettavia ehtoja.

Operaattorin yksinkertaisin muotoif

if(( kunto ) )( operaattori ) ( seuraava lausunto )

Operaattori iftoimii näin:

  • jos suluissa oleva ehto on tosi, suoritetaan ensimmäinen lauseke ja sitten lauseen jälkeinen lauseke suoritetaan if.
  • jos suluissa määritetty ehto ei täyty, lauseen jälkeen määritetty käsky suoritetaan välittömästi if.

Erityisesti seuraava koodi, jos määritetty ehto täyttyy, ei suorita mitään toimintoa, koska itse asiassa suoritetaan tyhjä käsky:

if(( kunto )) ;

Monimutkaisempi operaattorin muoto ifsisältää avainsanan else:

if(( kunto ) )( operaattori ) else( vaihtoehtoinen operaattori ) ( seuraava lausunto )

Tässä, jos suluissa määritetty ehto ei täyty, avainsanan jälkeen määritetty käsky suoritetaan else.

Vaikka standardi sallii lausekkeiden määrittämisen yhdelle riville iftai elseyksittäiseksi riviksi, tätä pidetään huonona tyylinä ja heikentää koodin luettavuutta. Suosittelemme, että määrität aina lauselohkon käyttämällä aaltosulkeiden runko-osaa [31] .

Silmukan suorituslausekkeet

Silmukka on koodinpätkä, joka sisältää

  • silmukan suoritusehto - ehto, jota tarkistetaan jatkuvasti;
  • ja silmukan runko on yksinkertainen tai yhdistelmälause, jonka suoritus riippuu silmukan tilasta.

Näin ollen on olemassa kahdentyyppisiä syklejä:

  • silmukan, jolla on ehto , jossa silmukan suoritusehto tarkistetaan ensin, ja jos ehto täyttyy, silmukan runko suoritetaan;
  • silmukka, jossa on jälkiehto , jossa silmukan jatkuvuuden ehto tarkistetaan silmukan rungon suorittamisen jälkeen.

Jälkiehdollinen silmukka takaa, että silmukan runko suoritetaan vähintään kerran.

C-kieli tarjoaa kaksi muunnelmaa silmukoista, joissa on ennakkoehto: whileja for.

while(kunto) [ loop body ] for( alustuslohkon ;ehtolause [ loop body ] ;,)

Silmukkaa forkutsutaan myös parametriseksi, se vastaa seuraavaa lauseketta:

[ alustuslohko ] while(kunto) { [ loop body ] [ operaattori ] }

Normaalitilanteessa alustuslohko sisältää muuttujan alkuarvon asettamisen, jota kutsutaan silmukkamuuttujaksi, ja käsky, joka suoritetaan välittömästi sen jälkeen, kun silmukan runko muuttaa käytetyn muuttujan arvoja, ehto sisältää käytetyn silmukkamuuttujan arvon vertailu johonkin ennalta määritettyyn arvoon, ja heti kun vertailu lopetetaan, silmukka keskeytyy ja silmukkakäskyä välittömästi seuraavan ohjelmakoodin suorittaminen alkaa.

Silmukalle do-whileehto määritetään silmukan rungon jälkeen:

do[ loop body ] while( kunto)

Silmukan ehto on boolen lauseke. Implisiittisen tyypin valu mahdollistaa kuitenkin aritmeettisen lausekkeen käyttämisen silmukkaehtona. Tämän avulla voit järjestää niin kutsutun "äärettömän silmukan":

while(1);

Sama voidaan tehdä operaattorin kanssa for:

for(;;);

Käytännössä tällaisia ​​äärettömiä silmukoita käytetään yleensä yhdessä break, gototai :n kanssa return, jotka katkaisevat silmukan eri tavoin.

Ehdollisen lauseen tavoin yksirivisen rungon käyttäminen ilman, että sitä suljetaan käskylohkossa, jossa on aaltosulkeet, katsotaan huonoksi tyyliksi, mikä heikentää koodin luettavuutta [31] .

Ehdottomat hyppyoperaattorit

Ehdottomien haaraoperaattorien avulla voit keskeyttää minkä tahansa laskutoimituslohkon suorittamisen ja siirtyä toiseen paikkaan ohjelmassa nykyisen funktion sisällä. Ehdottomia hyppyoperaattoreita käytetään yleensä ehdollisten operaattoreiden yhteydessä.

goto[ etiketti ],

Tarra on jokin tunniste, joka siirtää ohjauksen operaattorille, joka on merkitty ohjelmassa määritetyllä tunnisteella:

[ etiketti ] :[ operaattori ]

Jos määritettyä tunnistetta ei ole ohjelmassa tai jos samalla tunnisteella on useita lauseita, kääntäjä ilmoittaa virheestä.

Ohjauksen siirto on mahdollista vain sen toiminnon sisällä, jossa siirtymäoperaattoria käytetään, joten operaattoria käyttämällä gotoei voi siirtää ohjausta toiselle toiminnolle.

Muut hyppykäskyt liittyvät silmukoihin ja mahdollistavat silmukan rungon suorittamisen keskeyttämisen:

  • käsky breakkeskeyttää välittömästi silmukan rungon suorittamisen ja ohjaus siirtyy välittömästi silmukan jälkeiseen käskyyn;
  • operaattori continuekeskeyttää silmukan nykyisen iteraation suorituksen ja aloittaa yrityksen siirtyä seuraavaan.

Käsky breakvoi myös keskeyttää lauseen toiminnan switch, joten switchsilmukassa olevan käskyn sisällä lauseke breakei voi keskeyttää silmukkaa. Silmukan rungossa määritetty se keskeyttää lähimmän sisäkkäisen silmukan työn.

Operaattoria continuevoidaan käyttää vain do, whileja -operaattoreiden sisällä for. Silmukoille whileja do-whileoperaattori continueaiheuttaa silmukan ehdon testin ja silmukan tapauksessa silmukan for 3. parametrissa määritellyn operaattorin suorittamisen ennen silmukan jatkamisen ehdon tarkistamista.

Funktiopalautuslause

Operaattori returnkeskeyttää sen toiminnon suorittamisen, jossa sitä käytetään. Jos funktion ei pitäisi palauttaa arvoa, käytetään kutsua ilman palautusarvoa:

return;

Jos funktion on palautettava arvo, palautusarvo ilmoitetaan operaattorin jälkeen:

return[ arvo ];

Jos funktion rungossa on return-käskyn jälkeen muita käskyjä, näitä käskyjä ei koskaan suoriteta, jolloin kääntäjä voi antaa varoituksen. Operaattorin jälkeen returnvoidaan kuitenkin ilmoittaa ohjeet toiminnon vaihtoehtoiseen lopettamiseen, esimerkiksi vahingossa, ja siirtyminen näihin operaattoreihin voidaan suorittaa käyttämällä operaattoria gotoehtojen mukaan .

Muuttujat

Kun muuttujaa määritetään, sen tyyppi ja nimi määritetään, ja alkuarvo voidaan myös määrittää:

[kuvaus] [nimi];

tai

[descriptor] [name] =[initializer] ;,

missä

  • [descriptor] - muuttujan tyyppi ja tyyppiä edeltävät valinnaiset modifikaattorit;
  • [nimi] — muuttujan nimi;
  • [initializer] - muuttujan alkuarvo, joka määritetään sen luonnin yhteydessä.

Jos muuttujalle ei ole annettu alkuarvoa, globaalin muuttujan tapauksessa sen arvo täytetään nolilla ja paikallisen muuttujan aloitusarvo on määrittelemätön.

Muuttujan kuvaajassa voit määrittää muuttujan globaaliksi, mutta vain tiedoston tai funktion laajuudeksi käyttämällä avainsanaa static. Jos muuttuja on ilmoitettu globaaliksi ilman avainsanaa static, siihen pääsee myös muista tiedostoista, joissa tämä muuttuja on ilmoitettava ilman alustusta, mutta avainsanalla extern. Tällaisten muuttujien osoitteet määritetään linkin aikana .

Toiminnot

Funktio on itsenäinen ohjelmakoodin osa, jota voidaan käyttää uudelleen ohjelmassa. Funktiot voivat ottaa argumentteja ja palauttaa arvoja. Funktioilla voi olla myös sivuvaikutuksia niiden suorittamisen aikana: globaalien muuttujien muuttaminen, tiedostojen käsittely, vuorovaikutus käyttöjärjestelmän tai laitteiston kanssa [28] .

Määrittääksesi funktion C:ssä, sinun on määritettävä se:

  • ilmoittaa toiminnon nimi (tunniste),
  • luettele syöttöparametrit (argumentit)
  • ja määritä palautustyyppi.

On myös tarpeen tarjota funktion määritelmä, joka sisältää lohkon lauseita, jotka toteuttavat funktion käyttäytymisen.

Tietyn funktion ilmoittamatta jättäminen on virhe, jos funktiota käytetään määritelmän ulkopuolella, mikä johtaa toteutuksesta riippuen ilmoituksiin tai varoituksiin.

Funktiota kutsuaksesi riittää, että määrität sen nimen suluissa määritetyillä parametreilla. Tällöin pinoon sijoitetaan kutsupisteen osoite, funktioparametreista vastaavat muuttujat luodaan ja alustetaan ja ohjaus siirretään kutsutun funktion toteuttavalle koodille. Kun funktio on suoritettu, funktiokutsun aikana varattu muisti vapautetaan, paluu kutsupisteeseen ja jos funktiokutsu on osa jotakin lauseketta, funktion sisällä laskettu arvo välitetään paluupisteeseen.

Jos sulkuja ei ole määritetty funktion jälkeen, kääntäjä tulkitsee tämän saavansa funktion osoitteen. Toiminnon osoite voidaan syöttää osoittimeen ja kutsua sen jälkeen funktioksi osoittimella, jota käytetään aktiivisesti esimerkiksi liitännäisjärjestelmissä [32] .

Avainsanalla inlinevoit merkitä toimintoja, joiden kutsut haluat suorittaa mahdollisimman nopeasti. Kääntäjä voi korvata tällaisten funktioiden koodin suoraan niiden kutsun kohdalla [33] . Tämä toisaalta lisää suoritettavan koodin määrää, mutta toisaalta säästää sen suorittamiseen kuluvaa aikaa, koska aikaa vievää funktiokutsuoperaatiota ei käytetä. Tietokoneiden arkkitehtuurista johtuen inline-toiminnot voivat kuitenkin joko nopeuttaa tai hidastaa sovellusta kokonaisuutena. Kuitenkin monissa tapauksissa rivifunktiot ovat suositeltavin makrojen korvike [34] .

Toimintoilmoitus

Toimintomäärityksellä on seuraava muoto:

[descriptor] [name] ([list] );,

missä

  • [descriptor] — funktion palauttaman arvon tyyppikuvaaja;
  • [nimi] - funktion nimi (funktion yksilöllinen tunniste);
  • [luettelo] - luettelo funktion (muodollisista) parametreista tai voidniiden puuttuessa [35] .

Toimintomäärityksen merkki on “ ;”-symboli, joten funktiomäärittely on käsky.

Yksinkertaisimmassa tapauksessa [declarator] sisältää viitteen tietyntyyppisestä palautusarvosta. Funktio, jonka ei pitäisi palauttaa arvoa, ilmoitetaan tyyppiseksi void.

Tarvittaessa kuvaaja voi sisältää muunnoksia, jotka on määritetty avainsanoilla:

  • externosoittaa, että funktion määritys on toisessa moduulissa ;
  • staticmäärittää staattisen funktion, jota voidaan käyttää vain nykyisessä moduulissa.

Funktioparametrien luettelo määrittää funktion allekirjoituksen.

C ei salli useiden funktioiden ilmoittamista samalla nimellä, funktion ylikuormitusta ei tueta [36] .

Funktiomäärittely

Funktiomäärittelyllä on seuraava muoto:

[descriptor] [name] ([list] )[body]

Missä [declarator], [name] ja [list] ovat samat kuin ilmoituksessa ja [body] on yhdistelmälause, joka edustaa funktion konkreettista toteutusta. Kääntäjä erottaa samannimisen funktion määritelmät niiden allekirjoituksen perusteella ja näin (allekirjoituksella) muodostetaan yhteys määritelmän ja vastaavan ilmoituksen välille.

Toiminnon runko näyttää tältä:

{ [lausuntosekvenssi] return([palautusarvo]); }

Palautus funktiosta suoritetaan -operaattorilla , joka joko määrittää palautusarvon tai ei määritä sitä riippuen funktion palauttamasta tietotyypistä. Harvinaisissa tapauksissa funktio voidaan merkitä otsikkotiedoston makron avulla palautumatta jättäväksi , jolloin lauseketta ei vaadita. Esimerkiksi funktiot, jotka kutsuvat sisällään ehdoitta, voidaan merkitä tällä tavalla [33] . returnnoreturnstdnoreturn.hreturnabort()

Toimintokutsu

Toimintokutsun tarkoituksena on suorittaa seuraavat toiminnot:

  • hälytyspisteen tallentaminen pinoon;
  • automaattinen muistin allokointi funktion muodollisia parametreja vastaaville muuttujille;
  • muuttujien alustus muuttujien arvoilla (funktion todelliset parametrit), jotka funktiolle välitetään, kun sitä kutsutaan, sekä niiden muuttujien alustus, joiden oletusarvot on määritetty funktion määrittelyssä, mutta joille niitä vastaavia todellisia parametreja ei määritetty puhelun aikana;
  • ohjauksen siirtäminen toiminnon rungolle.

Toteutuksesta riippuen kääntäjä joko varmistaa tiukasti, että varsinaisen parametrin tyyppi vastaa muodollisen parametrin tyyppiä, tai, jos mahdollista, suorittaa implisiittisen tyyppimuunnoksen, joka luonnollisesti johtaa sivuvaikutuksiin.

Jos funktiolle välitetään muuttuja, funktiota kutsuttaessa siitä luodaan kopio ( pinoon varataan muisti ja arvo kopioidaan). Esimerkiksi rakenteen välittäminen funktiolle aiheuttaa koko rakenteen kopioimisen. Jos osoitin rakenteeseen välitetään, vain osoittimen arvo kopioidaan. Matriisin välittäminen funktiolle aiheuttaa myös vain osoittimen kopioimisen sen ensimmäiseen elementtiin. Tässä tapauksessa voit nimenomaisesti osoittaa, että taulukon alun osoite otetaan syötteeksi funktiolle, ei osoitin yksittäiseen muuttujaan, sen sijaan, että ilmoittaisit osoittimen muuttujan nimen jälkeen, voit laittaa hakasulkeet, esimerkki:

void esimerkki_funktio ( int array []); // taulukko on osoitin int-tyypin taulukon ensimmäiseen elementtiin

C sallii sisäkkäiset puhelut. Puheluiden sisäkkäisyydellä on ilmeinen rajoitus, joka liittyy ohjelmalle varatun pinon kokoon. Siksi C-toteutukset asettavat rajan sisäkkäisyyden syvyydelle.

Sisäkkäisen kutsun erikoistapaus on funktiokutsu kutsutun funktion rungossa. Tällaista kutsua kutsutaan rekursiiviseksi, ja sitä käytetään yhtenäisten laskelmien järjestämiseen. Sisäkkäisten puhelujen luonnollisen rajoituksen vuoksi rekursiivinen toteutus korvataan silmukoita käyttävällä toteutuksella.

Tietotyypit

Primitiivityypit

Kokonaisluvut

Kokonaislukutietotyypit ovat kooltaan vähintään 8 - vähintään 32 bittiä. C99-standardi kasvattaa kokonaisluvun maksimikoon vähintään 64 bittiin. Kokonaislukutietotyyppejä käytetään kokonaislukujen tallentamiseen (tyyppiä charkäytetään myös ASCII-merkkien tallentamiseen). Kaikki alla olevien tietotyyppien aluekoot ovat minimiä ja voivat olla suurempia tietyllä alustalla [37] .

Tyyppien vähimmäiskokojen seurauksena standardi edellyttää, että integroitujen tyyppien koot täyttävät ehdon:

1= ≤ ≤ ≤ ≤ . sizeof(char)sizeof(short)sizeof(int)sizeof(long)sizeof(long long)

Siten joidenkin tyyppien koot tavumäärän suhteen voivat täsmää, jos ehto bittien vähimmäismäärälle täyttyy. Jopa charja longvoi olla samankokoisia, jos yksi tavu kestää 32 bittiä tai enemmän, mutta tällaiset alustat ovat hyvin harvinaisia ​​tai niitä ei ole olemassa. Standardi takaa, että tyyppi on char aina 1 tavu. Tavun koon bitteinä määrittää CHAR_BITotsikkotiedoston vakio limits.h, joka on 8 bittiä POSIX -yhteensopivissa järjestelmissä [38] .

Standardin mukainen kokonaislukutyyppien minimiarvoalue määritellään etumerkittyille tyypeille välillä - ja etumerkittömille tyypeille alkaen -  , missä N on tyypin bittisyvyys. Kääntäjätoteutukset voivat laajentaa tätä aluetta harkintansa mukaan. Käytännössä aluetta - - käytetään yleisemmin allekirjoitetuille tyypeille . Kunkin tyypin minimi- ja maksimiarvot on määritetty tiedostossa makromäärityksinä. -(2N-1-1)2N-1-102N-2N-12N-1-1limits.h

Erityistä huomiota tulee kiinnittää tyyppiin char. Muodollisesti tämä on erillinen tyyppi, mutta itse asiassa charvastaa joko signed char, tai unsigned charkääntäjästä riippuen [39] .

Välttääkseen sekaannuksia tyyppikokojen välillä C99-standardi esitteli uusia tietotyyppejä, jotka on kuvattu stdint.h. Niiden joukossa on tyyppejä, kuten: , , , jossa = 8, 16, 32 tai 64. Etuliite tarkoittaa minimityyppiä, johon bittejä mahtuu , etuliite tarkoittaa vähintään 16 bitin tyyppiä, joka on nopein tällä alustalla. Tyypit ilman etuliitteitä tarkoittavat tyyppejä, joiden bittikoko on kiinteä . intN_tint_leastN_tint_fastN_tNleast-Nfast-N

Tyypit, joissa on etuliitteet least-ja niitä voidaan pitää tyyppien , , fast-korvaavina , sillä ainoalla erolla, että edelliset antavat ohjelmoijalle mahdollisuuden valita nopeuden ja koon välillä. intshortlong

Perustietotyypit kokonaislukujen tallentamiseen
Tietotyyppi Koko Minimiarvoalue Vakio
signed char vähintään 8 bittiä arvosta −127 [40] (= -(2 7 −1)) arvoon 127 C90 [j]
int_least8_t C99
int_fast8_t
unsigned char vähintään 8 bittiä 0 - 255 (=2 8 -1) C90 [j]
uint_least8_t C99
uint_fast8_t
char vähintään 8 bittiä −127 - 127 tai 0 - 255 kääntäjästä riippuen C90 [j]
short int vähintään 16 bittiä -32,767 (= -(2 15 -1)) arvoon 32,767 C90 [j]
int
int_least16_t C99
int_fast16_t
unsigned short int vähintään 16 bittiä 0 - 65,535 (= 2 16 -1) C90 [j]
unsigned int
uint_least16_t C99
uint_fast16_t
long int vähintään 32 bittiä −2 147 483 647 - 2 147 483 647 C90 [j]
int_least32_t C99
int_fast32_t
unsigned long int vähintään 32 bittiä 0 - 4 294 967 295 (= 2 32 -1) C90 [j]
uint_least32_t C99
uint_fast32_t
long long int vähintään 64 bittiä -9 223 372 036 854 775 807 - 9 223 372 036 854 775 807 C99
int_least64_t
int_fast64_t
unsigned long long int vähintään 64 bittiä 0 - 18 446 744 073 709 551 615 (= 264 −1 )
uint_least64_t
uint_fast64_t
int8_t 8-bittinen -127-127
uint8_t 8-bittinen 0 - 255 (=2 8 -1)
int16_t 16-bittinen -32.767 - 32.767
uint16_t 16-bittinen 0 - 65,535 (= 2 16 -1)
int32_t 32 bittiä −2 147 483 647 - 2 147 483 647
uint32_t 32 bittiä 0 - 4 294 967 295 (= 2 32 -1)
int64_t 64 bittiä -9 223 372 036 854 775 807 - 9 223 372 036 854 775 807
uint64_t 64 bittiä 0 - 18 446 744 073 709 551 615 (= 264 −1 )
Taulukko näyttää arvojen vähimmäisalueen kielistandardin mukaan. C-kääntäjät voivat laajentaa arvoaluetta.
Apukokonaislukutyypit

Lisäksi C99-standardin jälkeen tyypit intmax_tja on lisätty uintmax_t, jotka vastaavat suurinta allekirjoitettua ja allekirjoittamatonta tyyppiä. Nämä tyypit ovat käteviä, kun niitä käytetään makroissa väli- tai väliaikaisten arvojen tallentamiseen kokonaislukuargumenttien aikana, koska niiden avulla voit sovittaa minkä tahansa tyyppisiä arvoja. Näitä tyyppejä käytetään esimerkiksi Check unit testing -kirjaston C :n kokonaislukuvertailumakroissa [41] .

C:ssä on useita muita kokonaislukutyyppejä osoittimen tietotyypin turvalliseen käsittelyyn: intptr_t, uintptr_tja ptrdiff_t. intptr_tC99-standardin ja - tyypit uintptr_ton suunniteltu tallentamaan etumerkittyjä ja etumerkittömiä arvoja, jotka mahtuvat osoittimen kokoon. Näitä tyyppejä käytetään usein mielivaltaisen kokonaisluvun tallentamiseen osoittimeen, esimerkiksi keinona päästä eroon tarpeettomasta muistin varauksesta palautefunktioita rekisteröitäessä [42] tai käytettäessä kolmannen osapuolen linkitettyjä listoja, assosiatiivisia taulukoita ja muita rakenteita, joissa tiedot tallennetaan osoittimella. ptrdiff_tOtsikkotiedoston tyyppi stddef.hon suunniteltu tallentamaan turvallisesti kahden osoittimen erot.

size_tKoon tallentamiseksi tarjotaan otsikkotiedostosta allekirjoittamaton tyyppi stddef.h. Tämä tyyppi pystyy pitämään suurimman mahdollisen määrän osoittimessa käytettävissä olevia tavuja, ja sitä käytetään tyypillisesti tallentamaan koko tavuina. Tämän tyypin arvon palauttaa operaattori sizeof[43] .

Kokonaislukutyyppinen valu

Kokonaislukutyypin muunnokset voivat tapahtua joko eksplisiittisesti, käyttämällä cast-operaattoria tai implisiittisesti. Tyyppien arvot, jotka ovat pienempiä kuin int, kun osallistuvat mihin tahansa toimintoon tai siirretään funktiokutsuun, lähetetään automaattisesti tyyppiin int, ja jos muuntaminen on mahdotonta, tyyppiin unsigned int. Usein tällaiset implisiittiset heitot ovat välttämättömiä, jotta laskennan tulos olisi oikea, mutta joskus ne johtavat intuitiivisesti käsittämättömiin virheisiin laskelmissa. Jos operaatioon liittyy esimerkiksi tyypin intja lukuja unsigned intja etumerkillinen arvo on negatiivinen, negatiivisen luvun muuntaminen etumerkittömäksi tyypiksi johtaa ylivuotoon ja erittäin suureen positiiviseen arvoon, mikä voi johtaa vertailutoimintojen virheelliseen tulokseen. [44] .

Oikean ja väärän automaattisen tyyppivalun vertailu
Allekirjoitetut ja allekirjoittamattomat tyypit ovat pienempiä kuinint Allekirjoitettu on vähemmän kuin allekirjoittamaton, ja allekirjoittamaton ei ole pienempiint
#include <stdio.h> etumerkillinen merkki x = -1 ; etumerkitön char y = 0 ; if ( x > y ) { // ehto on false printf ( "Viestiä ei näytetä. \n " ); } if ( x == UCHAR_MAX ) { // ehto on false printf ( "Viestiä ei näytetä. \n " ); } #include <stdio.h> etumerkillinen merkki x = -1 ; unsigned int y = 0 ; if ( x > y ) { // ehto on tosi printf ( "Ylivuoto muuttujassa x. \n " ); } if (( x == UINT_MAX ) && ( x == ULONG_MAX )) { // ehto on aina tosi printf ( "Ylivuoto muuttujassa x. \n " ); }
Tässä esimerkissä molemmat tyypit, allekirjoitetut ja allekirjoittamattomat, välitetään allekirjoitettuun muotoon int, koska sen avulla molempien tyyppien alueet mahtuvat. Siksi ehdollisen operaattorin vertailu on oikea. Etumerkillinen tyyppi välitetään etumerkittömäksi, koska etumerkitön tyyppi on suurempi tai yhtä suuri kuin int, mutta ylivuoto tapahtuu, koska on mahdotonta esittää negatiivista arvoa etumerkittömässä tyypissä.

Myös automaattinen tyyppien valu toimii, jos lausekkeessa käytetään kahta tai useampaa erilaista kokonaislukutyyppiä. Standardi määrittelee sääntöjoukon, jonka mukaan valitaan sellainen tyyppimuunnos, joka voi antaa oikean laskennan tuloksen. Eri tyypeille annetaan eri arvot muunnoksen sisällä, ja itse arvot perustuvat tyypin kokoon. Kun lausekkeessa on mukana eri tyyppejä, nämä arvot valitaan yleensä heittämään korkeamman tason tyyppiin [44] .

Reaaliluvut

C:n liukulukuja edustavat kolme perustyyppiä: float, doubleja long double.

Reaaliluvuilla on hyvin erilainen esitys kuin kokonaisluvuilla. Erityyppisten reaalilukujen vakiot, jotka on kirjoitettu desimaalimuodossa, eivät välttämättä ole keskenään samanarvoisia. Esimerkiksi ehto 0.1 == 0.1fon epätosi tyypin tarkkuuden menettämisen vuoksi float, kun taas ehto 0.5 == 0.5fon tosi, koska nämä luvut ovat äärellisiä binääriesityksenä. Valintaehto (float) 0.1 == 0.1fon kuitenkin myös totta, koska valettaessa vähemmän tarkkaan tyyppiin menetetään bitit, jotka tekevät kahdesta vakiosta erilaisia.

Aritmeettiset operaatiot reaaliluvuilla ovat myös epätarkkoja ja niissä on usein kelluva virhe [45] . Suurin virhe tapahtuu käytettäessä arvoja, jotka ovat lähellä tietyn tyypin mahdollista minimiä. Virhe voi myös osoittautua suureksi, kun lasketaan samanaikaisesti hyvin pienistä (≪ 1) ja erittäin suurista luvuista (≫ 1). Joissakin tapauksissa virhettä voidaan pienentää muuttamalla algoritmeja ja laskentamenetelmiä. Esimerkiksi, kun useiden yhteenlasku korvataan kertolaskulla, virhe voi pienentyä niin monta kertaa kuin alunperin summausoperaatioita oli.

Myös otsikkotiedostossa math.hon kaksi lisätyyppiä float_tja double_t, jotka vastaavat vähintään tyyppejä floatja doublevastaavasti, mutta voivat poiketa niistä. Tyypit float_tja double_tlisätään C99-standardiin ja niiden vastaavuus perustyyppeihin määräytyy makron arvon mukaan FLT_EVAL_METHOD.

Todelliset tietotyypit
Tietotyyppi Koko Vakio
float 32 bittiä IEC 60559 ( IEEE 754 ), C-standardin laajennus F [46] [k] , yksi tarkkuusnumero
double 64 bittiä IEC 60559 (IEEE 754), C-standardin laajennus F [46] [k] , kaksinkertainen tarkkuusnumero
long double vähintään 64 bittiä täytäntöönpanosta riippuvainen
float_t(C99) vähintään 32 bittiä riippuu pohjatyypistä
double_t(C99) vähintään 64 bittiä riippuu pohjatyypistä
Lisätyyppien yhteensopivuus perustyyppien kanssa [47]
FLT_EVAL_METHOD float_t double_t
yksi float double
2 double double
3 long double long double

Strings

Nollapääteiset merkkijonot

Vaikka C:n merkkijonoille ei sinänsä ole erityistä tyyppiä, nollapääteisiä merkkijonoja käytetään kielessä voimakkaasti. ASCII -merkkijonot ilmoitetaan tyypin taulukona char, jonka viimeisen elementin on oltava merkkikoodi 0( '\0'). On tapana tallentaa UTF-8- merkkijonot samassa muodossa . Kuitenkin kaikki funktiot, jotka toimivat ASCII-merkkijonojen kanssa, pitävät jokaista merkkiä tavuna, mikä rajoittaa standardifunktioiden käyttöä tätä koodausta käytettäessä.

Huolimatta nollapääteisten merkkijonojen idean laajasta käytöstä ja niiden käyttömukavuudesta joissakin algoritmeissa, niillä on useita vakavia haittoja.

  1. Tarve lisätä päätemerkki merkkijonon loppuun ei mahdollista osamerkkijonon saamista ilman tarvetta kopioida sitä, eikä kieli tarjoa toimintoja alamerkkijonoon ja sen pituuteen osoittavan osoittimen käyttämiseen.
  2. Jos syötetietoihin perustuvan algoritmin tulokselle on varattava muistia etukäteen, täytyy joka kerta kulkea koko merkkijonon läpi sen pituuden laskemiseksi.
  3. Suuria tekstimääriä käsiteltäessä pituuslaskenta voi olla pullonkaula .
  4. Työskentely merkkijonon kanssa, joka ei ole vahingossa päätetty tyhjäksi, voi johtaa määrittelemättömään ohjelman toimintaan, kuten segmentointivirheisiin , puskurin ylivuotovirheisiin ja haavoittuvuuksiin .

Nykyaikaisissa olosuhteissa, kun koodin suorituskyky asetetaan etusijalle muistinkulutukseen nähden, voi olla tehokkaampaa ja helpompaa käyttää rakenteita, jotka sisältävät sekä itse merkkijonon että sen koon [48] , esimerkiksi:

struct string_t { char * str ; // osoitin merkkijonoon koko_t str_size ; // merkkijonon koko }; typedef rakenne merkkijono_t merkkijono_t ; // vaihtoehtoinen nimi koodin yksinkertaistamiseksi

Vaihtoehtoinen vähän muistia sisältävä merkkijonokoon tallennustapa olisi liittää merkkijonon eteen sen koko muuttuvan pituisen koon muodossa.. Samanlaista lähestymistapaa käytetään protokollapuskureissa , kuitenkin vain tiedonsiirtovaiheessa, mutta ei niiden tallentamisessa.

Merkkijono literaalit

C:n merkkijonoliteraalit ovat luonnostaan ​​vakioita [10] . Ilmoitaessa ne laitetaan lainausmerkkeihin ja 0kääntäjä lisää automaattisesti päätteen. On kaksi tapaa määrittää merkkijonoliteraali: osoittimen ja arvon perusteella. Osoittimella määritettäessä char *tyyppimuuttujaan syötetään osoitin muuttumattomaan merkkijonoon, eli muodostetaan vakiomerkkijono. Jos syötät taulukkoon merkkijonoliteraalin, merkkijono kopioidaan pinoalueelle.

#include <stdio.h> #include <string.h> int main ( tyhjä ) { const char * s1 = "Vakiomerkkijono" ; char s2 [] = "Merkkijono, jota voidaan muuttaa" ; memcpy ( s2 , " c " , strlen ( " c " )); // muuta ensimmäinen kirjain pieneksi laittaa ( s2 ); // rivin teksti tulee näkyviin memcpy (( char * ) s1 , "to" , strlen ( "to" )); // segmentointivirhe laittaa ( s1 ); // riviä ei suoriteta }

Koska merkkijonot ovat tavallisia merkkijonoja, alustajia voidaan käyttää literaalien sijasta, kunhan jokainen merkki mahtuu 1 tavuun:

char s [] = { 'minä' , 'n' , 'i' , 't' , 'i' , 'a' , 'l' , 'i' , 'z' , 'e' , ​​'r' , '\0' };

Käytännössä tämä lähestymistapa on kuitenkin järkevä vain erittäin harvoissa tapauksissa, kun ASCII-merkkijonoon ei vaadita lopettavaa nollaa.

Leveät viivat Tyyppikoodaus wchar_talustasta riippuen
Alusta Koodaus
GNU/Linux USC-4 [49]
Mac käyttöjärjestelmä
Windows USC-2 [50]
AIX
FreeBSD Riippuu alueesta

ei dokumentoitu [50]

Solaris

Vaihtoehto tavallisille merkkijonoille ovat leveät merkkijonot, joissa jokainen merkki on tallennettu erityiseen tyyppiin wchar_t. Standardin antaman tyypin pitäisi pystyä sisältämään itsessään kaikki suurimman olemassa olevan paikan merkit . Leveiden merkkijonojen työskentelyn toiminnot kuvataan otsikkotiedostossa wchar.hja leveiden merkkien kanssa työskentelyn toiminnot otsikkotiedostossa wctype.h.

Kun määritetään merkkijonoliteraaleja leveille merkkijonoille, käytetään muuntajaa L:

const wchar_t * wide_str = L "Leveä merkkijono" ;

Muotoiltu tulos käyttää määritettä %ls, mutta koon määrittäjä, jos se on annettu, määritellään tavuissa, ei merkeissä [51] .

Tyyppi wchar_tsuunniteltiin niin, että siihen mahtui mikä tahansa merkki ja leveät merkkijonot - minkä tahansa kielen merkkijonojen tallentamiseen, mutta sen seurauksena API osoittautui hankalaksi ja toteutukset olivat alustariippuvaisia. Joten Windows -alustalla tyypin kooksi valittiin 16 bittiä wchar_tja myöhemmin UTF-32-standardi ilmestyi, joten wchar_tWindows-alustalla oleva tyyppi ei enää mahdu kaikkia UTF-32-koodauksen merkkejä, jonka seurauksena tämän tyypin merkitys menetetään [50] . Samaan aikaan Linux [49] ja macOS-alustoilla tämä tyyppi vie 32 bittiä, joten tyyppi ei sovellu useiden alustojen tehtävien toteuttamiseen.wchar_t

Monitavuiset merkkijonot

On olemassa monia erilaisia ​​koodauksia, joissa yksi merkki voidaan ohjelmoida eri määrällä tavuja. Tällaisia ​​koodauksia kutsutaan monitavuiksi. UTF-8 koskee myös niitä . C:llä on joukko toimintoja, joilla muunnetaan merkkijonoja nykyisen kielen monitavuisista leveiksi ja päinvastoin. Monitavuisten merkkien kanssa työskentelyyn tarkoitetuilla funktioilla on etu- tai pääte mbja ne kuvataan otsikkotiedostossa stdlib.h. Jotta C-ohjelmien monitavuisia merkkijonoja voidaan tukea, tällaisia ​​merkkijonoja on tuettava nykyisellä kielitasolla . Jos haluat määrittää koodauksen nimenomaisesti, voit muuttaa nykyistä aluetta käyttämällä funktiota setlocale(). locale.hKäytettävän vakiokirjaston on kuitenkin tuettava maa-asetuksen koodauksen määrittämistä. Esimerkiksi Glibc -standardikirjasto tukee täysin UTF-8-koodausta ja pystyy muuttamaan tekstin moniin muihin koodauksiin [52] .

C11-standardista alkaen kieli tukee myös 16-bittisiä ja 32-bittisiä monitavuisia merkkijonoja sopivilla merkkityypeillä char16_tja char32_totsikkotiedostosta uchar.hsekä UTF-8-merkkijonoliteraalien ilmoittamista käyttämällä u8. 16-bittisiä ja 32-bittisiä merkkijonoja voidaan käyttää UTF-16- ja UTF-32-koodausten tallentamiseen, jos uchar.hmakromääritykset __STDC_UTF_16__ja ne on määritetty otsikkotiedostossa __STDC_UTF_32__. Merkkijonoliteraalien määrittämiseen näissä muodoissa käytetään muuntajia: u16-bittisille merkkijonoille ja U32-bittisille merkkijonoille. Esimerkkejä merkkijonoliteraalien ilmoittamisesta monitavuisille merkkijonoille:

const char * s8 = u8 "UTF-8 monitavuinen merkkijono" ; const char16_t * s16 = u "16-bittinen monitavuinen merkkijono" ; const char32_t * s32 = U "32-bittinen monitavuinen merkkijono" ;

Huomaa, että toiminto c16rtomb()16-bittisen merkkijonon muuntamiseksi monitavuiseksi merkkijonoksi ei toimi tarkoitetulla tavalla, ja C11-standardissa havaittiin, ettei se pysty kääntämään UTF-16:sta UTF-8:aan [53] . Tämän toiminnon korjaaminen voi riippua kääntäjän erityisestä toteutuksesta.

Mukautetut tyypit

Luettelot

Enumit ovat joukko nimettyjä kokonaislukuvakioita, ja niitä merkitään avainsanalla enum. Jos vakio ei liity numeroon, se asetetaan automaattisesti joko 0luettelon ensimmäiselle vakiolle tai numerolle, joka on yhtä suurempi kuin edellisessä vakiossa määritetty. Tässä tapauksessa itse luettelointitietotyyppi voi itse asiassa vastata mitä tahansa etumerkittyä tai etumerkitöntä primitiivityyppiä, jonka alueelle kaikki luettelointiarvot sopivat; Kääntäjä päättää, mitä tyyppiä käyttää. Vakioiden eksplisiittisten arvojen on kuitenkin oltava lausekkeita, kuten int[18] .

Luettelotyyppi voi olla myös anonyymi, jos luettelon nimeä ei ole määritetty. Kahdessa eri enumissa määritetyt vakiot ovat kahta eri tietotyyppiä riippumatta siitä, ovatko enumit nimettyjä vai anonyymejä.

Käytännössä luetteloita käytetään usein osoittamaan äärellisten automaattien tiloja , asettamaan vaihtoehtoja toimintatiloihin tai parametriarvoihin [54] , luomaan kokonaislukuvakioita ja myös luettelemaan yksilöllisiä objekteja tai ominaisuuksia [55] .

Rakenteet

Rakenteet ovat eri tietotyyppien muuttujien yhdistelmä samalla muistialueella; merkitty avainsanalla struct. Rakenteen sisällä olevia muuttujia kutsutaan rakenteen kentiksi. Osoiteavaruuden kannalta kentät seuraavat aina toisiaan samassa järjestyksessä, jossa ne on määritelty, mutta kääntäjät voivat kohdistaa kenttäosoitteet optimoidakseen tietyn arkkitehtuurin mukaan. Siten itse asiassa kenttä voi olla suurempi kuin ohjelmassa on määritetty.

Jokaisella kentällä on tietty siirtymä suhteessa rakenteen osoitteeseen ja kokoon. Poikkeama saadaan käyttämällä makroa offsetof()otsikkotiedostosta stddef.h. Tässä tapauksessa siirtymä riippuu edellisten kenttien kohdistamisesta ja koosta. Kentän koko määräytyy yleensä rakenteen kohdistuksen mukaan: jos kentän tietotyypin kohdistuskoko on pienempi kuin rakenteen tasausarvo, kentän koon määrää rakenteen kohdistus. Tietotyyppikohdistus saadaan käyttämällä makroa alignof()[f] otsikkotiedostosta stdalign.h. Itse rakenteen koko on sen kaikkien kenttien kokonaiskoko, mukaan lukien kohdistus. Samaan aikaan jotkin kääntäjät tarjoavat erikoisattribuutteja, joiden avulla voit pakata rakenteita poistamalla niistä tasaukset [56] .

Rakennekentät voidaan nimenomaisesti asettaa kooltaan kaksoispisteellä erotettuina bitteinä kentän määrittelyn ja bittien lukumäärän jälkeen, mikä rajoittaa niiden mahdollisten arvojen vaihteluväliä kentän tyypistä riippumatta. Tätä lähestymistapaa voidaan käyttää vaihtoehtona lipuille ja bitmaskeille niiden käyttämiseksi. Bittimäärän määrittäminen ei kuitenkaan peruuta muistissa olevien rakenteiden kenttien mahdollista kohdistusta. Bittikenttien kanssa työskentelemisessä on useita rajoituksia: niihin on mahdotonta soveltaa operaattoria sizeoftai makroa alignof(), niihin on mahdotonta saada osoitinta.

Yhdistykset

Liitoksia tarvitaan, kun halutaan viitata samaan muuttujaan eri tietotyypeinä; merkitty avainsanalla union. Liiton sisällä voidaan ilmoittaa mielivaltainen määrä leikkaavia kenttiä, jotka itse asiassa tarjoavat pääsyn samalle muistialueelle eri tietotyypeinä. Liiton koon valitsee kääntäjä liiton suurimman kentän koon perusteella. On syytä muistaa, että yhden liiton kentän muuttaminen johtaa muutokseen kaikissa muissa kentissä, mutta vain muuttuneen kentän arvo on taatusti oikea.

Liitot voivat toimia kätevämpänä vaihtoehtona osoittimen heittämiselle mielivaltaiseen tyyppiin. Esimerkiksi rakenteeseen sijoitetun liitoksen avulla voit luoda objekteja dynaamisesti muuttuvilla tietotyypeillä:

Rakennekoodi tietotyypin muuttamiseen lennossa #include <stddef.h> enum arvo_tyyppi_t { VALUE_TYPE_LONG , // kokonaisluku VALUE_TYPE_DOUBLE , // todellinen luku VALUE_TYPE_STRING , // merkkijono VALUE_TYPE_BINARY , // mielivaltaiset tiedot }; struct binary_t { mitätön * tiedot ; // osoitin dataan size_t data_size ; // tiedon koko }; struct string_t { char * str ; // osoitin merkkijonoon koko_t str_size ; // merkkijonon koko }; liiton arvo_sisältö_t { long as_long ; // arvo kokonaislukuna double as_double ; // arvo reaalilukuna struct string_t as_string ; // arvo merkkijonona struct binary_t as_binary ; // arvo mielivaltaisena datana }; rakenne arvo_t { enum arvo_tyyppi_t -tyyppi ; // arvon tyyppi liiton arvo_sisältö_t sisältö ; // arvosisältö }; Taulukot

C:n taulukot ovat primitiivisiä ja ovat vain syntaktista abstraktiota osoittimen aritmetiikkaa vastaan . Joukko itsessään on osoitin muistialueelle, joten kaikki tiedot taulukon dimensiosta ja sen rajoista ovat käytettävissä vain käännöshetkellä tyyppimäärityksen mukaisesti. Taulukot voivat olla joko yksiulotteisia tai moniulotteisia, mutta taulukkoelementtiin pääsy edellyttää yksinkertaisesti siirtymän laskemista suhteessa taulukon alun osoitteeseen. Koska taulukot perustuvat osoitearitmetiikkaan, niiden kanssa on mahdollista työskennellä ilman indeksejä [57] . Joten esimerkiksi seuraavat kaksi esimerkkiä 10 numeron lukemisesta syöttövirrasta ovat identtisiä keskenään:

Indeksien kautta tehtävän työn vertailu osoitearitmetiikkaan perustuvaan työhön
Esimerkkikoodi hakemistojen käsittelyyn Esimerkkikoodi osoitearitmetiikkaa varten
#include <stdio.h> int a [ 10 ] = { 0 }; // Nolla alustus unsigned int count = sizeof ( a ) / sizeof ( a [ 0 ]); for ( int i = 0 ; i < count ; ++ i ) {     int * ptr = &a [ i ]; // Osoitin nykyiseen taulukon elementtiin int n = scanf ( "%8d" , ptr );         if ( n != 1 ) {         perror ( "arvon lukeminen epäonnistui" );         // Virhekatkon käsittely ;            } } #include <stdio.h> int a [ 10 ] = { 0 }; // Nolla alustus unsigned int count = sizeof ( a ) / sizeof ( a [ 0 ]); int * a_end = a + count ; // Osoitin elementille, joka seuraa viimeistä for ( int * ptr = a ; ptr != a_end ; ++ ptr ) { int n = scanf ( "%8d" , ptr ); if ( n != 1 ) { perror ( "arvon lukeminen epäonnistui" ); // Virhekatkon käsittely ; } }

Tunnetun kokoisten taulukoiden pituus lasketaan käännöshetkellä. C99-standardi esitteli mahdollisuuden ilmoittaa vaihtelevan pituisia taulukoita, joiden pituus voidaan asettaa ajon aikana. Tällaisille taulukoille on varattu muistia pinoalueelta, joten niitä on käytettävä varoen, jos niiden koko voidaan asettaa ohjelman ulkopuolelta. Toisin kuin dynaaminen muistin varaus, pinoalueen sallitun koon ylittäminen voi johtaa arvaamattomiin seurauksiin, ja negatiivinen taulukon pituus on määrittelemätön toiminta . Alkaen C11 :stä muuttuvapituiset taulukot ovat valinnaisia ​​kääntäjille, ja tuen puute määräytyy makron läsnäolosta __STDC_NO_VLA__[58] .

Paikallisiksi tai globaaleiksi muuttujiksi ilmoitetut kiinteän kokoiset taulukot voidaan alustaa antamalla niille alkuarvo aaltosulkeilla ja listaamalla taulukon elementit pilkuilla erotettuina. Globaalit taulukon alustajat voivat käyttää vain lausekkeita, jotka arvioidaan käännöshetkellä [59] . Tällaisissa lausekkeissa käytetyt muuttujat on ilmoitettava vakioina modifierilla const. Paikallisille taulukoille alustusohjelmat voivat sisältää lausekkeita, joissa on funktiokutsuja ja muita muuttujia, mukaan lukien osoitin itse ilmoitettuun taulukkoon.

C99-standardista lähtien rakenteiden viimeisenä elementtinä on sallittu ilmoittaa mielivaltaisen pituinen taulukko, jota käytetään laajasti käytännössä ja jota eri kääntäjät tukevat. Tällaisen taulukon koko riippuu rakenteelle varatun muistin määrästä. Tässä tapauksessa et voi ilmoittaa tällaisten rakenteiden joukkoa etkä voi sijoittaa niitä muihin rakenteisiin. Tällaista rakennetta koskevissa operaatioissa mielivaltaisen pituinen matriisi jätetään yleensä huomiotta, myös rakenteen kokoa laskettaessa, ja taulukon yli meneminen johtaa määrittelemättömään käyttäytymiseen [60] .

C-kieli ei tarjoa minkäänlaista ohjausta taulukon ulkopuolisiin rajoihin, joten ohjelmoijan on itse valvottava taulukoiden käyttöä. Virheet taulukon käsittelyssä eivät aina vaikuta suoraan ohjelman suorittamiseen, mutta voivat johtaa segmentointivirheisiin ja haavoittuvuuksiin .

Kirjoita synonyymit

C-kielen avulla voit luoda omia tyyppinimiä typedef. Vaihtoehtoisia nimiä voidaan antaa sekä järjestelmätyypeille että käyttäjän määrittämille. Tällaiset nimet ilmoitetaan globaalissa nimiavaruudessa, eivätkä ne ole ristiriidassa rakenteen, numeroinnin ja liittotyyppien nimien kanssa.

Vaihtoehtoisia nimiä voidaan käyttää sekä yksinkertaistamaan koodia että luomaan abstraktiotasoja. Esimerkiksi joitain järjestelmätyyppejä voidaan lyhentää koodin luettavuuden lisäämiseksi tai yhtenäistämiseksi käyttäjäkoodissa:

#include <stdint.h> typedef int32_t i32_t ; typedef int_fast32_t i32fast_t ; typedef int_least32_t i32least_t ; typedef uint32_t u32_t ; typedef uint_fast32_t u32fast_t ; typedef uint_least32_t u32least_t ;

Esimerkki abstraktiosta ovat tyyppien nimet käyttöjärjestelmien otsikkotiedostoissa. Esimerkiksi POSIX -standardi määrittelee tyypin pid_tnumeerisen prosessitunnuksen tallentamista varten. Itse asiassa tämä tyyppi on vaihtoehtoinen nimi jollekin primitiivityypille, esimerkiksi:

typedef int __kernel_pid_t ; typedef __kernel_pid_t __pid_t typedef __pid_t pid_t ;

Koska tyypit, joilla on vaihtoehtoiset nimet, ovat vain synonyymejä alkuperäisille tyypeille, niiden välinen täydellinen yhteensopivuus ja vaihdettavuus säilyy.

Esiprosessori

Esiprosessori toimii ennen kääntämistä ja muuntaa ohjelmatiedoston tekstin siinä havaittujen tai esiprosessorille välitettyjen ohjeiden mukaan . Teknisesti esiprosessori voidaan toteuttaa eri tavoin, mutta on loogisesti kätevää ajatella sitä erillisenä moduulina, joka käsittelee jokaisen käännettäväksi tarkoitetun tiedoston ja muodostaa tekstin, joka tulee kääntäjän syötteeseen. Esiprosessori etsii tekstistä rivejä, jotka alkavat merkillä #, jota seuraa esikäsittelijän käskyt. Kaikki mikä ei kuulu esiprosessoridirektiiveihin ja jota ei ole jätetty kääntämisen ulkopuolelle ohjeiden mukaan, välitetään kääntäjän syötteeseen muuttumattomana.

Esiprosessorin ominaisuuksia ovat:

  • tietyn lekseemin korvaaminen tekstillä käyttämällä direktiiviä #define, mukaan lukien kyky luoda parametroituja tekstimalleja (kutsutaan samalla tavalla kuin funktioita) sekä peruuttaa tällaiset korvaukset, mikä mahdollistaa korvaamisen ohjelman tekstin rajoitetuilla alueilla;
  • ehdollinen upotus ja osien poistaminen tekstistä, mukaan lukien itse käskyt, käyttämällä ehdollisia komentoja #ifdef, #ifndef, #if, #elseja #endif;
  • upota tekstiä toisesta tiedostosta nykyiseen tiedostoon käyttämällä #include.

On tärkeää ymmärtää, että esiprosessori tarjoaa vain tekstin korvaamisen, ottamatta huomioon kielen syntaksia ja semantiikkaa. Joten esimerkiksi makromäärityksiä #definevoi esiintyä funktioiden tai tyyppimääritelmien sisällä, ja ehdolliset käännösdirektiivit voivat johtaa koodin minkä tahansa osan poissulkemiseen ohjelman käännetystä tekstistä, kielen kielioppista riippumatta. Parametrisen makron kutsuminen eroaa myös funktion kutsumisesta, koska pilkuilla eroteltujen argumenttien semantiikkaa ei jäsennetä. Joten esimerkiksi taulukon alustusta on mahdotonta siirtää parametrisen makron argumenteille, koska sen elementit erotetaan myös pilkulla:

#define array_of(type, array) (((tyyppi) []) (taulukko)) int * a ; a = matriisi_of ( int , { 1 , 2 , 3 }); // käännösvirhe: // "array_of" -makro läpäisi 4 argumenttia, mutta se kestää vain 2

Makromäärityksiä käytetään usein varmistamaan yhteensopivuus kirjastojen eri versioiden kanssa, jotka ovat muuttaneet sovellusliittymiä , mukaan lukien tietyt koodiosat kirjaston versiosta riippuen. Näitä tarkoituksia varten kirjastot tarjoavat usein makromääritelmiä, jotka kuvaavat niiden versiota [61] , ja joskus makroja parametreillä, joiden avulla voidaan verrata nykyistä versiota esiprosessorissa määritettyyn versioon [62] . Makromäärityksiä käytetään myös ohjelman yksittäisten osien ehdolliseen kokoamiseen , esimerkiksi joidenkin lisätoimintojen tukemiseksi.

Parametreja sisältäviä makromäärityksiä käytetään laajalti C - ohjelmissa luomaan analogeja geneerisille funktioille . Aikaisemmin niitä käytettiin myös inline-toimintojen toteuttamiseen, mutta C99-standardin jälkeen tämä tarve on eliminoitu inline-toimintojen lisäyksen vuoksi. Kuitenkin, koska makromääritykset parametreilla eivät ole toimintoja, vaan niitä kutsutaan samalla tavalla, voi ilmetä odottamattomia ongelmia ohjelmointivirheen vuoksi, mukaan lukien vain osan makromääritelmän koodin käsittelystä [63] ja virheellisistä prioriteeteista. toimintojen suorittaminen [64] . Esimerkki virheellisestä koodista on neliöintimakro:

#include <stdio.h> int main ( tyhjä ) { #define SQR(x) x * x printf ( "%d" , SQR ( 5 )); // kaikki on oikein, 5*5=25 printf ( "%d" , SQR ( 5 + 0 )); // oletetaan olevan 25, mutta tulostaa 5 (5+0*5+0) printf ( "%d" , SQR ( 4/3 ) ) ; // kaikki on oikein, 1 (koska 4/3=1, 1*4=4, 4/3=1) printf ( "%d" , SQR ( 5/2 ) ) ; // sen pitäisi olla 4 (2*2), mutta tulostaa 5 (5/2*5/2) paluu 0 ; }

Yllä olevassa esimerkissä virhe on, että makroargumentin sisältö korvataan tekstiin sellaisenaan, ottamatta huomioon operaatioiden ensisijaisuutta. Tällaisissa tapauksissa sinun on käytettävä inline-funktioita tai nimenomaisesti priorisoitava operaattorit lausekkeissa, jotka käyttävät makroparametreja suluissa:

#include <stdio.h> int main ( tyhjä ) { #define SQR(x) ((x) * (x)) printf ( "%d" , SQR ( 4 + 1 )); // totta, 25 paluu 0 ; }

C-ohjelmointi

Ohjelman rakenne

Moduulit

Ohjelma on joukko C-tiedostoja, jotka voidaan kääntää objektitiedostoiksi . Tämän jälkeen objektitiedostot käyvät läpi linkitysvaiheen keskenään sekä ulkoisten kirjastojen kanssa, jolloin tuloksena on lopullinen suoritettava tiedosto tai kirjasto . Tiedostojen linkittäminen keskenään sekä kirjastojen kanssa vaatii kuvauksen käytettävien toimintojen prototyypeistä, ulkoisista muuttujista ja tarvittavista tietotyypeistä kussakin tiedostossa. Tällaiset tiedot on tapana sijoittaa erillisiin otsikkotiedostoihin , jotka yhdistetään käskyllä ​​niissä #include tiedostoissa, joissa vaaditaan tätä tai toista toiminnallisuutta, ja mahdollistaa moduulijärjestelmän kaltaisen järjestelmän järjestämisen. Tässä tapauksessa moduuli voi olla:

  • joukko yksittäisiä tiedostoja lähdekoodilla, joille käyttöliittymä esitetään otsikkotiedostojen muodossa;
  • objektikirjasto tai sen osa asianmukaisine otsikkotiedostoineen;
  • yhden tai useamman otsikkotiedoston itsenäinen joukko (rajapintakirjasto);
  • staattinen kirjasto tai osa siitä sopivilla otsikkotiedostoilla;
  • dynaaminen kirjasto tai osa siitä sopivilla otsikkotiedostoilla.

Koska direktiivi #includevain korvaa toisen tiedoston tekstin esikäsittelyvaiheessa , saman tiedoston sisällyttäminen useita kertoja voi johtaa käännösaikavirheisiin. Siksi tällaiset tiedostot suojaavat uudelleenkäyttöönottoa vastaan ​​makrojen ja #define [ #ifndef65] .

Lähdekooditiedostot

C-lähdekooditiedoston runko koostuu joukosta yleisiä datamääritelmiä, -tyyppejä ja -funktioita. Yleiset muuttujat ja funktiot, jotka on ilmoitettu ja-määritteiden avulla, staticovat inlinekäytettävissä vain siinä tiedostossa, jossa ne on ilmoitettu, tai kun tiedosto sisältyy toiseen tiedostoon #include. Tässä tapauksessa otsikkotiedostossa sanalla ilmoitetut funktiot ja muuttujat staticluodaan uudelleen aina, kun otsikkotiedosto yhdistetään seuraavaan lähdekoodin sisältävään tiedostoon. Globaalit muuttujat ja funktioprototyypit, jotka on ilmoitettu ulkoisella määritteellä, katsotaan sisällytetyiksi muista tiedostoista. Eli niitä saa käyttää kuvauksen mukaisesti; oletetaan, että ohjelman rakentamisen jälkeen linkkiohjelma linkittää ne tiedostoissa kuvattuihin alkuperäisiin objekteihin ja toimintoihin.

Yleisiä muuttujia ja funktioita, paitsi staticja inline, voidaan käyttää muista tiedostoista, jos ne on ilmoitettu siellä oikein määritteen kanssa extern. Muokkaajalla ilmoitettuja muuttujia ja funktioita staticvoidaan käyttää myös muissa tiedostoissa, mutta vain silloin, kun niiden osoite välitetään osoittimella. Kirjoita ilmoitukset typedef, structeikä unionniitä voi tuoda muihin tiedostoihin. Jos niitä on tarpeen käyttää muissa tiedostoissa, ne tulee kopioida sinne tai sijoittaa erilliseen otsikkotiedostoon. Sama koskee inline-funktioita.

Ohjelman sisääntulopiste

Suoritettavan ohjelman vakiolähtökohta on funktio nimeltä main, joka ei voi olla staattinen ja jonka on oltava ohjelmassa ainoa. Ohjelman suoritus alkaa funktion ensimmäisestä käskystä main()ja jatkuu sen poistumiseen asti, jonka jälkeen ohjelma lopettaa ja palauttaa käyttöjärjestelmälle abstraktin kokonaislukukoodin työnsä tuloksesta.

Kelvolliset funktioprototyypit main()[66]
ei argumentteja Komentoriviargumenteilla
int main ( void ); int main ( int argc , char ** argv );

Kutsuttaessa muuttuja argcvälitetään ohjelmalle lähetettyjen argumenttien määrä, mukaan lukien polku itse ohjelmaan, joten argc-muuttuja sisältää yleensä arvon, joka on vähintään 1. Itse argvohjelman käynnistysrivi välitetään muuttujalle taulukkona. merkkijonoista, joiden viimeinen elementti on NULL. Kääntäjä takaa, että main()kaikki ohjelman globaalit muuttujat alustetaan, kun toimintoa ajetaan [67] .

Tämän seurauksena funktio main()voi palauttaa minkä tahansa kokonaisluvun tyypin arvoalueella int, joka välitetään käyttöjärjestelmään tai muuhun ympäristöön ohjelman palautuskoodina [66 ] . Kielistandardi ei määrittele paluukoodien merkitystä [68] . Yleensä käyttöjärjestelmällä, jossa ohjelmat ovat käynnissä, on jokin keino saada palautuskoodin arvo ja analysoida se. Joskus näiden koodien merkityksestä on tiettyjä sopimuksia. Yleinen käytäntö on, että palautuskoodi nolla osoittaa ohjelman onnistuneen valmistumisen, kun taas nollasta poikkeava arvo edustaa virhekoodia. Otsikkotiedosto stdlib.hmäärittelee kaksi yleistä makromääritelmää EXIT_SUCCESSja EXIT_FAILURE, jotka vastaavat ohjelman onnistunutta ja epäonnistunutta valmistumista [68] . Paluukoodeja voidaan käyttää myös sovelluksissa, jotka sisältävät useita prosesseja viestinnän tarjoamiseksi näiden prosessien välillä, jolloin sovellus itse määrittää kunkin palautuskoodin semanttisen merkityksen.

Työskentely muistin kanssa

Muistimalli

C tarjoaa neljä tapaa varata muistia, jotka määrittävät muuttujan eliniän ja sen alustushetken [67] .

Muistin varausmenetelmät [67]
Valintamenetelmä Tavoitteet Valinnan aika julkaisuaika Yleiskulut
Staattinen muistin varaus Yleiset muuttujat ja muuttujat, jotka on merkitty avainsanalla static(mutta ilman _Thread_local) Ohjelman alkaessa Ohjelman lopussa Puuttuu
Muistin varaus säietasolla Muuttujat merkitty avainsanalla_Thread_local Kun lanka alkaa Virran lopussa Kun luot lankaa
Automaattinen muistin varaus Funktioiden argumentit ja palautusarvot, funktioiden paikalliset muuttujat, mukaan lukien vaihtelevan pituiset rekisterit ja taulukot Kutsuttaessa toimintoja pinotasolla . Automaattinen toimintojen päätyttyä Merkitön, koska vain pinon yläosassa oleva osoitin muuttuu
Dynaaminen muistin varaus Muisti jaettu toimintojen malloc()ja calloc()jarealloc() Manuaalisesti kasosta käytetyn funktion kutsumishetkellä. Manuaalisesti käyttämällä toimintoafree() Suuri sekä jakamiseen että vapauttamiseen

Kaikki nämä tiedon tallennusmenetelmät sopivat erilaisiin tilanteisiin ja niillä on omat etunsa ja haittansa. Globaalit muuttujat eivät anna sinun kirjoittaa uudelleentuloalgoritmeja , ja automaattinen muistin varaus ei salli mielivaltaisen muistialueen palauttamista funktiokutsusta . Automaattinen allokointi ei myöskään sovellu suurten muistimäärien varaamiseen, koska se voi johtaa pinon tai kasan korruptioon [69] . Dynaamisessa muistissa ei ole näitä puutteita, mutta sillä on suuri yläraja sen käytössä ja sitä on vaikeampi käyttää.

Mikäli mahdollista, automaattista tai staattista muistin varausta suositellaan: tätä objektien tallennustapaa ohjaa kääntäjä , mikä vapauttaa ohjelmoijan manuaalisen varauksen ja muistin vapauttamisen vaivasta, joka on yleensä vaikeasti löydettävien muistivuotojen syy. segmentointivirheet ja virheiden vapauttaminen ohjelmassa . Valitettavasti monet tietorakenteet ovat kooltaan vaihtelevia ajon aikana, joten koska automaattisesti ja staattisesti allokoiduilla alueilla on oltava tiedossa kiinteä koko käännöshetkellä, on hyvin yleistä käyttää dynaamista allokointia.

Automaattisesti allokoiduille muuttujille registervoidaan käyttää muuntajaa, joka vihjaa kääntäjälle, jotta ne voivat nopeasti käyttää niitä. Tällaisia ​​muuttujia voidaan sijoittaa prosessorirekistereihin. Rekistereiden rajallisesta määrästä ja mahdollisista kääntäjien optimoinneista johtuen muuttujat voivat päätyä tavalliseen muistiin, mutta niistä ei kuitenkaan ole mahdollista saada osoitinta ohjelmasta [70] . Muokkaus registeron ainoa, joka voidaan määrittää funktion argumenteissa [71] .

Muistin osoitus

C-kieli peri lineaarisen muistiosoitteen työskennellessään rakenteiden, taulukoiden ja allokoitujen muistialueiden kanssa. Kielistandardi mahdollistaa myös vertailutoimintojen suorittamisen nollaosoittimille ja osoitteille taulukoissa, rakenteissa ja varatuilla muistialueilla. On myös sallittua työskennellä viimeistä seuraavan taulukkoelementin osoitteen kanssa, mikä tehdään kirjoitusalgoritmien helpottamiseksi. Eri muuttujille (tai muistialueille) saatujen osoiteosoittimien vertailua ei kuitenkaan pidä suorittaa, koska tulos riippuu tietyn kääntäjän toteutuksesta [72] .

Muistin esitys

Ohjelman muistiesitys riippuu laitteistoarkkitehtuurista, käyttöjärjestelmästä ja kääntäjästä. Joten esimerkiksi useimmissa arkkitehtuureissa pino kasvaa alas, mutta on arkkitehtuureja, joissa pino kasvaa [73] . Pinon ja pinon välinen raja voidaan osittain suojata pinon ylivuodolta erityisellä muistialueella [74] . Ja kirjastojen datan ja koodin sijainti voi riippua käännösvaihtoehdoista [75] . C-standardi abstrahoituu toteutuksesta ja mahdollistaa kannettavan koodin kirjoittamisen, mutta prosessin muistirakenteen ymmärtäminen auttaa virheenkorjauksessa ja turvallisten ja vikasietoisten sovellusten kirjoittamisessa.

Tyypillinen esitys prosessimuistista Unix-tyyppisissä käyttöjärjestelmissä

Kun ohjelma käynnistetään suoritettavasta tiedostosta, prosessoriohjeet (konekoodi) ja alustetut tiedot tuodaan RAM-muistiin. main()Samanaikaisesti komentoriviargumentit (saatavilla funktioissa , joissa on seuraava allekirjoitus toisessa argumentissa int argc, char ** argv) ja ympäristömuuttujat tuodaan korkeampiin osoitteisiin .

Alustamaton tietoalue sisältää globaaleja muuttujia (mukaan lukien ne, jotka on ilmoitettu muodossa static), joita ei ole alustettu ohjelmakoodissa. Tällaiset muuttujat alustetaan oletusarvoisesti nollaan ohjelman käynnistymisen jälkeen. Alustavien tietojen alue - datasegmentti - sisältää myös globaaleja muuttujia, mutta tämä alue sisältää ne muuttujat, joille on annettu alkuarvo. Muuttumaton data, mukaan lukien muuttujat, jotka on ilmoitettu muuntimella const, merkkijonoliteraalit ja muut yhdistelmäliteraalit, sijoitetaan ohjelman tekstisegmenttiin. Ohjelman tekstisegmentti sisältää myös suoritettavaa koodia ja on vain luku -tilassa, joten yritys muokata tämän segmentin tietoja johtaa määrittelemättömään toimintaan segmentointivian muodossa .

Pinoalueen on tarkoitus sisältää funktiokutsuihin ja paikallisiin muuttujiin liittyvää dataa. Ennen jokaista funktion suoritusta pino laajennetaan funktiolle välitettävien argumenttien mukaan. Toimintansa aikana funktio voi allokoida pinossa paikallisia muuttujia ja varata sille muistia vaihtelevan pituisille taulukoille, ja jotkut kääntäjät tarjoavat myös keinoja varata pinon sisällä muistia kutsulla alloca(), joka ei sisälly kielistandardiin. . Toiminnon päätyttyä pino pienennetään arvoon, joka oli ennen kutsua, mutta tämä ei välttämättä tapahdu, jos pinoa käsitellään väärin. Dynaamisesti varattu muisti saadaan kasasta .

Tärkeä yksityiskohta on satunnainen täyttö pinon ja yläalueen välillä [77] sekä alustetun tietoalueen ja kasan välillä . Tämä tehdään turvallisuussyistä, kuten muiden toimintojen pinoamisen estämiseksi.

Dynaamiset linkkikirjastot ja tiedostojärjestelmän tiedostokartoitukset sijaitsevat pinon ja keon välissä [78] .

Virheiden käsittely

C:ssä ei ole sisäänrakennettuja virheenhallintamekanismeja, mutta on olemassa useita yleisesti hyväksyttyjä tapoja käsitellä virheitä kielen avulla. Yleensä C-virheiden käsittely vikasietoisessa koodissa pakottaa kirjoittamaan hankalia, usein toistuvia rakenteita, joissa algoritmi on yhdistetty virheenkäsittelyyn .

Virhemerkit ja errno

C-kieli käyttää aktiivisesti erikoismuuttujaa errnootsikkotiedostosta errno.h, jossa funktiot syöttävät virhekoodin ja palauttavat arvon, joka on virhemerkki. Tuloksen virheiden tarkistamiseksi tulosta verrataan virhemerkkiin, ja jos ne täsmäävät, voit analysoida tallennettua virhekoodia errnoohjelman korjaamiseksi tai virheenkorjausviestin näyttämiseksi. Standardikirjastossa standardi määrittelee usein vain palautetut virhemerkit, ja asetus errnoon toteutusriippuvainen [79] .

Seuraavat arvot toimivat yleensä virhemerkeinä:

  • -1tyypille inttapauksissa, joissa negatiivista tulosaluetta ei käytetä [80] ;
  • -1tyypille ssize_t(POSIX) [81] ;
  • (size_t) -1tyypille size_t[80] ;
  • (time_t) -1kun käytetään joitain toimintoja ajan kanssa työskentelemiseen [80] ;
  • NULLosoittimille [80] ;
  • EOFtiedostoja suoratoistettaessa [80] ;
  • nollasta poikkeava virhekoodi [80] .

Käytäntö palauttaa virhemerkki virhekoodin sijaan, vaikka se säästää funktiolle välitettyjen argumenttien määrän, johtaa joissakin tapauksissa virheisiin inhimillisen tekijän seurauksena. On esimerkiksi yleistä, että ohjelmoijat jättävät huomioimatta tyypin tyypin tuloksen tarkistamisen ssize_t, ja itse tulosta käytetään edelleen laskelmissa, mikä johtaa hienovaraisiin virheisiin, jos -1[82] palautetaan .

Oikean arvon palauttaminen virhemerkkinä [82] edistää edelleen virheiden ilmaantumista , mikä myös pakottaa ohjelmoijan tekemään enemmän tarkistuksia ja vastaavasti kirjoittamaan enemmän samantyyppistä toistuvaa koodia. Tätä lähestymistapaa harjoitetaan stream-funktioissa, jotka toimivat tyyppisten objektien kanssa FILE *: virhemerkki on arvo EOF, joka on myös tiedoston lopun merkki. Siksi EOFjoskus sinun on tarkistettava merkkivirta sekä tiedoston lopun osalta funktiolla feof(), että virheen olemassaolosta käyttämällä ferror()[83] . Samanaikaisesti joitain toimintoja, jotka voivat palata EOFstandardin mukaan, ei tarvitse asettaa errno[79] .

Yhtenäisen virheenkäsittelykäytännön puute vakiokirjastossa johtaa mukautettujen virheenkäsittelymenetelmien ilmestymiseen ja yleisesti käytettyjen menetelmien yhdistelmään kolmansien osapuolien projekteissa. Esimerkiksi systemd -projektissa ideat virhekoodin ja numeron palauttamisesta -1merkkinä yhdistettiin - palautetaan negatiivinen virhekoodi [84] . Ja GLib - kirjasto esitteli käytännön palauttaa loogisen arvon virhemerkkinä , kun taas virheen yksityiskohdat sijoitetaan erityiseen rakenteeseen, johon osoitin palautetaan funktion viimeisen argumentin kautta [85] . Samanlaista ratkaisua käyttää Enlightenment -projekti , joka käyttää myös Boolen tyyppiä merkkinä, mutta palauttaa virhetiedot, jotka ovat samanlaisia ​​kuin standardikirjasto - erillisen funktion [86] kautta , joka on tarkistettava, jos merkki palautettiin.

Virhekoodin palauttaminen

Vaihtoehto virhemarkkereille on palauttaa virhekoodi suoraan ja palauttaa funktion tulos osoitinargumenttien kautta. POSIX-standardin kehittäjät valitsivat tämän polun, jonka toiminnoissa on tapana palauttaa virhekoodi numerotyyppinä int. Tyyppiarvon palauttaminen intei kuitenkaan tee selväksi, että se on virhekoodi, joka palautetaan, eikä merkki, mikä voi johtaa virheisiin, jos tällaisten funktioiden tulos tarkistetaan arvoon -1. C11-standardin laajennus K esittelee erikoistyypin errno_tvirhekoodin tallentamiseen. On suositeltavaa käyttää tätä tyyppiä käyttäjäkoodissa virheiden palauttamiseksi, ja jos se ei ole vakiokirjastossa, ilmoita se itse [87] :

#ifndef __STDC_LIB_EXT1__ typedef int errno_t ; #loppu Jos

Tämä lähestymistapa koodin laadun parantamisen lisäksi poistaa tarpeen käyttää errno, jonka avulla voit tehdä kirjastoja, joissa on palautuvia toimintoja ilman, että sinun tarvitsee sisällyttää lisäkirjastoja, kuten POSIX Threads , määrittääksesi oikein errno.

Virheet matemaattisissa funktioissa

Monimutkaisempi on matemaattisten funktioiden virheiden käsittely otsikkotiedostosta math.h, jossa voi esiintyä kolmenlaisia ​​virheitä [88] :

  • ylittää syöttöarvojen alueen;
  • äärettömän tuloksen saaminen rajalliselle syöttödatalle;
  • tulos on käytetyn tietotyypin alueen ulkopuolella.

Kahden kolmesta virhetyypistä ehkäisy tarkoittaa, että syötetiedot tarkistetaan kelvollisten arvojen alueella. On kuitenkin erittäin vaikea ennustaa tuloksen tuottoa tyypin rajojen ulkopuolella. Siksi kielistandardi tarjoaa mahdollisuuden analysoida matemaattisia funktioita virheiden varalta. C99-standardista alkaen tämä analyysi on mahdollista kahdella tavalla, riippuen tiedostoon tallennetusta arvosta math_errhandling.

  1. Jos bitti on asetettu MATH_ERRNO, muuttuja errnoon ensin nollattava arvoon 0ja matemaattisen funktion kutsun jälkeen tarkista virheet EDOMja ERANGE.
  2. Jos bitti on asetettu MATH_ERREXCEPT, mahdolliset matemaattiset virheet nollataan aiemmin funktiolla feclearexcept()otsikkotiedostosta fenv.h, ja matemaattisen funktion kutsun jälkeen ne testataan funktiolla fetestexcept().

Tässä tapauksessa virheenkäsittelymenetelmä määräytyy vakiokirjaston tietyn toteutuksen mukaan, ja se voi puuttua kokonaan. Siksi alustariippumattomassa koodissa voi olla tarpeen tarkistaa tulos kahdella tavalla kerralla, riippuen math_errhandling[88] :n arvosta .

Resurssien vapauttaminen

Tyypillisesti virheen esiintyminen edellyttää, että toiminto poistuu ja palauttaa virheilmaisimen. Jos toiminnossa voi esiintyä virhe sen eri osissa, sen toiminnan aikana varatut resurssit on vapautettava vuotojen estämiseksi. On hyvä käytäntö vapauttaa resurssit käänteisessä järjestyksessä ennen paluuta funktiosta ja virheiden sattuessa käänteisessä järjestyksessä pääfunktion jälkeen return. Tällaisen julkaisun erillisissä osissa voit hypätä käyttämällä operaattoria goto[89] . Tämän lähestymistavan avulla voit siirtää koodin osia, jotka eivät liity toteutettavaan algoritmiin, itse algoritmin ulkopuolelle, mikä lisää koodin luettavuutta, ja se on samanlainen kuin deferGo- ohjelmointikielen operaattorin työ . Esimerkki resurssien vapauttamisesta on annettu alla esimerkkiosiossa .

Resurssien vapauttamiseksi ohjelman sisällä tarjotaan ohjelman poistumiskäsittelijämekanismi. Käsittelijät määritetään funktion avulla atexit()ja ne suoritetaan sekä funktion lopussa main()käskyn kautta returnettä funktion suorittamisen yhteydessä exit(). Tässä tapauksessa funktiot abort()ja _Exit()[90] eivät suorita käsittelijöitä .

Esimerkki resurssien vapauttamisesta ohjelman lopussa on globaaleille muuttujille varatun muistin vapauttaminen. Huolimatta siitä, että muisti vapautuu tavalla tai toisella sen jälkeen, kun käyttöjärjestelmä lopettaa ohjelman, ja koko ohjelman toiminnan aikana tarvittavaa muistia ei saa vapauttaa [91] , eksplisiittisen purkaminen on suositeltavaa, koska se tekee siitä helpompi löytää muistivuotoja kolmansien osapuolien työkaluilla ja vähentää virheestä johtuvien muistivuotojen mahdollisuutta:

Esimerkkiohjelmakoodi resurssien julkaisulla #include <stdio.h> #include <stdlib.h> int numeroiden_määrä ; int * numerot ; void free_numbers ( void ) { ilmainen ( numerot ); } int main ( int argc , char ** argv ) { if ( arg < 2 ) { exit ( EXIT_FAILURE ); } numeroiden_määrä = atoi ( argv [ 1 ]); if ( numeroiden_määrä <= 0 ) { exit ( EXIT_FAILURE ); } numerot = calloc ( numeroiden_määrä , koko ( * numerot )); if ( ! numerot ) { perror ( "Virhe varattaessa muistia taulukolle" ); exit ( EXIT_FAILURE ); } atexit ( free_numbers ); // ... työskennellä numerotaulukon kanssa // Free_numbers()-käsittelijä kutsutaan automaattisesti tänne paluu EXIT_SUCCESS ; }

Tämän lähestymistavan haittana on, että määritettävien käsittelijöiden muoto ei salli mielivaltaisten tietojen välittämistä funktiolle, mikä mahdollistaa käsittelijöiden luomisen vain globaaleille muuttujille.

Esimerkkejä C-ohjelmista

Minimaalinen C-ohjelma

Minimaalinen C-ohjelma, joka ei vaadi argumenttien käsittelyä, on seuraava:

int main ( void ){}

returnFunktiolle ei saa kirjoittaa operaattoria main(). Tässä tapauksessa funktio main()palauttaa standardin mukaan 0:n ja suorittaa kaikki funktiolle annetut käsittelijät exit(). Tämä olettaa, että ohjelma on suoritettu onnistuneesti [40] .

Hei maailma!

Hei maailma! on Kernighanin ja Ritchien kirjan " The C Programming Language " ensimmäisessä painoksessa:

#include <stdio.h> int main ( void ) // Ei ota argumentteja { printf ( "Hei, maailma! \n " ); // '\n' - uusi rivi paluu 0 ; // Onnistunut ohjelman lopettaminen }

Tämä ohjelma tulostaa viestin Hello, world! ' vakiolähdöllä .

Virheiden käsittely käyttämällä tiedoston lukua esimerkkinä

Monet C-funktiot voivat palauttaa virheen tekemättä sitä, mitä niiden piti tehdä. Virheet on tarkistettava ja niihin on vastattava oikein, mukaan lukien usein tarve heittää virhe funktiosta korkeammalle tasolle analysointia varten. Samalla toiminto, jossa virhe tapahtui, voidaan tehdä reentrant , jolloin toiminto ei vahingossa saisi muuttaa tulo- tai lähtötietoja, jolloin voit käynnistää sen turvallisesti uudelleen virhetilanteen korjaamisen jälkeen.

Esimerkki toteuttaa funktion tiedoston lukemiseksi C-kielellä, mutta se edellyttää funktioiden fopen()ja POSIXfread() - standardin noudattamista , muuten ne eivät ehkä aseta muuttujaa , mikä vaikeuttaa suuresti sekä virheenkorjausta että yleisen ja turvallisen koodin kirjoittamista. Muilla kuin POSIX-alustoilla tämän ohjelman toiminta on määrittelemätön -virheen sattuessa . Virheiden resurssien jakaminen on pääalgoritmin takana luettavuuden parantamiseksi, ja siirto tapahtuu käyttämällä [89] . errnogoto

Tiedostonlukijan esimerkkikoodi virheenkäsittelyllä #include <errno.h> #include <stdio.h> #include <stdlib.h> // Määritä virhekoodin tallennustyyppi, jos sitä ei ole määritetty #ifndef __STDC_LIB_EXT1__ typedef int errno_t ; #loppu Jos enum { EOK = 0 , // errno_t:n arvo onnistumiselle }; // Toiminto tiedoston sisällön lukemiseen errno_t get_file_contents ( const char * tiedostonimi , void ** contents_ptr , koko_t * contents_size_ptr ) { TIEDOSTO * f ; f = fopen ( tiedostonimi , "rb" ); if ( ! f ) { // POSIXissa fopen() asettaa errno vahingossa palautusvirhe ; _ } // Hae tiedostokoko fseek ( f , 0 , SEEK_END ); pitkä sisällön_koko = ftell ( f ); if ( contents_size == 0 ) { * contents_ptr = NULL ; * contents_size_ptr = 0 ; goto cleaning_fopen ; } kelaa taaksepäin ( f ); // Muuttuja palautetun virhekoodin tallentamiseen errno_t saved_errno ; void * sisältö ; contents = malloc ( sisällön_koko ); if ( ! sisältö ) { saved_errno = errno ; goto aborting_fopen ; } // Lue koko tiedoston sisältö sisältöosoittimesta koko_t n ; n = fread ( sisältö , sisällön_koko , 1 , f ); if ( n == 0 ) { // Älä tarkista feof():ta, koska puskuroitu fseek() jälkeen // POSIX fread() asettaa errno vahingossa saved_errno = errno ; goto aborting_contents ; } // Palauta varattu muisti ja sen koko * contents_ptr = sisältö ; * contents_size_ptr = sisällön_koko ; // Resurssien julkaisu -osio menestyksestä puhdistus_fopen : fclose ( f ); paluu EOK ; // Erillinen osio resurssien vahingossa vapauttamiseen aborting_contents : ilmainen ( sisältö ); aborting_fopen : fclose ( f ); palauttaa tallennettu_errno ; } int main ( int argc , char ** argv ) { if ( arg < 2 ) { palauttaa EXIT_FAILURE ; } const char * tiedostonimi = argv [ 1 ]; errno_t errnum ; void * sisältö ; koko_t sisällön_koko ; errnum = get_file_contents ( tiedoston nimi , & sisältö , & sisällön_koko ); if ( errnum ) { charbuf [ 1024 ] ; const char * error_text = strerror_r ( errnum , buf , sizeof ( buf )); fprintf ( stderr , "%s \n " , error_text ); exit ( EXIT_FAILURE ); } printf ( "%.*s" , ( int ) sisällön_koko , sisältö ); ilmainen ( sisältö ); paluu EXIT_SUCCESS ; }

Kehitystyökalut

Kääntäjät

Jotkut kääntäjät toimitetaan muiden ohjelmointikielien (mukaan lukien C++ ) kääntäjien kanssa tai ne ovat osa ohjelmistokehitysympäristöä .

  • GNU Compiler Collection (GCC) tukee täysin C99- ja C17-standardeja ( C11 korjauksin) [92] . Se tukee myös GNU-laajennuksia, koodisuojausta desinfiointiaineilla ja monia lisäominaisuuksia, mukaan lukien attribuutit.
  • Clang tukee myös täysin C99 [93] ja C17 [94] standardeja . Kehitetty suurelta osin yhteensopivaksi GCC-kääntäjän kanssa, mukaan lukien tuki GNU-laajennuksille ja koodinpuhdistussuojaus.
Vakiokirjaston toteutukset

Huolimatta siitä, että standardikirjasto on osa kielistandardia, sen toteutukset ovat erillisiä kääntäjistä. Siksi kääntäjän ja kirjaston tukemat kielistandardit voivat poiketa toisistaan.

Integroidut kehitysympäristöt
  • CLion tukee täysin C99:ää, mutta C11:n tuki on osittainen [99] , koontiversio perustuu CMakeen.
  • Code::Blocks  on ilmainen cross-platform integroitu kehitysympäristö C-, C++-, D- ja Fortran-kielille. Tukee yli kahta tusinaa kääntäjää. GCC - kääntäjällä kaikki C-versiot C90:stä C17:ään ovat saatavilla.
  • Eclipse  on ilmainen IDE, joka tukee C99-standardin C-kieltä. Siinä on modulaarinen arkkitehtuuri, joka mahdollistaa eri ohjelmointikielien tuen ja lisäominaisuuksien yhdistämisen. Git - integraatiomoduuli on saatavilla , mutta CMake -integraatiota ei ole .
  • KDevelop  on ilmainen IDE, joka tukee joitain C11-standardin C-kielen ominaisuuksia. Voit hallita projekteja eri ohjelmointikielillä, mukaan lukien C++ ja Python , tukee CMake-koontijärjestelmää. Siinä on sisäänrakennettu tuki Gitille tiedostotasolla ja muokattava lähdekoodin muotoilu eri kielille.
  • Microsoft Visual Studio tukee vain osittain C99- ja C11-standardeja, koska se keskittyy C++-kehitykseen, mutta siinä on sisäänrakennettu tuki CMakelle.
Yksikkötestaustyökalut

Koska C-kieli ei tarjoa keinoa koodin kirjoittamiseen turvallisesti ja monet kielen elementit aiheuttavat virheitä, laadukkaan ja vikasietoisen koodin kirjoittaminen voidaan taata vain kirjoittamalla automaattisia testejä. Tällaisen testauksen helpottamiseksi on olemassa erilaisia ​​​​toteutuksia kolmannen osapuolen yksikkötestikirjastoista .

  • Check - kirjasto tarjoaa puitteet C-koodin testaamiseen yleisessä xUnit- tyylissä . Mahdollisuuksista voidaan mainita testien suorittaminen erillisissä prosesseissa kautta fork(), joka mahdollistaa segmentointivirheiden tunnistamisen testeissä [100] ja mahdollistaa myös yksittäisten testien enimmäissuoritusajan asettamisen.
  • Google Test -kirjasto tarjoaa myös xUnit-tyylistä testausta, mutta se on suunniteltu C++-koodin testaamiseen , joten sitä voidaan käyttää myös C-koodin testaamiseen. Se tukee myös ohjelman yksittäisten osien erillistä testausta. Yksi kirjaston eduista on testimakrojen erottaminen väitteiksi ja virheiksi, mikä voi helpottaa koodin virheenkorjausta.

On myös monia muita järjestelmiä C-koodin testaamiseen, kuten AceUnit, GNU Autounit, cUnit ja muut, mutta ne joko eivät testaa eristetyissä ympäristöissä, tarjoavat vain vähän ominaisuuksia [100] tai niitä ei enää kehitetä.

Virheenkorjaustyökalut

Virheiden ilmentymien perusteella ei aina ole mahdollista tehdä yksiselitteistä johtopäätöstä koodin ongelma-alueesta, mutta erilaiset virheenkorjaustyökalut auttavat usein paikallistamaan ongelman.

  • Gdb  on interaktiivinen konsolin virheenkorjausohjelma useille kielille, mukaan lukien C.
  • Valgrind on dynaaminen koodianalyysityökalu , joka voi havaita koodissa olevat virheet suoraan ohjelman suorituksen aikana. Tukee seuraavien havaitsemista: vuodot, alustamattoman muistin käyttö, virheellisten osoitteiden käyttö (mukaan lukien puskurin ylivuoto). Tukee myös suoritusta profilointitilassa callgrind [101] -profiilia käyttämällä .
  • KCacheGrind  on graafinen käyttöliittymä callgrind [102] -profiloijalla saatujen profilointitulosten visualisointiin .
Kääntäjät dynaamisille kielille ja alustoille

Joskus tiettyjen C-kielellä kirjoitettujen kirjastojen, toimintojen ja työkalujen siirtämiseksi toiseen ympäristöön on tarpeen kääntää C-koodi ylemmän tason kielelle tai sellaiselle kielelle suunnitellun virtuaalikoneen koodiksi. Seuraavat projektit on suunniteltu tähän tarkoitukseen:

Lisätyökalut

Myös C:lle on olemassa muita työkaluja, jotka helpottavat ja täydentävät kehitystä, mukaan lukien staattiset analysaattorit ja apuohjelmat koodin muotoiluun. Staattinen analyysi auttaa tunnistamaan mahdolliset virheet ja haavoittuvuudet. Ja automaattinen koodin muotoilu yksinkertaistaa yhteistyön järjestämistä versionhallintajärjestelmissä ja minimoi tyylimuutosten aiheuttamat ristiriidat.

  • Cppcheck on avoimen lähdekoodin  staattinen koodin analysaattori C:lle ja C++:lle , joka antaa joskus vääriä positiivisia tuloksia, jotka voidaan tukahduttaa koodissa olevilla erityisesti muotoilluilla kommenteilla.
  • Clang-format  on komentorivin apuohjelma lähdekoodin muotoiluun tietyn tyylin mukaan, joka voidaan määrittää erityisesti laaditussa asetustiedostossa. Siinä on monia vaihtoehtoja ja useita sisäänrakennettuja tyylejä. Kehitetty osana Clang -projektia [107] .
  • Indent- ja GNU Indent -apuohjelmat tarjoavat myös koodin muotoilun, mutta muotoiluvaihtoehdot on määritetty komentorivivaihtoehdoiksi [108] .

Laajuus

Kieltä käytetään laajalti käyttöjärjestelmien kehittämisessä, käyttöjärjestelmän API-tasolla, sulautetuissa järjestelmissä sekä tehokkaan tai virhekriittisen koodin kirjoittamiseen. Yksi syistä matalan tason ohjelmoinnin laajaan käyttöön on kyky kirjoittaa cross-platform-koodia, jota voidaan käsitellä eri tavalla eri laitteissa ja käyttöjärjestelmissä.

Kyky kirjoittaa korkean suorituskyvyn koodia tapahtuu ohjelmoijan täydellisen toimintavapauden ja kääntäjän tiukan valvonnan puuttumisen kustannuksella. Esimerkiksi Java , Python , Perl ja PHP ensimmäiset toteutukset kirjoitettiin C -kielellä. Samaan aikaan monissa ohjelmissa eniten resursseja vaativat osat kirjoitetaan yleensä C-kielellä. Mathematican [109] ydin on kirjoitettu C-kielellä, kun taas alun perin Fortranilla kirjoitettu MATLAB kirjoitettiin uudelleen C-kielellä vuonna 1984 [110] .

C:tä käytetään joskus myös välikielenä korkeamman tason kieliä käännettäessä. Esimerkiksi C++- , Objective-C- ja Go - kielten ensimmäiset toteutukset toimivat tämän periaatteen mukaisesti - näillä kielillä kirjoitettu koodi käännettiin C-kielen väliesitykseen. Samalla periaatteella toimivat nykykielet ovat Vala ja Nim .

Toinen C-kielen sovellusalue on reaaliaikaiset sovellukset , jotka vaativat koodin reagointikykyä ja sen suoritusaikaa. Tällaisten sovellusten on aloitettava toimenpiteiden suorittaminen tiukasti rajoitetun ajan sisällä, ja itse toimien tulee mahtua tiettyyn ajanjaksoon. Erityisesti POSIX.1- standardi tarjoaa joukon toimintoja ja ominaisuuksia reaaliaikaisten sovellusten rakentamiseen [111] [112] [113] , mutta käyttöjärjestelmän on myös otettava käyttöön kova reaaliaikainen tuki [114] .

Jälkiläiset kielet

C-kieli on ollut ja on edelleen yksi yleisimmin käytetyistä ohjelmointikielistä yli neljänkymmenen vuoden ajan. Luonnollisesti sen vaikutus voidaan jäljittää jossain määrin monissa myöhemmissä kielissä. Siitä huolimatta tietyn levinneisyyden saavuttaneiden kielten joukossa on vain vähän C:n suoria jälkeläisiä.

Jotkut jälkeläiskielet rakentuvat C:lle lisätyökaluilla ja -mekanismeilla, jotka lisäävät tukea uusille ohjelmointiparadigmoille ( OOP , toiminnallinen ohjelmointi , yleinen ohjelmointi jne.). Näihin kieliin kuuluvat ensisijaisesti C++ ja Objective-C sekä välillisesti niiden jälkeläiset Swift ja D. Tunnetaan myös yrityksiä parantaa C:tä korjaamalla sen merkittävimmät puutteet, mutta säilyttämällä sen houkuttelevat ominaisuudet. Niistä voidaan mainita tutkimuskieli Cyclone (ja sen jälkeläinen Rust ). Joskus molemmat kehityssuunnat yhdistetään yhdellä kielellä, Go on esimerkki .

Erikseen on mainittava koko joukko kieliä, jotka enemmän tai vähemmän ovat perineet C:n perussyntaksin (kiharasulkujen käyttö koodilohkojen erottimina, muuttujien ilmoittaminen, operaattorien tyypilliset muodot for, while, if, switchparametrit suluissa, yhdistetyt toiminnot ++, --, +=, -=ja muut) , minkä vuoksi näiden kielten ohjelmilla on tyypillinen ulkoasu, joka liittyy nimenomaan C:hen. Nämä ovat kieliä, kuten Java , JavaScript , PHP , Perl , AWK , C# . Itse asiassa näiden kielten rakenne ja semantiikka ovat hyvin erilaisia ​​kuin C, ja ne on yleensä tarkoitettu sovelluksiin, joissa alkuperäistä C:tä ei koskaan käytetty.

C++

C++ - ohjelmointikieli luotiin C:stä ja peri sen syntaksin täydentäen sitä uusilla rakenteilla Simula-67:n, Smalltalkin, Modula-2:n, Adan, Mesan ja Clu:n hengessä [116] . Tärkeimmät lisäykset olivat tuki OOP :lle (luokkakuvaus, moniperintö, virtuaalisiin funktioihin perustuva polymorfismi) ja geneerinen ohjelmointi (mallimoottori). Mutta tämän lisäksi kieleen on tehty monia erilaisia ​​lisäyksiä. Tällä hetkellä C++ on yksi laajimmin käytetyistä ohjelmointikielistä maailmassa ja se on sijoitettu yleiskäyttöiseksi kieleksi, joka painottaa järjestelmäohjelmointia [117] .

Aluksi C++ säilytti yhteensopivuuden C:n kanssa, mikä todettiin yhdeksi uuden kielen eduista. Ensimmäiset C++:n toteutukset yksinkertaisesti käänsivät uudet konstruktit puhtaaksi C:ksi, minkä jälkeen koodi käsiteltiin tavallisella C-kääntäjällä. Yhteensopivuuden säilyttämiseksi C++:n luojat kieltäytyivät jättämästä siitä pois joitakin C++:n usein kritisoiduista ominaisuuksista, vaan luovat sen sijaan uusia, "rinnakkaismekanismeja", joita suositellaan kehitettäessä uutta C++-koodia (mallit makrojen sijaan, eksplisiittinen tyyppisuora automaattisen sijaan , vakiokirjastosäilöjä manuaalisen dynaamisen muistin varauksen sijaan ja niin edelleen). Kielet ovat kuitenkin sittemmin kehittyneet itsenäisesti, ja nyt uusimpien standardien C ja C++ ovat vain osittain yhteensopivia: ei ole takeita siitä, että C++-kääntäjä onnistuu kääntämään C-ohjelman, ja jos onnistuu, ei ole takeita siitä, että käännetty ohjelma toimii oikein. Erityisen ärsyttäviä ovat jotkut hienovaraiset semanttiset erot, jotka voivat johtaa saman koodin erilaiseen käyttäytymiseen, joka on syntaktisesti oikein molemmilla kielillä. Esimerkiksi merkkivakioiden (merkkien sisällä lainausmerkeissä) tyyppi on intC ja tyyppi C++ , joten tällaisten vakioiden käyttämä muisti vaihtelee kielestä toiseen. [118] Jos ohjelma on herkkä merkkivakion koolle, se käyttäytyy eri tavalla, kun se käännetään C- ja C++-kääntäjillä. char

Tällaiset erot vaikeuttavat sellaisten ohjelmien ja kirjastojen kirjoittamista, jotka voivat kääntää ja toimia samalla tavalla sekä C:ssä että C++ :ssa , mikä tietysti hämmentää molemmilla kielillä ohjelmoivia. Sekä C- että C++-kehittäjien ja käyttäjien keskuudessa kannattavat kielten välisten erojen minimoimista, mikä toisi objektiivisesti konkreettisia etuja. On kuitenkin päinvastainen näkemys, jonka mukaan yhteensopivuus ei ole erityisen tärkeää, vaikka se on hyödyllistä, eikä yhteensopivuuden vähentäminen saa estää kunkin kielen kehittämistä erikseen.

Objective-C

Toinen vaihtoehto C:n laajentamiseen oliopohjaisilla työkaluilla on vuonna 1983 luotu Objective-C- kieli. Olioalijärjestelmä lainattiin Smalltalkilta ja kaikki tähän alijärjestelmään liittyvät elementit on toteutettu omassa syntaksissaan, joka eroaa melko jyrkästi C-syntaksista (johon asti luokkakuvauksissa kenttien ilmoittamisen syntaksi on päinvastainen kuin C:n muuttujien ilmoittamisen syntaksi: ensin kirjoitetaan kentän nimi, sitten sen tyyppi). Toisin kuin C++, Objective-C on klassisen C:n superjoukko, eli se säilyttää yhteensopivuuden lähdekielen kanssa; oikea C-ohjelma on oikea Objective-C-ohjelma. Toinen merkittävä ero C++-ideologiaan on se, että Objective-C toteuttaa objektien vuorovaikutusta vaihtamalla täysimittaisia ​​viestejä, kun taas C++ toteuttaa käsitteen "viestin lähettäminen menetelmäkutsuna". Täysi viestien käsittely on paljon joustavampaa ja sopii luonnollisesti rinnakkaislaskentaan. Objective-C sekä sen suora jälkeläinen Swift ovat suosituimpia Applen tukemilla alustoilla .

Ongelmia ja kritiikkiä

C-kieli on ainutlaatuinen siinä mielessä, että se oli ensimmäinen korkean tason kieli , joka syrjäytti kokoajan järjestelmäohjelmistojen kehittämisessä . Se on edelleen kieli, jota käytetään eniten laitteistoalustoilla ja yksi suosituimmista ohjelmointikielistä , erityisesti vapaiden ohjelmistojen maailmassa [119] . Siitä huolimatta kielessä on monia puutteita, ja sen perustamisesta lähtien monet asiantuntijat ovat kritisoineet sitä.

Yleinen kritiikki

Kieli on hyvin monimutkaista ja täynnä vaarallisia elementtejä, joita on erittäin helppo käyttää väärin. Rakenteellaan ja säännöillään se ei tue ohjelmointia, jonka tavoitteena on luoda luotettavaa ja ylläpidettävää ohjelmakoodia, vaan päinvastoin, eri prosessoreille suoritetun ohjelmoinnin aikakaudella syntynyt kieli myötävaikuttaa vaarallisen ja hämmentävän koodin kirjoittamiseen [119] . Monet ammattiohjelmoijat ajattelevat, että C-kieli on tehokas työkalu tyylikkäiden ohjelmien luomiseen, mutta samalla sillä voidaan luoda erittäin huonolaatuisia ratkaisuja [120] [121] .

Kielen erilaisten oletusten vuoksi ohjelmat voivat kääntää useita virheitä, mikä usein johtaa arvaamattomaan ohjelman toimintaan. Nykyaikaiset kääntäjät tarjoavat vaihtoehtoja staattiseen koodin analysointiin [122] [123] , mutta nekään eivät pysty havaitsemaan kaikkia mahdollisia virheitä. Lukutaidoton C-ohjelmointi voi aiheuttaa ohjelmiston haavoittuvuuksia , jotka voivat vaikuttaa sen käytön turvallisuuteen .

Xi:llä on korkea pääsykynnys [119] . Sen määrittely vie yli 500 sivua tekstiä, joka on tutkittava kokonaan, koska virheettömän ja laadukkaan koodin luomiseksi on otettava huomioon monet kielen epäselvät ominaisuudet. Esimerkiksi kokonaislukulausekkeiden operandien automaattinen syöttö kirjoitukseen intvoi antaa vaikeasti ennustettavia tuloksia käytettäessä binäärioperaattoreita [44] :

etumerkitön merkki x = 0xFF ; etumerkitön char y = ( ~ x | 0x1 ) >> 1 ; // Intuitiivisesti tässä odotetaan 0x00 printf ( "y = 0x%hhX \n " , y ); // Tulostaa 0x80, jos sizeof(int) > sizeof(char)

Tällaisten vivahteiden ymmärtämisen puute voi johtaa lukuisiin virheisiin ja haavoittuvuuksiin. Toinen C:n hallitsemisen monimutkaisuutta lisäävä tekijä on kääntäjän palautteen puute: kieli antaa ohjelmoijalle täydellisen toimintavapauden ja mahdollistaa ohjelmien kääntämisen, joissa on ilmeisiä loogisia virheitä. Kaikki tämä vaikeuttaa C :n käyttöä opetuksessa ensimmäisenä ohjelmointikielenä [119]

Lopuksi, yli 40 vuoden olemassaolon aikana kieli on vanhentunut, ja on melko ongelmallista käyttää siinä monia moderneja ohjelmointitekniikoita ja paradigmoja .

Tiettyjen kielen elementtien haitat

Primitiivisen modulaarisuuden tuki

C-syntaksissa ei ole moduuleja ja mekanismeja niiden vuorovaikutukseen. Lähdekooditiedostot kootaan erikseen, ja niiden tulee sisältää muista tiedostoista tuotujen muuttujien, funktioiden ja tietotyyppien prototyyppejä. Tämä tehdään sisällyttämällä otsikkotiedostot makrokorvauksen avulla . Jos kooditiedostojen ja otsikkotiedostojen välinen vastaavuus rikotaan, voi tapahtua sekä linkkiaikavirheitä että kaikenlaisia ​​ajonaikaisia ​​virheitä: pinon ja kasan korruptiosta segmentointivirheisiin . Koska direktiivi korvaa vain yhden tiedoston tekstin toisella, suuren määrän otsikkotiedostojen sisällyttäminen johtaa siihen, että käännettävän koodin todellinen määrä moninkertaistuu, mikä on syynä suhteellisen hitaalle suorituskyvylle. C-kääntäjät. Tarve koordinoida kuvaukset päämoduulissa ja otsikkotiedostoissa vaikeuttaa ohjelman ylläpitoa. #include#include

Varoitukset virheiden sijaan

Kielistandardi antaa ohjelmoijalle enemmän toimintavapautta ja siten suuren mahdollisuuden tehdä virheitä. Suurin osa siitä, mikä ei ole useimmiten sallittua, on kielen sallimaa, ja kääntäjä antaa parhaimmillaan varoituksia. Vaikka nykyaikaiset kääntäjät mahdollistavat kaikkien varoitusten muuntamisen virheiksi, tätä ominaisuutta käytetään harvoin, ja useimmiten varoitukset ohitetaan, jos ohjelma toimii tyydyttävästi.

Joten esimerkiksi ennen C99-standardia funktion kutsuminen mallocilman otsikkotiedostoa stdlib.hsaattoi johtaa pinon vioittumiseen, koska prototyypin puuttuessa funktiota kutsuttiin palauttavana tyypin int, kun taas itse asiassa se palautti tyypin void*( virhe tapahtui, kun kohdealustan tyyppien koot erosivat). Siitä huolimatta se oli vain varoitus.

Muuttujien alustuksen hallinnan puute

Automaattisesti ja dynaamisesti luotuja objekteja ei alustata oletusarvoisesti, ja kun ne on luotu, ne sisältävät arvot, jotka ovat jääneet muistiin aiemmin siellä olleista objekteista. Tällainen arvo on täysin arvaamaton, se vaihtelee koneesta toiseen, ajosta suoritukseen, funktiokutsusta kutsuun. Jos ohjelma käyttää tällaista arvoa alustuksen vahingossa tekemättä jättämisen vuoksi, tulos on arvaamaton eikä välttämättä näy heti. Nykyaikaiset kääntäjät yrittävät diagnosoida tämän ongelman staattisella lähdekoodin analyysillä, vaikka yleensä ongelman ratkaiseminen staattisen analyysin avulla on erittäin vaikeaa. Lisätyökaluja voidaan käyttää näiden ongelmien tunnistamiseen testausvaiheessa ohjelman suorittamisen aikana: Valgrind ja MemorySanitizer [124] .

Osoitteiden aritmeettisen hallinnan puute

Vaarallisten tilanteiden lähde on osoittimien yhteensopivuus numeeristen tyyppien kanssa ja mahdollisuus käyttää osoitearitmetiikkaa ilman tiukkaa valvontaa käännös- ja suoritusvaiheissa. Tämä mahdollistaa osoittimen saamisen mihin tahansa objektiin, myös suoritettavaan koodiin, ja viitata tähän osoittimeen, ellei järjestelmän muistin suojaus estä tätä.

Osoittimien virheellinen käyttö voi aiheuttaa ohjelman määrittelemätöntä toimintaa ja johtaa vakaviin seurauksiin. Osoitin voi esimerkiksi olla alustamatta tai virheellisten aritmeettisten operaatioiden seurauksena osoittaa mielivaltaiseen muistipaikkaan. Joillakin alustoilla tällaisen osoittimen käyttäminen saattaa pakottaa ohjelman pysähtymään, toisilla se voi vioittaa mielivaltaisia ​​muistissa olevia tietoja. Viimeinen virhe on vaarallinen, koska sen seuraukset ovat arvaamattomia ja voivat ilmaantua milloin tahansa, myös paljon myöhemmin kuin todellisen virheellisen toiminnan hetki.

Pääsy taulukoihin C:ssä on myös toteutettu osoitearitmetiikalla, eikä se tarkoita keinoja tarkistaa taulukon elementtien oikeellisuuden indeksin perusteella. Esimerkiksi lausekkeet a[i]ja i[a]ovat identtisiä ja ne käännetään yksinkertaisesti muotoon *(a + i), eikä rajojen ulkopuolisten taulukoiden tarkistusta suoriteta. Pääsy indeksillä, joka on suurempi kuin taulukon yläraja, johtaa taulukon jälkeen olevassa muistissa olevaan dataan, jota kutsutaan puskurin ylivuodoksi . Kun tällainen kutsu on virheellinen, se voi johtaa arvaamattomaan ohjelman toimintaan [57] . Usein tätä ominaisuutta käytetään hyväksikäytöissä , joilla päästään laittomasti toisen sovelluksen muistiin tai käyttöjärjestelmän ytimen muistiin.

Virheille altis dynaaminen muisti

Järjestelmätoiminnot dynaamisesti varatun muistin kanssa työskentelyyn eivät tarjoa hallintaa sen varauksen ja vapauttamisen oikeellisuudesta ja oikea-aikaisuudesta, vaan dynaamisen muistin kanssa työskentelyn oikean järjestyksen noudattaminen on täysin ohjelmoijan vastuulla. Sen virheet voivat johtaa vääriin osoitteisiin pääsyyn, ennenaikaiseen julkaisuun tai muistivuotoon (jälkimmäinen on mahdollista esimerkiksi, jos kehittäjä unohti soittaa free()tai kutsua kutsuvaan free()toimintoon, kun sitä vaadittiin) [125] .

Yksi yleisimmistä virheistä on, että muistinvaraustoimintojen ( malloc(), calloc()ja muut) tulosta ei tarkisteta -sovelluksessa NULL, kun taas muistia ei välttämättä varata, jos sitä ei ole tarpeeksi tai jos sitä pyydettiin liikaa, esim. -1virheellisten matemaattisten operaatioiden seurauksena saadun luvun vähentäminen etumerkittömään tyyppiin size_tja siihen liittyvät myöhemmät operaatiot . Toinen ongelma järjestelmän muistifunktioissa on määrittelemätön käyttäytyminen pyydettäessä nollakokoista lohkovarausta: funktiot voivat palauttaa joko tai todellisen osoitinarvon, riippuen tietystä toteutuksesta [126] . NULL

Jotkut erityiset toteutukset ja kolmannen osapuolen kirjastot tarjoavat ominaisuuksia, kuten viitteiden laskemisen ja heikkojen viittausten [127] , älykkäiden osoittimien [128] ja rajoitetun jätteenkeräyksen [129] , mutta kaikki nämä ominaisuudet eivät ole vakiomuotoisia, mikä luonnollisesti rajoittaa niiden käyttöä. .

Tehottomat ja vaaralliset merkkijonot

Kielelle nollapääteiset merkkijonot ovat vakiona, joten kaikki vakiofunktiot toimivat niiden kanssa. Tämä ratkaisu johtaa merkittävään tehokkuuden heikkenemiseen vähäisistä muistinsäästöistä (verrattuna koon nimenomaiseen tallentamiseen): merkkijonon pituuden (funktio ) laskeminen vaatii silmukan koko merkkijonon läpi alusta loppuun, myös merkkijonojen kopiointi on vaikeaa. optimoida päättävän nollan läsnäolon vuoksi [48] . Koska merkkijonotietoihin on lisättävä päättävä nolla, on mahdotonta saada tehokkaasti osamerkkijonoja viipaleina ja työskennellä niiden kanssa kuten tavallisten merkkijonojen kanssa; merkkijonojen osien varaaminen ja käsitteleminen vaatii yleensä manuaalista muistin varaamista ja purkamista, mikä lisää edelleen virheiden mahdollisuutta. strlen()

Nollapääteiset merkkijonot ovat yleinen virhelähde [130] . Edes standardifunktiot eivät yleensä tarkista kohdepuskurin kokoa [130] eivätkä välttämättä lisää tyhjää merkkiä [131] merkkijonon loppuun , puhumattakaan siitä, että sitä ei ehkä lisätä tai ylikirjoittaa ohjelmointivirheen vuoksi. [132] .

Variadic-funktioiden vaarallinen toteutus

Vaikka C tukee funktioita, joissa on vaihteleva määrä argumentteja , se ei tarjoa keinoa määrittää sellaiselle funktiolle siirrettyjen todellisten parametrien määrää ja tyyppejä, eikä mekanismia niiden turvalliseen käyttöön [133] . Ohjelmoijan tehtävänä on ilmoittaa funktiolle todellisten parametrien koostumuksesta, ja niiden arvoihin pääsemiseksi on tarpeen laskea oikea tavumäärä pinon viimeisen kiinteän parametrin osoitteesta joko manuaalisesti tai käyttämällä joukkoa makroja va_argotsikkotiedostosta stdarg.h. Samanaikaisesti on otettava huomioon automaattisen implisiittisen tyypin edistämisen mekanismin toiminta funktioita kutsuttaessa [134] , jonka mukaan kokonaislukutyyppiset argumentit, jotka ovat pienempiä kuin int, heitetään kohtaan int(tai unsigned int), mutta floatheitetään osoitteeseen double. Virhe kutsussa tai funktion sisällä olevien parametrien kanssa tulee näkyviin vain ohjelman suorituksen aikana, mikä johtaa arvaamattomiin seurauksiin virheellisten tietojen lukemisesta pinon korruptoitumiseen.

printf()Samaan aikaan funktiot, joissa on vaihteleva määrä parametreja ( ja scanf()muita), jotka eivät pysty tarkistamaan, vastaako argumenttiluettelo muotomerkkijonoa , ovat muotoillun I/O:n vakiokeino . Monet nykyaikaiset kääntäjät suorittavat tämän tarkistuksen jokaiselle kutsulle ja luovat varoituksia, jos he löytävät ristiriidan, mutta yleensä tämä tarkistus ei ole mahdollista, koska jokainen variadic-funktio käsittelee tätä luetteloa eri tavalla. Edes kaikkia funktiokutsuja on mahdotonta ohjata staattisesti, printf()koska muotomerkkijono voidaan luoda dynaamisesti ohjelmassa.

Virheenkäsittelyn yhtenäisyyden puute

C-syntaksi ei sisällä erityistä virheenkäsittelymekanismia. Vakiokirjasto tukee vain yksinkertaisimpia keinoja: otsikkotiedostosta tulevaa muuttujaa ( POSIX :n tapauksessa  makroa) viimeisen virhekoodin asettamiseksi ja toimintoja, jotka saavat koodien mukaiset virheilmoitukset. Tämä lähestymistapa johtaa tarpeeseen kirjoittaa suuri määrä toistuvaa koodia sekoittaen pääalgoritmin virheenkäsittelyyn, ja lisäksi se ei ole säikeen turvallista. Lisäksi edes tässä mekanismissa ei ole yhtä järjestystä: errnoerrno.h

  • virheen yhteydessä ja koodi itse on hankittava, jos funktio paljastaa sen;-1errno
  • POSIXissa on tapana palauttaa virhekoodi suoraan, mutta kaikki POSIX-toiminnot eivät tee niin;
  • monissa toiminnoissa, esimerkiksi , fopen()ja fread(), fwrite()asetusta errnoei ole standardoitu ja se voi vaihdella eri toteutuksissa [79] (POSIXissa vaatimukset ovat tiukemmat ja jotkin mahdollisten virheiden vaihtoehdot on määritelty );
  • on toimintoja, joissa virhemerkki on yksi sallituista palautusarvoista, ja ennen niiden kutsumista on asetettava nolla errnovarmistaakseen, että virhekoodi on asetettu tällä funktiolla [79] .

Vakiokirjastossa koodit errnomääritetään makromäärittelyillä ja niillä voi olla samat arvot, mikä tekee virhekoodien analysoinnin operaattorin kautta mahdottomaksi switch. Kielellä ei ole erityistä tietotyyppiä lipuille ja virhekoodeille, ne välitetään tyypin arvoina int. Erillinen tyyppi errno_tvirhekoodin tallentamiseen ilmestyi vain C11-standardin K-laajennuksessa, eikä kääntäjät välttämättä tue sitä [87] .

Tapoja voittaa kielen puutteet

C:n puutteet ovat olleet hyvin tiedossa jo pitkään, ja kielen perustamisesta lähtien on yritetty parantaa C-koodin laatua ja turvallisuutta sen ominaisuuksista tinkimättä.

Keinot koodin oikeellisuuden analysointiin

Lähes kaikki nykyaikaiset C-kääntäjät mahdollistavat rajoitetun staattisen koodin analyysin ja varoittavat mahdollisista virheistä. Vaihtoehtoja tuetaan myös taulukon rajojen ulkopuolella, pinon tuhoutumisen, keon ulkopuolella olevien rajoitusten, alustamattomien muuttujien, määrittelemättömän toiminnan jne. tarkistusten upottamiseen koodiin. Lisätarkistukset voivat kuitenkin vaikuttaa lopullisen sovelluksen suorituskykyyn, joten ne ovat käytetään useimmiten vain virheenkorjausvaiheessa.

On olemassa erityisiä ohjelmistotyökaluja C-koodin staattiseen analysointiin ei-syntaksivirheiden havaitsemiseksi. Niiden käyttö ei takaa ohjelmien virheettömyyttä, mutta mahdollistaa merkittävän osan tyypillisistä virheistä ja mahdollisista haavoittuvuuksista tunnistamisen. Näiden työkalujen suurin vaikutus ei saavuteta satunnaisella käytöllä, vaan kun niitä käytetään osana vakiintunutta jatkuvan koodin laadunvalvontajärjestelmää, esimerkiksi jatkuvassa integraatio- ja käyttöönottojärjestelmissä. Koodi voi myös olla tarpeen merkitä erityisillä kommenteilla, jotta analysaattorin väärät hälytykset voidaan sulkea pois oikeista koodin osista, jotka muodollisesti kuuluvat virheellisten kriteerien piiriin.

Suojatut ohjelmointistandardit

Oikeasta C-ohjelmoinnista on julkaistu huomattava määrä tutkimusta pienistä artikkeleista pitkiin kirjoihin. C-koodin laadun ylläpitämiseksi otetaan käyttöön yritys- ja teollisuusstandardeja. Erityisesti:

  • MISRA C  on Motor Industry Software Reliability Associationin kehittämä standardi C:n käyttöön ajoneuvojen sulautettujen järjestelmien kehittämisessä. Nyt MISRA C:tä käytetään monilla teollisuudenaloilla, mukaan lukien armeija, lääketiede ja ilmailu. Vuoden 2013 painos sisältää 16 direktiiviä ja 143 sääntöä, mukaan lukien koodivaatimukset ja rajoitukset tiettyjen kieliominaisuuksien käyttöön (esimerkiksi funktioiden käyttö, joissa on vaihteleva määrä parametreja, on kielletty). Markkinoilla on noin tusina MISRA C -koodintarkistustyökalua ja useita kääntäjiä, joissa on sisäänrakennettu MISRA C -rajoitusten tarkistus.
  • CERT C -koodausstandardi  on CERT Coordinating Centerin [135] kehittämä standardi . Sen tavoitteena on myös tarjota luotettava ja turvallinen C-ohjelmointi. Sisältää säännöt ja ohjeet kehittäjille, mukaan lukien tapauskohtaisia ​​esimerkkejä virheellisestä ja oikeasta koodista. Standardia käyttävät tuotekehityksessä sellaiset yritykset kuin Cisco ja Oracle [136] .
POSIX-standardit

POSIX -standardisarja kompensoi joitain kielen puutteita . Asennus on standardisoitu errnomonilla toiminnoilla, mikä mahdollistaa esimerkiksi tiedostotoimintojen virheiden käsittelyn ja joidenkin standardikirjaston toimintojen säikeen turvallisia analogeja esitellään, joiden turvalliset versiot ovat kielistandardissa vain K-laajennus [137] .

Katso myös

Muistiinpanot

Kommentit

  1. B on englannin aakkosten toinen kirjain ja C on englannin aakkosten kolmas kirjain .
  2. Otsikkotiedoston makro on avainsanan päälle kääre .boolstdbool.h_Bool
  3. Otsikkotiedoston makro on avainsanan päälle kääre .complexcomplex.h_Complex
  4. Otsikkotiedoston makro on avainsanan päälle kääre .imaginarycomplex.h_Imaginary
  5. Otsikkotiedoston makro on avainsanan päälle kääre .alignasstdalign.h_Alignas
  6. 1 2 3 Otsikkotiedoston makro on avainsanan päälle kääre .alignofstdalign.h_Alignof
  7. Otsikkotiedoston makro on avainsanan päälle kääre .noreturnstdnoreturn.h_Noreturn
  8. Otsikkotiedoston makro on avainsanan päälle kääre .static_assertassert.h_Static_assert
  9. Otsikkotiedoston makro on avainsanan päälle kääre .thread_localthreads.h_Thread_local
  10. 1 2 3 4 5 6 7 Merkittyjen ja allekirjoittamattomien , ja -tyyppien ensiesiintyminen charoli shortK &R C:ssä.intlong
  11. 1 2 Tyyppiformaatin IEC 60559 -standardin mukainen tyyppi määritellään C-laajennuksella F, joten muoto voi vaihdella eri alustoilla tai kääntäjillä.floatdouble

Lähteet

  1. 1 2 http://www.bell-labs.com/usr/dmr/www/chist.html
  2. Rui Ueyama. Kuinka kirjoitin itseisännöivän C-kääntäjän 40  päivässä . www.sigbus.info (joulukuu 2015). Haettu 18. helmikuuta 2019. Arkistoitu alkuperäisestä 23. maaliskuuta 2019.
  3. Roskankerääjä C:lle ja C++:lle Arkistoitu 13. lokakuuta 2005 Wayback Machinessa 
  4. Olio-ohjelmointi ANSI-C:llä arkistoitu 6. maaliskuuta 2016 Wayback Machinessa 
  5. Instantiable luokiteltu tyypit:  objektit . GObject Reference Manual . developer.gnome.org. Haettu 27. toukokuuta 2019. Arkistoitu alkuperäisestä 27. toukokuuta 2019.
  6. Muuttumattomat luokitellut tyypit:  rajapinnat . GObject Reference Manual . developer.gnome.org. Haettu 27. toukokuuta 2019. Arkistoitu alkuperäisestä 27. toukokuuta 2019.
  7. 1 2 C17-standardin luonnos , 5.2.1 Merkistöt, s. 17.
  8. 12 C17-standardin luonnos , 6.4.2 Tunnisteet, s. 43-44.
  9. C17-standardin luonnos , 6.4.4 Vakiot, s. 45-50.
  10. 1 2 Podbelsky, Fomin, 2012 , s. 19.
  11. 12 C17-standardin luonnos , 6.4.4.1 Kokonaislukuvakiot, s. 46.
  12. C17-standardin luonnos , 6.4.4.2 Kelluvat vakiot, s. 47-48.
  13. 1 2 C17-standardin luonnos , 6.4.4.4 Merkkivakiot, s. 49-50.
  14. STR30-C.  Älä yritä muokata merkkijonoliteraaaleja - SEI CERT C -koodausstandardi - Confluence . wiki.sei.cmu.edu. Haettu 27. toukokuuta 2019. Arkistoitu alkuperäisestä 27. toukokuuta 2019.
  15. C17-standardin luonnos , 6.4.5 Merkkijono literaalit, s. 50-52.
  16. Clang-Format Style Options - Clang 9  -dokumentaatio . clang.llvm.org. Haettu 19. toukokuuta 2019. Arkistoitu alkuperäisestä 20. toukokuuta 2019.
  17. ↑ 1 2 3 4 DCL06-C. Käytä merkityksellisiä symbolisia vakioita kirjaimellisten arvojen esittämiseen - SEI CERT C -koodausstandardi -  Confluence . wiki.sei.cmu.edu. Haettu 6. helmikuuta 2019. Arkistoitu alkuperäisestä 7. helmikuuta 2019.
  18. 1 2 C17 - standardin luonnos , s. 84.
  19. C17-standardin luonnos , 6.4.1 Avainsanat, s. 42.
  20. ↑ 1 2 Free Software Foundation (FSF). C99:n ominaisuuksien tila GCC:  ssä . GNU projekti . gcc.gnu.org. Haettu 31. toukokuuta 2019. Arkistoitu alkuperäisestä 3. kesäkuuta 2019.
  21. 1 2 3 4 C17-standardin luonnos , 7.1.3 Varatut tunnisteet, s. 132.
  22. C17-standardin luonnos , 6.5.3 Unaarioperaattorit, s. 63-65.
  23. C17-standardin luonnos , 6.5 Lausekkeet, s. 66-72.
  24. C17-standardin luonnos , 6.5.16 Osoitusoperaattorit, s. 72-74.
  25. C17-standardin luonnos , s. 55-75.
  26. ↑ 1 2 GNU C -viiteopas . 3.19 Operaattorin  etusija . www.gnu.org . Haettu 13. helmikuuta 2019. Arkistoitu alkuperäisestä 7. helmikuuta 2019.
  27. ↑ 1 2 3 4 5 EXP30-C. Älä ole riippuvainen sivuvaikutusten arviointijärjestyksestä - SEI CERT C -koodausstandardi -  Confluence . wiki.sei.cmu.edu. Haettu 14. helmikuuta 2019. Arkistoitu alkuperäisestä 15. helmikuuta 2019.
  28. ↑ 12BB . _ Määritelmät - SEI CERT C -koodausstandardi -  Confluence . wiki.sei.cmu.edu. Haettu 16. helmikuuta 2019. Arkistoitu alkuperäisestä 16. helmikuuta 2019.
  29. Podbelsky, Fomin, 2012 , 1.4. Operations, s. 42.
  30. Podbelsky, Fomin, 2012 , 2.3. Loop-lauseet, s. 78.
  31. ↑ 12 EXP19 -C. Käytä aaltosulkeet if-, for- tai while-lauseen rungossa - SEI CERT C -koodausstandardi -  Confluence . wiki.sei.cmu.edu. Haettu 2. kesäkuuta 2019. Arkistoitu alkuperäisestä 2. kesäkuuta 2019.
  32. Dynaamisesti ladatut (DL)  kirjastot . tldp.org. Haettu 18. helmikuuta 2019. Arkistoitu alkuperäisestä 12. marraskuuta 2020.
  33. 1 2 C17-standardin luonnos, 6.7.4 Toimintojen määrittelyt , s. 90-91.
  34. PRE00-C. Valitse rivin tai staattiset funktiot funktion kaltaisten makrojen sijaan - SEI CERT C Coding Standard -  Confluence . wiki.sei.cmu.edu. Haettu 4. kesäkuuta 2019. Arkistoitu alkuperäisestä 7. elokuuta 2021.
  35. C17-standardin luonnos , 6.11 Tulevaisuuden kieliohjeet, s. 130.
  36. Tukeeko C toimintojen ylikuormitusta? | GeeksforGeeks . Käyttöpäivä: 15. joulukuuta 2013. Arkistoitu alkuperäisestä 15. joulukuuta 2013.
  37. GNU C -viiteopas . www.gnu.org. Haettu 21. toukokuuta 2017. Arkistoitu alkuperäisestä 27. huhtikuuta 2021.
  38. Tyypin leveys (GNU C -kirjasto  ) . www.gnu.org. Haettu 7. joulukuuta 2018. Arkistoitu alkuperäisestä 9. joulukuuta 2018.
  39. C17-standardin luonnos , 6.2.5 Tyypit, s. 31.
  40. ↑ 1 2 Tekninen sekakomitea ISO/IEC JTC 1. ISO/IEC 9899:201x. Ohjelmointikielet - C. - ISO / IEC, 2011. - S. 14. - 678 s. Arkistoitu 30. toukokuuta 2017 Wayback Machineen
  41. Tarkista 0.10.0: 4.  Lisäominaisuudet . Tarkista . check.sourceforge.net. Haettu 11. helmikuuta 2019. Arkistoitu alkuperäisestä 18. toukokuuta 2018.
  42. Tyyppimuunnosmakrot: GLib-  viiteopas . developer.gnome.org. Haettu 14. tammikuuta 2019. Arkistoitu alkuperäisestä 14. tammikuuta 2019.
  43. INT01-C. Käytä arvoa rsize_t tai size_t kaikille kokonaislukuarvoille, jotka edustavat objektin kokoa - SEI CERT C -koodausstandardi -  Confluence . wiki.sei.cmu.edu. Haettu 22. helmikuuta 2019. Arkistoitu alkuperäisestä 7. elokuuta 2021.
  44. ↑ 1 2 3 INT02-C. Ymmärrä kokonaislukujen muunnossäännöt - SEI CERT C -koodausstandardi -  Confluence . wiki.sei.cmu.edu. Käyttöpäivä: 22. helmikuuta 2019. Arkistoitu alkuperäisestä 22. helmikuuta 2019.
  45. FLP02-C. Vältä liukulukujen käyttöä, kun tarvitaan tarkkaa laskentaa - SEI CERT C -koodausstandardi -  Confluence . wiki.sei.cmu.edu. Haettu 21. toukokuuta 2019. Arkistoitu alkuperäisestä 7. elokuuta 2021.
  46. 1 2 Luonnos standardista C17 , IEC 60559 liukulukuaritmetiikka, s. 370.
  47. C17-standardin luonnos , 7.12 Matematiikka <math.h>, s. 169-170.
  48. ↑ 1 2 Poul-Henning Camp. Kallein yhden tavun virhe - ACM-  jono . queue.acm.org (25. heinäkuuta 2011). Haettu 28. toukokuuta 2019. Arkistoitu alkuperäisestä 30. huhtikuuta 2019.
  49. ↑ 1 2 unicode (7) - Linux-käyttöopassivu  . man7.org. Haettu 24. helmikuuta 2019. Arkistoitu alkuperäisestä 25. helmikuuta 2019.
  50. ↑ 1 2 3 wchar_t mess - GNU libunistring  . www.gnu.org. Haettu 2. tammikuuta 2019. Arkistoitu alkuperäisestä 17. syyskuuta 2019.
  51. ↑ Ohjelmointi leveillä merkeillä  . linux.com | Linux-tietojen lähde (11. helmikuuta 2006). Haettu 7. kesäkuuta 2019. Arkistoitu alkuperäisestä 7. kesäkuuta 2019.
  52. Markus Kuhn . UTF-8 ja Unicode UKK  . www.cl.cam.ac.uk. Haettu 25. helmikuuta 2019. Arkistoitu alkuperäisestä 27. helmikuuta 2019.
  53. Vikailmoituksen yhteenveto C11:lle . www.open-std.org. Haettu 2. tammikuuta 2019. Arkistoitu alkuperäisestä 1. tammikuuta 2019.
  54. ↑ Vakioluettelot : GTK+ 3 -viiteopas  . developer.gnome.org. Haettu 15. tammikuuta 2019. Arkistoitu alkuperäisestä 14. tammikuuta 2019.
  55. ↑ Objektin ominaisuudet : GObject Reference Manual  . developer.gnome.org. Haettu 15. tammikuuta 2019. Arkistoitu alkuperäisestä 16. tammikuuta 2019.
  56. GNU Compiler Collectionin (GCC) käyttäminen: Yleiset  tyyppiattribuutit . gcc.gnu.org. Käyttöpäivä: 19. tammikuuta 2019. Arkistoitu alkuperäisestä 16. tammikuuta 2019.
  57. ↑ 12 ARR00 -C.  Ymmärrä taulukoiden toiminta - SEI CERT C -koodausstandardi - Confluence . wiki.sei.cmu.edu. Haettu 30. toukokuuta 2019. Arkistoitu alkuperäisestä 30. toukokuuta 2019.
  58. ARR32-C. Varmista, että muuttuvan pituisten taulukoiden kokoargumentit ovat kelvollisella alueella - SEI CERT C Coding Standard -  Confluence . wiki.sei.cmu.edu. Haettu 18. helmikuuta 2019. Arkistoitu alkuperäisestä 19. helmikuuta 2019.
  59. C17-standardin luonnos , 6.7.9 Alustus, s. 101.
  60. DCL38-C. Käytä oikeaa syntaksia, kun määrität joustavan taulukon jäsenen - SEI CERT C Coding Standard -  Confluence . wiki.sei.cmu.edu. Haettu 21. helmikuuta 2019. Arkistoitu alkuperäisestä 22. helmikuuta 2019.
  61. OpenSSL_versio  . _ www.openssl.org. Haettu 9. joulukuuta 2018. Arkistoitu alkuperäisestä 9. joulukuuta 2018.
  62. ↑ Versiotiedot : GTK+ 3 -viiteopas  . developer.gnome.org. Haettu 9. joulukuuta 2018. Arkistoitu alkuperäisestä 16. marraskuuta 2018.
  63. PRE10-C. Kääri monilausemakrot do-while-silmukaksi - SEI CERT C -koodausstandardi -  Confluence . wiki.sei.cmu.edu. Haettu 9. joulukuuta 2018. Arkistoitu alkuperäisestä 9. joulukuuta 2018.
  64. PRE01-C.  Käytä sulkuja makroissa parametrien nimien ympärillä - SEI CERT C Coding Standard - Confluence . wiki.sei.cmu.edu. Haettu 9. joulukuuta 2018. Arkistoitu alkuperäisestä 9. joulukuuta 2018.
  65. PRE06-C. Liitä otsikkotiedostot sisällyttää suoja-SEI CERT C Coding Standard-  Confluence -koodausstandardiin . wiki.sei.cmu.edu. Haettu 25. toukokuuta 2019. Arkistoitu alkuperäisestä 25. toukokuuta 2019.
  66. 1 2 Luonnos C17, 5.1.2.2 Isännöity ympäristö , s. 10-11.
  67. 1 2 3 C17-standardin luonnos , 6.2.4 Objektien säilytysajat, s. kolmekymmentä.
  68. 1 2 Luonnos C17, 7.22.4.4 Poistumistoiminto , s. 256.
  69. MEM05-C. Vältä suuria pinovarauksia - SEI CERT C -koodausstandardi -  Confluence . wiki.sei.cmu.edu. Haettu 24. toukokuuta 2019. Arkistoitu alkuperäisestä 24. toukokuuta 2019.
  70. C17 Luonnos , 6.7.1 Varastointiluokan määrittelyt, s. 79.
  71. C17-standardin luonnos , 6.7.6.3 Toimintojen ilmoittajat (mukaan lukien prototyypit), s. 96.
  72. Osoittimet C:ssä ovat abstraktimpia kuin luuletkaan . www.viva64.com. Haettu 30. joulukuuta 2018. Arkistoitu alkuperäisestä 30. joulukuuta 2018.
  73. Tanenbaum Andrew S, Bos Herbert. nykyaikaiset käyttöjärjestelmät. 4. painos . - Pietari. : Piter Publishing House, 2019. - S. 828. - 1120 s. — (Klassikot "Tietokoneet"). — ISBN 9785446111558 . Arkistoitu 7. elokuuta 2021 Wayback Machinessa
  74. Jonathan Corbet. Ripples Stack  Clashista . lwn.net (28. kesäkuuta 2017). Haettu 25. toukokuuta 2019. Arkistoitu alkuperäisestä 25. toukokuuta 2019.
  75. ELF-binäärien kovettaminen käyttämällä vain luku -toimintoa (RELRO  ) . www.redhat.com. Haettu 25. toukokuuta 2019. Arkistoitu alkuperäisestä 25. toukokuuta 2019.
  76. Perinteinen prosessiosoiteavaruus - staattinen  ohjelma . www.openbsd.org. Haettu 4. maaliskuuta 2019. Arkistoitu alkuperäisestä 8. joulukuuta 2019.
  77. Tohtori Thabang Mokoteli. ICMLG 2017 5th International Conference on Management Leadership and Governance . - Akateemiset konferenssit ja julkaisu rajoitettu, 2017-03. - S. 42. - 567 s. — ISBN 9781911218289 . Arkistoitu 7. elokuuta 2021 Wayback Machinessa
  78. Perinteinen prosessiosoiteavaruus - Ohjelma  jaetuilla kirjastoilla . www.openbsd.org. Haettu 4. maaliskuuta 2019. Arkistoitu alkuperäisestä 8. joulukuuta 2019.
  79. ↑ 1 2 3 4 ERR30-C. Aseta errno nollaan ennen kuin kutsut kirjastofunktiota, jonka tiedetään asettavan errno, ja tarkista errno vasta, kun funktio palauttaa virheen ilmaisevan arvon - SEI CERT C Coding Standard -  Confluence . wiki.sei.cmu.edu. Haettu 23. toukokuuta 2019. Arkistoitu alkuperäisestä 19. marraskuuta 2018.
  80. ↑ 1 2 3 4 5 6 ERR33-C. Tunnista ja käsittele standardikirjastovirheet - SEI CERT C -koodausstandardi -  Confluence . wiki.sei.cmu.edu. Haettu 23. toukokuuta 2019. Arkistoitu alkuperäisestä 23. toukokuuta 2019.
  81. sys_types.h.0p - Linux-  käyttöopassivu . man7.org. Haettu 23. toukokuuta 2019. Arkistoitu alkuperäisestä 23. toukokuuta 2019.
  82. ↑ 12 ERR02 -C. Vältä kaistan sisäisiä virheilmaisimia - SEI CERT C -koodausstandardi -  Confluence . wiki.sei.cmu.edu. Haettu 4. tammikuuta 2019. Arkistoitu alkuperäisestä 5. tammikuuta 2019.
  83. FIO34-C. Erota tiedostosta luetut merkit ja EOF tai WEOF-SEI CERT C Coding Standard-  Confluence . wiki.sei.cmu.edu. Käyttöpäivä: 4. tammikuuta 2019. Arkistoitu alkuperäisestä 4. tammikuuta 2019.
  84. Koodaustyyli  . _ Järjestelmän järjestelmä- ja palvelupäällikkö . github.com. Haettu 1. helmikuuta 2019. Arkistoitu alkuperäisestä 31. joulukuuta 2020.
  85. ↑ Virheilmoitus : GLib Reference Manual  . developer.gnome.org. Haettu 1. helmikuuta 2019. Arkistoitu alkuperäisestä 2. helmikuuta 2019.
  86. ↑ Eina : Virhe  . docs.enlightenment.org. Haettu 1. helmikuuta 2019. Arkistoitu alkuperäisestä 2. helmikuuta 2019.
  87. ↑ 1 2 DCL09-C. Ilmoita funktiot, jotka palauttavat errno:n palautustyypillä errno_t-SEI CERT C Coding Standard-Confluence . wiki.sei.cmu.edu. Haettu 21. joulukuuta 2018. Arkistoitu alkuperäisestä 21. joulukuuta 2018.
  88. ↑ 1 2 FLP32-C. Estä tai havaitse toimialueen ja alueen virheet matemaattisissa funktioissa - SEI CERT C -koodausstandardi -  Confluence . wiki.sei.cmu.edu. Haettu 5. tammikuuta 2019. Arkistoitu alkuperäisestä 5. tammikuuta 2019.
  89. ↑ 12 MEM12 -C. Harkitse goto-ketjun käyttöä, kun jätät funktion virhetilaan, kun käytät ja vapautat resursseja - SEI CERT C Coding Standard -  Confluence . wiki.sei.cmu.edu. Haettu 4. tammikuuta 2019. Arkistoitu alkuperäisestä 5. tammikuuta 2019.
  90. ERR04-C.  Valitse sopiva lopetusstrategia - SEI CERT C -koodausstandardi - Confluence . wiki.sei.cmu.edu. Haettu 4. tammikuuta 2019. Arkistoitu alkuperäisestä 5. tammikuuta 2019.
  91. MEM31-C. Vapaa dynaamisesti varattu muisti, kun sitä ei enää tarvita - SEI CERT C Coding Standard -  Confluence . wiki.sei.cmu.edu. Haettu 6. tammikuuta 2019. Arkistoitu alkuperäisestä 6. tammikuuta 2019.
  92. GNU Compiler Collectionin (GCC) käyttäminen:  Standardit . gcc.gnu.org. Haettu 23. helmikuuta 2019. Arkistoitu alkuperäisestä 17. kesäkuuta 2012.
  93. Kielten yhteensopivuus  . clang.llvm.org. Haettu 23. helmikuuta 2019. Arkistoitu alkuperäisestä 19. helmikuuta 2019.
  94. Clang 6.0.0:n julkaisutiedot – Clang 6:n dokumentaatio . releases.llvm.org. Haettu 23. helmikuuta 2019. Arkistoitu alkuperäisestä 23. helmikuuta 2019.
  95. Siddhesh Poyarekar - GNU C Libraryn versio 2.29 on nyt  saatavilla . sourceware.org. Haettu 2. helmikuuta 2019. Arkistoitu alkuperäisestä 2. helmikuuta 2019.
  96. Alpine Linux on siirtynyt käyttämään musl libc |  Alpine Linux . alpinelinux.org. Haettu 2. helmikuuta 2019. Arkistoitu alkuperäisestä 3. helmikuuta 2019.
  97. musl - Void Linux Handbook . docs.voidlinux.org . Haettu 29. tammikuuta 2022. Arkistoitu alkuperäisestä 9. joulukuuta 2021.
  98. CRT-kirjaston ominaisuudet . docs.microsoft.com. Haettu 2. helmikuuta 2019. Arkistoitu alkuperäisestä 7. elokuuta 2021.
  99. Tuetut kielet - Ominaisuudet | CLion  (englanniksi) . jetbrains. Haettu 23. helmikuuta 2019. Arkistoitu alkuperäisestä 25. maaliskuuta 2019.
  100. ↑ 1 2 Tarkista 0.10.0: 2. Yksikkötestaus C  . check.sourceforge.net. Haettu 23. helmikuuta 2019. Arkistoitu alkuperäisestä 5. kesäkuuta 2018.
  101. ↑ 6. Callgrind : puhelukaavio , joka luo välimuistin ja haaran ennusteen profiloijan  . Valgrind-dokumentaatio . valgrind.org. Haettu 21. toukokuuta 2019. Arkistoitu alkuperäisestä 23. toukokuuta 2019.
  102. Kcachegrind . kcachegrind.sourceforge.net. Haettu 21. toukokuuta 2019. Arkistoitu alkuperäisestä 6. huhtikuuta 2019.
  103. Emscripten LLVM-to-JavaScript-kääntäjä . Haettu 25. syyskuuta 2012. Arkistoitu alkuperäisestä 17. joulukuuta 2012.
  104. Flash C++ -kääntäjä . Haettu 25. tammikuuta 2013. Arkistoitu alkuperäisestä 25. toukokuuta 2013.
  105. Project Clue osoitteessa SourceForge.net
  106. Axiomatic Solutions Sdn Bhd . Käyttöpäivä: 7. maaliskuuta 2009. Arkistoitu alkuperäisestä 23. helmikuuta 2009.
  107. ClangFormat - Clang 9 -  dokumentaatio . clang.llvm.org. Haettu 5. maaliskuuta 2019. Arkistoitu alkuperäisestä 6. maaliskuuta 2019.
  108. luetelmakohta(1) - Linuxin man-  sivu . linux.die.net. Haettu 5. maaliskuuta 2019. Arkistoitu alkuperäisestä 13. toukokuuta 2019.
  109. Wolfram Research, Inc. JÄRJESTELMÄN LIITTYMÄT JA  KÄYTTÖÖNOTTO . Wolfram Mathematica® Tutorial Collection 36-37. library.wolfram.com (2008). Haettu 29. toukokuuta 2019. Arkistoitu alkuperäisestä 6. syyskuuta 2015.
  110. Cleve Moler. MATLABin ja MathWorksin kasvu kahden vuosikymmenen aikana . TheMathWorks News&Notes . www.mathworks.com (tammikuu 2006). Haettu 29. toukokuuta 2019. Arkistoitu alkuperäisestä 4. maaliskuuta 2016.
  111. sched_setscheduler  . _ pubs.opengroup.org. Käyttöpäivä: 4. helmikuuta 2019. Arkistoitu alkuperäisestä 24. helmikuuta 2019.
  112. clock_gettime  . _ pubs.opengroup.org. Käyttöpäivä: 4. helmikuuta 2019. Arkistoitu alkuperäisestä 24. helmikuuta 2019.
  113. clock_nanosleep  . _ pubs.opengroup.org. Käyttöpäivä: 4. helmikuuta 2019. Arkistoitu alkuperäisestä 24. helmikuuta 2019.
  114. M. Jones. Reaaliaikaisten Linux-arkkitehtuurien anatomia . www.ibm.com (30. lokakuuta 2008). Haettu 4. helmikuuta 2019. Arkistoitu alkuperäisestä 7. helmikuuta 2019.
  115. TIOBE- indeksi  . www.tiobe.com . Haettu 2. helmikuuta 2019. Arkistoitu alkuperäisestä 25. helmikuuta 2018.
  116. Stroustrup, Bjarne Kielen kehittäminen todellisessa maailmassa ja sitä varten: C++ 1991-2006 . Haettu 9. heinäkuuta 2018. Arkistoitu alkuperäisestä 20. marraskuuta 2007.
  117. Stroustrup UKK . www.stroustrup.com. Haettu 3. kesäkuuta 2019. Arkistoitu alkuperäisestä 6. helmikuuta 2016.
  118. Liite 0: Yhteensopivuus. 1.2. C++ ja ISO C . Työasiakirja luonnokseksi ehdotetun kansainvälisen standardin tietojärjestelmille - Ohjelmointikieli C++ (2. joulukuuta 1996). — katso 1.2.1p3 (kohta 3 kohdassa 1.2.1). Haettu 6. kesäkuuta 2009. Arkistoitu alkuperäisestä 22. elokuuta 2011.
  119. 1 2 3 4 Stolyarov, 2010 , 1. Esipuhe, s. 79.
  120. Kielten kronikka. Si . Kustantaja "Avoimet järjestelmät". Haettu 8. joulukuuta 2018. Arkistoitu alkuperäisestä 9. joulukuuta 2018.
  121. Allen I. Holub. Tarpeeksi köyttä ampuaksesi itseäsi jalkaan: C- ja C++-ohjelmoinnin säännöt . - McGraw-Hill, 1995. - 214 s. — ISBN 9780070296893 . Arkistoitu 9. joulukuuta 2018 Wayback Machinessa
  122. GNU Compiler Collection -kokoelman (GCC) käyttäminen: Varoitusasetukset . gcc.gnu.org. Haettu 8. joulukuuta 2018. Arkistoitu alkuperäisestä 5. joulukuuta 2018.
  123. Diagnostiset liput Clang-Clang 8 - dokumentaatiossa . clang.llvm.org. Haettu 8. joulukuuta 2018. Arkistoitu alkuperäisestä 9. joulukuuta 2018.
  124. MemorySanitizer - Clang 8 -  dokumentaatio . clang.llvm.org. Haettu 8. joulukuuta 2018. Arkistoitu alkuperäisestä 1. joulukuuta 2018.
  125. MEM00-C. Varaa ja vapauta muistia samassa moduulissa samalla abstraktiotasolla - SEI CERT C Coding Standard -  Confluence . wiki.sei.cmu.edu. Haettu 4. kesäkuuta 2019. Arkistoitu alkuperäisestä 4. kesäkuuta 2019.
  126. MEM04-C. Varo nollapituuden varauksia - SEI CERT C -koodausstandardi -  Confluence . wiki.sei.cmu.edu. Haettu 11. tammikuuta 2019. Arkistoitu alkuperäisestä 12. tammikuuta 2019.
  127. Objektimuistin hallinta: GObject Reference Manual . developer.gnome.org. Haettu 9. joulukuuta 2018. Arkistoitu alkuperäisestä 7. syyskuuta 2018.
  128. Esimerkiksi snai.pe c-smart-pointers Arkistoitu 14. elokuuta 2018 Wayback Machineen
  129. Roskien kerääminen C-ohjelmissa . Haettu 16. toukokuuta 2019. Arkistoitu alkuperäisestä 27. maaliskuuta 2019.
  130. ↑ 1 2 CERN-tietokoneturvatiedot . security.web.cern.ch. Haettu 12. tammikuuta 2019. Arkistoitu alkuperäisestä 5. tammikuuta 2019.
  131. CWE - CWE-170: Virheellinen nollapääte (3.2  ) . cwe.mitre.org. Haettu 12. tammikuuta 2019. Arkistoitu alkuperäisestä 13. tammikuuta 2019.
  132. STR32-C. Älä välitä ei-nollapääteistä merkkijonoa kirjastofunktiolle, joka odottaa merkkijonoa - SEI CERT C Coding Standard -  Confluence . wiki.sei.cmu.edu. Haettu 12. tammikuuta 2019. Arkistoitu alkuperäisestä 13. tammikuuta 2019.
  133. DCL50-CPP. Älä määritä C-tyylistä variadicista funktiota - SEI CERT C++ -koodausstandardi -  Confluence . wiki.sei.cmu.edu. Haettu 25. toukokuuta 2019. Arkistoitu alkuperäisestä 25. toukokuuta 2019.
  134. EXP47-C. Älä kutsu parametria va_arg väärän tyypin argumentilla - SEI CERT C Coding Standard -  Confluence . wiki.sei.cmu.edu. Haettu 8. joulukuuta 2018. Arkistoitu alkuperäisestä 9. joulukuuta 2018.
  135. SEI CERT C -koodausstandardi - SEI CERT C -koodausstandardi - Confluence . wiki.sei.cmu.edu. Haettu 9. joulukuuta 2018. Arkistoitu alkuperäisestä 8. joulukuuta 2018.
  136. Johdanto - SEI CERT C -koodausstandardi - Confluence . wiki.sei.cmu.edu. Haettu 24. toukokuuta 2019. Arkistoitu alkuperäisestä 24. toukokuuta 2019.
  137. CON33-C.  Vältä kilpailuolosuhteita käyttäessäsi kirjastotoimintoja - SEI CERT C -koodausstandardi - Confluence . wiki.sei.cmu.edu. Haettu 23. tammikuuta 2019. Arkistoitu alkuperäisestä 23. tammikuuta 2019.

Kirjallisuus

  • ISO/IEC. ISO/IEC9899:2017 . Ohjelmointikielet - C (downlink) . www.open-std.org (2017) . Haettu 3. joulukuuta 2018. Arkistoitu alkuperäisestä 24. lokakuuta 2018. 
  • Kernigan B. , Ritchie D. C-ohjelmointikieli = C-ohjelmointikieli. - 2. painos - M .: Williams , 2007. - S. 304. - ISBN 0-13-110362-8 .
  • Gukin D. C-ohjelmointikieli tutille = C For Dummies. - M . : Dialektiikka , 2006. - S. 352. - ISBN 0-7645-7068-4 .
  • Podbelsky V. V., Fomin S. S. Ohjelmointikurssi C-kielellä: oppikirja . - M. : DMK Press, 2012. - 318 s. - ISBN 978-5-94074-449-8 .
  • Prata S. C-ohjelmointikieli: Luennot ja harjoitukset = C Primer Plus. - M. : Williams, 2006. - S. 960. - ISBN 5-8459-0986-4 .
  • Prata S. C-ohjelmointikieli (C11). Luennot ja harjoitukset, 6. painos = C Primer Plus, 6. painos. - M. : Williams, 2015. - 928 s. - ISBN 978-5-8459-1950-2 .
  • Stolyarov A. V. C-kieli ja ohjelmoinnin peruskoulutus  // CMC MSU:n tiedekunnan nuorten tutkijoiden artikkelikokoelma. - Moskovan valtionyliopiston CMC:n tiedekunnan julkaisuosasto, 2010. - Nro 7 . - S. 78-90 .
  • Schildt G. C: The Complete Reference, Classic Edition = C: The Complete Reference, 4th Edition. - M .: Williams , 2010. - S. 704. - ISBN 978-5-8459-1709-6 .
  • Ohjelmointikielet Ada, C, Pascal = Ohjelmointikielten Ada, C ja Pascal vertailu ja arviointi / A. Feuer, N. Jehani. - M . : Radio ja Sayaz, 1989. - 368 s. – 50 000 kappaletta.  — ISBN 5-256-00309-7 .

Linkit