ARCHIV |
|||||
Software (10844)
Distribuce (131)
Skripty (697)
Menu
Diskuze
Informace
|
C/C++ (33) - Rozdíly mezi C a C++Dnes probereme další drobné rozdíly mezi C a C++, napíšeme program který odpovídá normám
obou jazyků, ale v každém dělá něco jiného. Povíme si také, proč je třeba někdy kombinovat
C s C++, jaké problémy při tom vznikají a jak se dají jednoduše vyřešit. Drobné nekompatibilityMinule jsme si ukazovali, že C++ není jen rozšířením jazyka C, ale že má také některá omezení. Situace je ovšem ještě o něco komplikovanější. S trochou znalostí můžeme napsat kód, který vyhovuje normám obou jazyků, ale v každém z nich dělá něco jiného. Makro __cplusplusPředevším C++ definuje makro __cplusplus, pomocí podmíněného překladu tak můžeme napsat dvojí kód. Později si ukážeme, že se toho hodně využívá v knihovnách s rozhraním pro oba jazyky se společnými hlavičkovými soubory. Právě #ifdef __cplusplus je ten správný postup, pokud potřebujeme detekovat C++. Znaková konstantaV C mají znakové konstanty typ int, v C++ char. Prostor jmen strukturJiž z minula víme, že v C mají struktury vlastní prostor jmen a v C++ nikoli. Díky překrývání globálních identifikátorů lokálními a operátoru sizeof tak lze vykouzlit kód, který v obou jazycích dělá něco jiného. Níže uvedený kód berte jako odstrašující případ, nikoli jako návod pro vlastní programování. #include <stdio.h> int x; int main(void) { struct x { int i, j; }; /* V C++ je definováno makro __cplusplus. */ puts("Test 1"); #ifdef __cplusplus puts("C++"); #else puts("C"); #endif /* V C++ je znaková konstanta char, v C int. */ puts("Test 2"); if (sizeof('A') == sizeof(char)) { puts("C++"); } else { puts("C"); } /* V C++ nemají struktury vlastní prostor jmen, lokální definice struktury tak zastíní globální definici proměnné se stejným názvem. */ puts("Test 3"); if (sizeof(x) != sizeof(int)) { puts("C++"); } else { puts("C"); } return 0; } Mezi oběma jazyky existují další drobné odlišnosti, které mohou programátora potrápit. void funkce()Zápis funkce s prázdnými závorkami v hlavičce znamená v C funkci, o jejíchž parametrech nic nevíme. V C++ jde o funkci bez parametrů. Nejlepší je asi psát v obou jazycích void funkce(void) v tomto případě jde vždy o funkci bez parametrů. Různé typy charC++ zná a rozlišuje 3 různé typu odvozené od char: char, unsigned char a signed char. Později si ukážeme, že je to důležité například pro přetížené funkce. Typ boolV C++ je typ bool, který je určen pro logické hodnoty. Tento typ také vrací příslušné operátory (&&, || a podobně), zatímco v C je návratovým typem int. KonstantyC sice zná modifikátor const, ale například
const int sedm = 7;
je ve skutečnosti jen definice proměnné, která má své umístění v paměti, pouze nelze její hodnotu standardními prostředky měnit. V C++ se jedná opravdu o konstantu, která nemusí být linkována a může dokonce nahradit symbolické konstanty preprocesoru například při definici mezí polí a podobně.
const int sedm = 7;
int pole[sedm];
Odlišnosti při linkováníJak již víme, v C i C++ se jednotlivé zdrojové soubory překládají na objektové (zpravidla mají jméno *.o nebo *.obj na Windows). Spustitelný program se slinkuje z přeloženého kódu v objektových souborech a statických knihovnách. V C jednoznačně identifikuje funkci (která není static) nebo globální proměnnou na úrovni přeloženého kódu její jméno z kódu zdrojového. Bohužel v C++ to takhle jednoduše nejde. Díky přetěžování funkcí, metodám tříd a prostorům jmen může existovat ve zdrojovém kódu více funkcí se stejným identifikátorem. V přeloženém kódu je ovšem duplicita nepřípustná, překladač proto musí název rozšířit o nějaký řetězec odvozený od prostoru jmen, případné třídy a typu parametrů. Zkuste přeložit (ale nelinkovat) následující kód void funkce(void) {} void funkce(int i, const char *str) {} jako C++ g++ priklad.cpp -c -o cpp.o a potom jako C. gcc priklad.c -c -o c.o V případě C to udělejte dvakrát a vždy jednu z funkcí zakomentujte, jinak dojde ke konfliktu identifikátorů. Podívejte se nějakým textovým prohlížečem, úplně stačí vim, na soubory c.o (obě verze) a cpp.o. V případě C naleznete v obou případech na konci souboru symbol funkce, zatímco C++ generuje názvy jako _Z6funkcev a _Z6funkceiPKc. Zakomponování typu parametrů je docela názorné. Linkování C s C++Z uvedených rozdílů v přeloženém kódu vyplývají určité komplikace při spojování modulů napsaných v C s těmi v C++. Jde přitom o běžnou situaci, například každá linuxová distribuce obsahuje obrovské množství přeložených knihoven, statických i dynamických, řada z nich je určena pro oba jazyky a až na výjimky existují v jediné společné variantě tj. nikoli přeložená zvlášť pro C a zvlášť pro C++. Programátor, který je používá se přitom (obvykle) nemusí o nic starat. Jak je to možné? Kdyby se ani programátor knihovny v C o nic nestaral a aplikační programátor v C++ použil funkci z knihovny, skončí překlad programu chybou linkeru, který by například místo symbolu funkce hledal v knihovně třeba symbol _Z6funkceiPKc. Naštěstí jazyk C++ obsahuje modifikátor funkce, který změní její linkování ve stylu C
extern "C" void funkce(int i, const char *str) {
/* ... */
}
nebo alternativně pro více funkcí najednou. extern "C" { void funkce1(int i, const char *str) { /* ... */ } void funkce2(int i, const char *str) { /* ... */ } /* ... */ } Totéž se uvádí i v hlavičkovém souboru knihovny. extern "C" void funkce(int i, const char *str); Pokud tedy chceme nějakou knihovnu používat z obou jazyků, používáme standard volání funkcí ve stylu C. Věc je však ještě o trochu komplikovanější, neboť konstrukci extern "C" lze přeložit pouze v C++, takže například v hlavičkovém souboru knihovny je třeba použít podmíněný překlad. Ukážeme si to na příkladu, napíšeme si knihovnu a program, který ji využívá. Nebude přitom záležet na jazyku programu a navíc ani na jazyku knihovny, všechny 4 kombinace C a C++ budou fungovat. KnihovnaKnihovna poskytuje jedinou funkci, která vrací název jazyka, v němž je přeložená. Zdrojový kód tvoří soubory knihovna.h a knihovna.c respektive .cpp. Začneme hlavičkovým souborem. /* knihovna.h */ #ifndef knihovnaH #define knihovnaH /* Makro CFUNKCE se rozvine na extern "C" v C++ nebo na prázdný řetězec v C. */ #ifdef __cplusplus #define CFUNKCE extern "C" #else #define CFUNKCE #endif CFUNKCE const char * jazykKnihovny(void); #endif Implementace funkce jazykKnihovny je jednoduchá. /* knihovna.c nebo .cpp */ #include "knihovna.h" CFUNKCE const char * jazykKnihovny(void) { #ifdef __cplusplus return "C++"; #else return "C"; #endif } Hlavní programProgram jen vypíše svůj jazyk a pomocí funkce jazykKnihovny rovněž jazyk, kterým byla přeložena knihovna. /*program.c nebo .cpp */ #include <stdio.h> #include "knihovna.h" const char * jazykProgramu(void) { #ifdef __cplusplus return "C++"; #else return "C"; #endif } int main(void) { printf("Kód v %s volá funkci knihovny v %s.\n", jazykProgramu(), jazykKnihovny()); return 0; } Všimněte si, že hlavní program je velmi jednoduchý a že aplikační programátor ani nemusí znát rozdíly v linkování mezi C a C++, konstrukci extern "C" a nemusí rovněž vědět, v jakém jazyku je knihovna implementována a jakým způsobem se linkují funkce rozhraní knihovny. Na překlad a spuštění všech čtyř variant bude asi nejlepší si napsat malý skript. #/bin/sh gcc knihovna.c -c gcc program.c -c gcc program.o knihovna.o -o program ./program gcc knihovna.c -c g++ program.cpp -c g++ program.o knihovna.o -o program ./program g++ knihovna.cpp -c gcc program.c -c g++ program.o knihovna.o -o program ./program g++ knihovna.cpp -c g++ program.cpp -c g++ program.o knihovna.o -o program ./program Zbývá už jen se pokochat výsledkem: [honza@localhost]$ chmod 744 skript [honza@localhost]$ ./skript Kód v C volá funkci knihovny v C. Kód v C++ volá funkci knihovny v C. Kód v C volá funkci knihovny v C++. Kód v C++ volá funkci knihovny v C++. [honza@localhost]$ Pokračování příštěDrobných rozdílů mezi C a C++ jsme si už užili víc než dost. Od příštího dílu se budeme zabývat tím, co je v C++ navíc.
Související články
Předchozí Celou kategorii (seriál) Další
C/C++ (1) - Úvod
C/C++ (2) - První program C/C++ (3) - Proměnné a konstanty C/C++ (4) - Funkce printf C/C++ (5) - Funkce printf podruhé C/C++ (6) - Operátory C/C++ (7) - Podmínka C/C++ (8) - Cykly C/C++ (9) - Pole C/C++ (10) - Standardní vstup a výstup C/C++ (11) - Čtení a konverze čísel C/C++ (12) - Preprocesor C/C++ (13) - Preprocesor podruhé C/C++ (14) - Funkce C/C++ (15) - Proměnné C/C++ (16) - Hlavičkové soubory C/C++ (17) - Makefile C/C++ (18) - Makefile podruhé C/C++ (19) - Příkaz switch a bitové operátory C/C++ (20) - Alokace paměti C/C++ (21) - Práce s řetězci C/C++ (22) - Struktury C/C++ (23) - Seznam C/C++ (24) - Soubory C/C++ (25) - Funkce s proměnným počtem parametrů C/C++ (26) - Standardní knihovna C/C++ (27) - Standardní knihovna podruhé C/C++ (28) - Standardní knihovna potřetí C/C++ (29) - Standardní knihovna počtvrté C/C++ (30) - Výčtový typ a nestandardní knihovny C/C++ (31) - Jazyk C++, historie, charakteristika, vztah k C C/C++ (32) - Omezení C++ oproti C C/C++ (34) - Drobná vylepšení C++ C/C++ (35) - Reference, funkce C/C++ (36) - Prostory jmen C/C++ (37) - Prostory jmen podruhé C/C++ (38) - Prostory jmen potřetí C/C++ (39) - Objektově orientované programování C/C++ (40) - Dědičnost a virtuální metody GCC vs. CLANG C++ Binární vyhledávací stromy C++ Datová struktura zásobník C++ - Hashování C++ - Vyhledávání v textu - Brute Force algoritmus C++ šablony Grafy a grafové algoritmy I Grafy a grafové algoritmy II C++ výjimky C++ Funktory neboli funkční objekty Grafy a grafové algoritmy III. C++ a garbage collector Předchozí Celou kategorii (seriál) Další
|
Vyhledávání software
Vyhledávání článků
28.11.2018 23:56 /František Kučera 12.11.2018 21:28 /Redakce Linuxsoft.cz 6.11.2018 2:04 /František Kučera 4.10.2018 21:30 /Ondřej Čečák 18.9.2018 23:30 /František Kučera 9.9.2018 14:15 /Redakce Linuxsoft.cz 12.8.2018 16:58 /František Kučera 16.7.2018 1:05 /František Kučera
Poslední diskuze
31.7.2023 14:13 /
Linda Graham 30.11.2022 9:32 /
Kyle McDermott 13.12.2018 10:57 /
Jan Mareš 2.12.2018 23:56 /
František Kučera 5.10.2018 17:12 /
Jakub Kuljovsky | |||
ISSN 1801-3805 | Provozovatel: Pavel Kysilka, IČ: 72868490 (2003-2024) | mail at linuxsoft dot cz | Design: www.megadesign.cz | Textová verze |