C- ja C++-yhteensopivuus

Kokeneet kirjoittajat eivät ole vielä tarkistaneet sivun nykyistä versiota, ja se voi poiketa merkittävästi 3.9.2022 tarkistetusta versiosta . vahvistus vaatii 1 muokkauksen .

C- ja C++ - ohjelmointikielet liittyvät läheisesti toisiinsa, mutta niillä on merkittäviä eroja. C++ luotiin esistandardoidun C:n jälkeläiseksi, suurimmaksi osaksi yhteensopiva sen kanssa tuolloin lähdekoodin ja linkityksen tasolla [1] [2] . Tämän seurauksena molempien kielten kehitystyökalut (kuten kehitysympäristöt ja kääntäjät ) integroidaan usein yhteen tuotteeseen ohjelmoijan valitseessa lähdekieleksi C tai C++.

C ei kuitenkaan ole C++ :n [3] osajoukko , joten ei-triviaalit C-ohjelmat eivät käänny C++:aan ilman muutoksia. C++ esittelee myös monia ominaisuuksia, joita ei ole saatavilla C:ssä, ja käytännössä lähes kaikki C++:lla kirjoitettu koodi ei vastaa C-koodia. Tässä artikkelissa keskitytään kuitenkin eroihin, jotka aiheuttavat sen, että vastaava C-koodi kirjoitetaan väärin . . huonosti muotoiltu ) koodi C++ tai vastaava/hyvin muotoiltu molemmilla kielillä, mutta voi käyttäytyä eri tavalla C- ja C++-kielissä .  

Björn Stroustrup , C++:n luoja, ehdotti [4] , että C:n ja C++:n välisiä yhteensopimattomuutta tulisi vähentää mahdollisimman paljon, jotta varmistetaan maksimaalinen yhteensopivuus näiden kahden kielen välillä. Toiset väittävät, että koska C ja C++ ovat kaksi eri kieltä, niiden välinen yhteensopivuus on hyödyllistä, mutta ei välttämätöntä; Niiden mukaan pyrkimykset vähentää yhteensopimattomuutta eivät saisi haitata pyrkimyksiä parantaa jokaista kieltä erikseen. Toiset taas väittävät, että melkein jokainen syntaksivirhe, joka voidaan tehdä C:ssä, on kirjoitettu uudelleen C++:ssa siten, että saadaan käännettävä, vaikkakaan ei välttämättä oikea koodi [5] . Vuoden 1999 C-standardin ( C99 ) virallinen perustelu "tukee periaatetta säilyttää suurin yhteinen osajoukko" C:n ja C++:n välillä, "säilyttäen niiden väliset erot ja sallien niiden kehittyä erikseen", siinä todetaan myös, että kirjoittajat olivat " iloinen siitä, että C++:sta on tullut suuri ja kunnianhimoinen kieli" [6] .

Joitakin C99-innovaatioita ei tueta nykyisessä C++-standardissa tai ne ovat ristiriidassa tiettyjen C++-ominaisuuksien, kuten muuttuvapituisten taulukoiden , monimutkaisten alkuperäisten tietotyyppien ja tyyppitunnisteen kanssa restrict . Toisaalta C99 on vähentänyt joitain muita yhteensopimattomuuksia verrattuna C89:ään sisällyttämällä C++-ominaisuudet, kuten yksiriviset kommentit //ja ilmoitus/koodisekoitus [7] .

Rakenteet ovat sallittuja C:ssä, mutta eivät C++:ssa

C++ pakottaa tiukemmat kirjoitussäännöt (ei implisiittisiä staattisen tyyppijärjestelmän rikkomuksia [1] ) ja alustusvaatimuksia (tarkistetaan käännöshetkellä, että laajuuden muuttujat eivät riko alustusta, eli ei ole mahdollista palata paikkaan ennen ilmoitusta eksplisiittinen tai implisiittinen alustus, paitsi lohkot, joihin ei-ohjattu vuo ei tullut) [8] , ja siksi jokin kelvollinen C-koodi ei ole sallittu C++:ssa. Perustelut tälle esitetään ISO C++ -standardin liitteessä C.1 [9] .

C99 ja C11 lisäsivät C:hen useita lisäominaisuuksia, joita ei ollut vakio C++:ssa, kuten kompleksiluvut, muuttuvapituiset taulukot (huomaa, että kompleksiluvut ja muuttuvapituiset taulukot on merkitty valinnaisiksi laajennuksiksi C11:ssä), joustava taulukkoelementti , rajoittava avainsana , taulukon parametrien tarkenteet, yhdistelmäliteraalit ja  nimetyt alustajat .

C++ lisää monia muita avainsanoja tukemaan uusia ominaisuuksia. Tämä tekee C-koodista, joka käyttää näitä avainsanoja tunnisteisiin, laitonta C++:ssa. Esimerkiksi tämä koodi:

rakennemalli _ { int uusi ; struct template * luokka ; }; templateon kelvollinen C-koodi, mutta C++-kääntäjä hylkää sen, koska , newja avainsanat on classvarattu.

Rakenteet, jotka käyttäytyvät eri tavalla C:ssä ja C++:ssa

On olemassa useita syntaksirakenteita, jotka ovat voimassa sekä C:ssä että C++:ssa, mutta tuottavat erilaisia ​​tuloksia näillä kielillä.

  • merkkiliteraaleilla , kuten'a', on tyyppiintC-kielessä ja tyyppicharC++-kielessä, mikä tarkoittaa, että sesizeof 'a'tuottaa yleensä erilaisia ​​tuloksia kahdella kielellä: C++:ssa se on1, kun taas C:ssä se onsizeof(int). Toinen seuraus tästä tyyppierosta, C:ssä'a'se on aina etumerkillinen lauseke riippumatta siitä, onko secharetumerkitty vai ei, kun taas C++:ssa se riippuukääntäjän toteutuksesta . 
  • C++ käyttää const-muuttujien sisäistä linkitystä nimiavaruuden laajuudessa, ellei niitä ole nimenomaisesti ilmoitettu muodossa extern, toisin kuin C, joka externon oletusarvo kaikille tiedostoalueille kuuluville entiteeteille .  Huomaa, että käytännössä tämä ei johda piilotettuihin semanttisiin muutoksiin identtisten C- ja C++-koodien välillä, vaan sen sijaan johtaa käännös- tai linkkivirheeseen.
  • C:ssä sisäisten funktioiden käyttö edellyttää, että avainsanaa käyttävä funktion prototyyppimääritys externlisätään manuaalisesti täsmälleen yhteen käännösyksikköön, jotta varmistetaan, että ei- inlineversio on linkitetty, kun taas C++ käsittelee tämän automaattisesti. Tarkemmin sanottuna C erottaa kahdenlaisia ​​sisäisiä funktiomääritelmiä: normaalit ulkoiset määritelmät (jossa 's' on nimenomaisesti käytetty extern) ja sisäisiä määritelmiä. C++ puolestaan ​​tarjoaa vain sisäänrakennetut määritelmät sisäänrakennetuille funktioille. C:ssä rivinsisäinen määritelmä on samanlainen kuin sisäinen (eli staattinen) määritelmä, koska se voi esiintyä samassa ohjelmassa yhden ulkoisen määritelmän ja minkä tahansa määrän saman funktion sisäisiä ja sisäisiä määritelmiä muissa käännösyksiköissä, kaikki jotka voivat vaihdella. Tämä ei ole sama kuin funktion linkitys , mutta ei täysin itsenäinen käsite. C-kääntäjät voivat valita saman funktion sisäisten ja ulkoisten määritelmien välillä, kun molemmat ovat saatavilla. C++ edellyttää kuitenkin, että jos ulkoisesti linkitetty funktio on ilmoitettu kuten inlinemissä tahansa käännösyksikössä, se on myös ilmoitettava (ja siten myös määriteltävä) jokaisessa käännösyksikössä, jossa sitä käytetään, ja että kaikki funktion määritelmät ovat samat yhden määritelmän sääntö. Huomaa, että staattiset sisäänrakennetut osat toimivat samalla tavalla C:ssä ja C++:ssa.
  • Sekä C99:llä että C++:lla on Boolen tyyppi bool vakioilla trueja false, mutta ne määritellään eri tavalla. C++ :ssa bool se on sisäänrakennettu tyyppi ja varattu avainsana . C99:ssä uusi avainsana _Boolesitellään uutena boolen tyyppinä. Otsikko stdbool.hsisältää makrot bool, trueja false, jotka määritellään vastaavasti _Bool, 1ja 0. Siksi trueja falsekirjoita intC-kirjaimella.

Joitakin muita eroja edelliseen osioon voidaan käyttää myös molemmilla kielillä kääntävän koodin luomiseen, mutta joka käyttäytyy eri tavalla. Esimerkiksi seuraava funktio palauttaa eri arvot C:ssä ja C++:ssa:

ulkoinen int T ; int koko ( tyhjä ) { struct T { int i ; int j ; }; palautuskoko ( T ) ; /* C: return sizeof(int) * C++: return sizeof(struct T) */ }

Tämä johtuu siitä, että C vaatii structrakenteen ennen tunnisteita (ja siksi sizeof(T)viittaa muuttujaan), mutta C++ sallii sen jättämisen pois (ja siksi sizeof(T)viittaa implisiittiseen typedef). Muista, että tulos on erilainen, kun ilmoitus externsijoitetaan funktion sisään: silloin samanniminen tunniste funktion laajuudessa estää implisiittisen typedefC++:n voimaantulon, ja tulos C++:lle on sama. Huomaa myös, että yllä olevan esimerkin epäselvyys johtuu sulkeiden käytöstä operaattorissa sizeof. Käytettäessä sizeof Tsen oletetaan Tolevan lauseke, ei tyyppi, joten esimerkkiä ei käännetä C++:ssa.

C- ja C++-koodin linkittäminen

Vaikka C ja C++ ylläpitävät korkeatasoista lähdeyhteensopivuutta, niiden kääntäjien luomilla objektitiedostoilla voi olla merkittäviä eroja, jotka näkyvät, kun C- ja C++-koodia sekoitetaan. Tärkeitä ominaisuuksia:

  • C-kääntäjät eivät suorita nimien muokkausta , kuten C++-kääntäjät [18] tekevät .
  • Kääntäjästä ja arkkitehtuurista riippuen kutsukäytännöt voivat vaihdella kielten välillä.

Jotta C++-koodi voi kutsua C-funktiota foo(), C++-koodin on luotava prototyyppi foo() käyttämällä extern "C". Vastaavasti, jotta C-koodi kutsuisi C++-funktiota, sen C++- bar()koodi bar()on ilmoitettava komennolla extern "C".

Yleinen käytäntö otsikkotiedostoissa yhteensopivuuden ylläpitämiseksi sekä C:n että C++:n kanssa on sisällyttää ilmoitus extern "C"koko otsikon laajuudelle [19] :

/* Otsikkotiedosto foo.h */ # ifdef __cplusplus /* Jos tämä on C++-kääntäjä, käytä linkitystä kuten C */ ulkoinen "C" { # loppu Jos /* Näillä funktioilla on C-asettelu */ voidfoo ( ); rakennepalkki { /* ... */ } ; # ifdef __cplusplus /* Jos tämä on C++-kääntäjä, poistu käyttämällä linkittämistä kuten C:ssä */ } # loppu Jos

Erot C- ja C++-linkitys- ja kutsukäytäntöjen välillä voivat myös vaikuttaa koodiin, joka käyttää funktioosoittimia. Jotkut kääntäjät rikkovat koodin, jos funktioosoitin ilmoittaa extern "C"C++-funktiolle, jota ei ole ilmoitettu extern "C"[20] .

Esimerkiksi seuraava koodi:

void my_function (); extern "C" void foo ( void ( * fn_ptr )( void )); voidbar ( ) { foo ( my_function ); }

Sun Microsystemsin C++-kääntäjä antaa seuraavan varoituksen:

$ CC - c testi . cc "test.cc " , rivi 6 : Varoitus ( Anakronismi ) : Muodollinen argumentti fn_ptr tyyppiä ulkoinen "C" void ( * )() kutsussa foo ( ulkoinen "C " void ( * ) ( ) ) välitetään mitätön ( * )().

Tämä johtuu siitä, että sitä ei my_function()ole ilmoitettu käyttämällä C-linkitys- ja kutsukäytäntöjä, vaan se välitetään C-funktiolle foo().

Muistiinpanot

  1. 1 2 Stroustrup, Bjarne Yleiskatsaus C++-ohjelmointikielestä julkaisussa The Handbook of Object Technology (Toimittaja: Saba Zamir). CRC Press LLC, Boca Raton. 1999. ISBN 0-8493-3135-8. (PDF) 4. Haettu 12. elokuuta 2009. Arkistoitu alkuperäisestä 16. elokuuta 2012.
  2. B. Stroustrup. C ja C++: Sisarukset. C/C++ Users Journal. heinäkuuta 2002 . Haettu 17. maaliskuuta 2019. Arkistoitu alkuperäisestä 21. joulukuuta 2018.
  3. Bjarne Stroustrupin UKK - Onko C C++:n osajoukko? . Haettu 22. syyskuuta 2019. Arkistoitu alkuperäisestä 6. helmikuuta 2016.
  4. B. Stroustrup. C ja C++: Yhteensopivuus. C/C++ Users Journal. elokuuta 2002. . Haettu 18. elokuuta 2013. Arkistoitu alkuperäisestä 22. heinäkuuta 2012.
  5. katso UNIX-HATERS Handbook , s.208
  6. Kansainvälisen standardin perusteet – ohjelmointikielet – C Arkistoitu 6. kesäkuuta 2016. , versio 5.10 (huhtikuu 2003).
  7. C Murteen asetukset - GNU Compiler Collectionin (GCC) käyttäminen . gnu.org . Arkistoitu alkuperäisestä 26. maaliskuuta 2014.
  8. N4659: Työluonnos, ohjelmointikielen C++ standardi . Arkistoitu alkuperäisestä 7. joulukuuta 2017. ("Ei ole virheellinen hypätä ilmoituksen ohi eksplisiittisellä tai implisiittisellä alustuksella (paitsi koko lohkossa, jota ei ole syötetty). ... Tällä yksinkertaisella käännösaikasäännöllä C++ varmistaa, että jos alustettu muuttuja on laajuudessa, se on varmasti alustettu.")
  9. N4659: Työluonnos, ohjelmointikielen C++ standardi . Arkistoitu alkuperäisestä 7. joulukuuta 2017.
  10. IBM Knowledge Center . ibm.com .
  11. FAQ > Casting malloc - Cprogramming.com . www.cprogramming.com . Arkistoitu alkuperäisestä 5. huhtikuuta 2007.
  12. 4.4a – Eksplisiittisen tyypin muunnos (valu) (16. huhtikuuta 2015). Arkistoitu alkuperäisestä 25. syyskuuta 2016.
  13. longjmp - C++ Viite . www.cplusplus.com _ Arkistoitu alkuperäisestä 19. toukokuuta 2018.
  14. 2011 ISO C standardiluonnos . Haettu 28. heinäkuuta 2022. Arkistoitu alkuperäisestä 29. maaliskuuta 2018.
  15. std::kompleksi - cppreference.com . en.cppreference.com . Arkistoitu alkuperäisestä 15. heinäkuuta 2017.
  16. Yhteensopimattomuudet ISO C:n ja ISO C++:n välillä . Arkistoitu alkuperäisestä 9. huhtikuuta 2006.
  17. Rajoitettu osoittimia arkistoitu 6. elokuuta 2016. GNU Compiler Collectionin (GCC) käyttämisestä
  18. IBM Knowledge Center . ibm.com .
  19. IBM Knowledge Center . ibm.com .
  20. Oracle-dokumentaatio . docs.sun.com. Haettu 18. elokuuta 2013. Arkistoitu alkuperäisestä 3. huhtikuuta 2009.

Linkit