430 likes | 643 Views
NPRG0 4 1 Programování v C++ Programování v C. David Bednárek Filip Zavoral. C vs. C++. V ývoj 1970-3 první verze C , spole č n ý v ý voj s UNIXem , jádro v C 1978 Kerninghan, Ritchie: The C Progr amming Language 1980 AT &T - " C with Classes"
E N D
NPRG041Programování v C++Programování v C David BednárekFilip Zavoral
C vs. C++ • Vývoj • 1970-3 první verze C, společný vývoj s UNIXem, jádro v C • 1978 Kerninghan, Ritchie:The C Programming Language • 1980AT&T - "C with Classes" • 1983poprvé názevC++ (Rick Mascitti) • 1985Stroustrup: The C++ Programming Language • 1998ISO/ANSI normaC++ • 1999ISO/ANSInorma C - 'C99' • 2006C++ 2003 TR1 • jen knihovny - traits, wrappers, regexp, numerics, ... • 2011 C++ 11 (C++0x) • zásadní rozšíření jazyka - lambda, r-values, generic prog., ... • Proč C v 21. století • jádra OS, drivery, pračky, knihovny, • údržba sw
C++ jako ostránadmnožina C ... neboli co v C není • OOP • classes, inheritance, member functions, constructors and destructors, virtual functions, access control, pointers to members, static members • Syntax • templates, exceptions, references, overloading, default arguments, namespaces, new/delete, casting, friend, inline, RTTI, auto, lambda, rvalues, ..., ..., ... • Libraries • containers, iterators & algorithms • strings • streams
Odlišnosti C a C++ • bool • struct & enum tags • implicitní konverze - enum, void * • main - return 0; bool b = true; typedefenumbool_ { FALSE, TRUE} bool; bool b = TRUE; int b2 = a > 1; C99 _Bool b = TRUE; struct S { int x; }; S s; struct S { int x; }; struct S s; typedefstruct S_ { int x; } S; S s; enum E{ NULA }; E e = NULA; int x = (int)e; int* p = (int*)malloc(sizeof(int)); enum E{ NULA }; enum E e = NULA; int x = e; int* p = malloc(sizeof( int)); int main() { ... return 0; } int main() { ... }
Řetězce • Řetězec - pole znaků (char) zakončené nulou • konvence, knihovny "Ahoj" • X proměnná • 'X' znaková konstanta - celočíselná hodnota • "X" řetězec - ukazatel Každý řetězec musí být vždy ukončen nulou '\0' = 0 char buffer[4]; strcpy( buffer, "Ahoj"); pozor na uvozovky a apostrofy ! vždymyslet na koncovou nulu ! kód znaku v použitém kódování (ASCII, CP1250, ISO8859-2, EBCDIC, ...)
Řetězcové proměnné a konstanty • static char s1[]= "Uno"; • const char *s2= "Due"; • puts( "Uno"); Inicializované pole (konstantní ukazatel) ++s1 nelze! ... = { 'U', 'n', 'o', 0 }; anonymní globální proměnná const char[] s1: s2: Inicializovaná proměnná typuukazatel s2++ se přesune na další znak ekvivalent globální proměnné typu const char[ ] inicializované obsahem konstanty
Délkařetězce –různé implementace intstrlen ( const char* s) { inti=0; while (s[i]!='\0') { ++i; } return i; } více inkrementací prázdné tělo nezapomenout na ';'!! for( i=0; *s != '\0'; ++i) ++s; for( i=0; *s != '\0'; ++i, ++s) ; složitější podmínka: test nenulovosti inkrementace ukazatele int i=0; while (*s++!= '\0') ++i; int i=0; while (*s!='\0') { ++i; ++s; } return i; while(a!=0) while(a) podmínka je splněna pokud je nenulová int i=0; while (*s++) ++i; rozdíl ukazatelů = počet prvků mezi nimi pozorna ± 1 ! char *p = s; while (*p++) ; return p-s-1; přístup přes ukazatel
Řetězce – chyby při kopírování vždy pozor na dostatek místa funkce nic nekontroluje !!! váš program provedl... char buf[6]; char pozdrav[]= "Dobry den"; strcpy( buf, pozdrav); buf pozdrav kopírování na neinicializovaný ukazatel !!! váš program provedl... char *ptr; char pozdrav[]= "Ahoj"; strcpy( ptr, pozdrav); ? ptr pozdrav ptr neinicializovaný!!!
Vracení řetězců - lokální proměnná • Naprosto chybné řešení • Nekontroluje přetečení pole buf • Vrací odkaz na lokální proměnnou, která v okamžiku návratu zaniká string cele_jmeno( const string& jm, const string& prijm) { return jm + " " + prijm; } char* cele_jmeno( const char* jm, const char * prijm) { char buf[ 100]; strcpy( buf, jm); strcat( buf, " "); strcat( buf, prijm); return buf; }
Vracení řetězců - statická proměnná • Chybné řešení • Nekontroluje přetečení pole buf • Používá statickou proměnnou • zbytečně zabírá místo i v době, kdy funkce není vyvolána • opakovaná volání ji sdílejí: • podmínka nikdy nebude splněna, protože strcmp vždy dostane stejné ukazatele na totéž pole buf char* cele_jmeno( const char* jm, const char * prijm) { static char buf[ 100]; strcpy( buf, jm); strcat( buf, " "); strcat( buf, prijm); return buf; } if ( strcmp( cele_jmeno( j1, p1), cele_jmeno( j2, p2)) )
Vracení řetězců - parametr • Funkční řešení, ale nebezpečné • Nekontroluje přetečení pole buf • Pokud volající nemá spolehlivý horní odhad velikostí jména a příjmení, nemůže tuto funkci bezpečně volat • Většina C knihovenale funguje podobně void cele_jmeno( char * buf, const char* jm, const char * prijm) { strcpy( buf, jm); strcat( buf, " "); strcat( buf, prijm); } void tisk( const char * jm, const char * prijm) { char buf[ 100]; cele_jmeno( buf, jm, prijm); puts( buf); }
Vracení řetězců - bezpečné řešení int cele_jmeno( char * buf, size_t bufsize, const char * jm, const char * prijm) { size_t lj = strlen( jm); size_t lp= strlen( prijm); if ( lj + lp + 2 > bufsize ) { /* error */ return -1; } memcpy( buf, jm, lj); buf[ lj] = ' '; memcpy( buf + lj + 1, prijm, lp); buf[ lj + lp + 1] = 0; return lj + lp + 1; } kontrola velikosti pole pozornamezeru a konec! kopírování jednotlivých částí návrat výsledné délky void tisk( const char * jm, const char * prijm) { enum { N = 100 }; char buf[ N]; if( cele_jmeno( buf, N, jm, prijm) > 0) puts( buf); } max velikost pole kontrola korektnosti výsledku
Kombinace typových konstrukcí typicky se nepoužívá pole = ukazatel na 1. prvek čtení deklarací: od identifikátoru doprava, až to nepůjde, tak doleva
Kombinace typových konstrukcí int*(*pf[10])(void); int*(*maso(int*(*p1)(void),int*(*p2)(void)))(void); co to je za maso ???
Kombinace typových konstrukcí int*(*pf[10])(void); int*(*maso(int*(*p1)(void),int*(*p2)(void)))(void); typedef int* fce( void); fce*pf [10]; fce* maso( fce* p1, fce* p2); použitímtypedefse výrazně zpřehlední kód
OOP v C explicitní this class A { public: A() : x_(1) {} intf( int y) { return x_ + y; } private: int x_; }; A* a = new A; a->f(2); absence ochrany typedefstructA_ { int x_; } A; intA_init( A* a) { a->x_ = 1; } intA_f( A* a, int y) { return a->x_ + y; } A* a = malloc( sizeof( A)); A_init(a); A_f(a,2); absence konstruktoru a destruktoru nemožnost přetížení explicitní inicializace beztypová alokace explicitní prefix nebo možnost kolize
Pozdní vazba v C class A { virtual int f(void) { return 1; } }; class B : public A { virtual int f(void) { return 2; } } A* a = new B; a->f(); VMT konstruktorinicializace VMT typedefint fnc(void); typedefstruct A_ { fnc* f_; } A; void A_init( A* a, fnc* f) { a->f_ = f; } intA_f( A* a) { return (*a->f_)(); } intA_f_body() { return 1; } intB_f_body() { return 2; } A* a = malloc( sizeof( A)); A_init( a, B_f_body); A_f( a); virtual method ruční inicializace zavolá se B_f 'odvozená metoda'
Variabilní argumenty typ ! #include <stdarg.h> typedef va_list???? va_start( va_listargptr, last_parm); va_arg( va_listargptr, type); va_end( va_listargptr); speciální typ int sum( int num, ...) { intrv = 0; va_listargptr; va_start( argptr, num); for( ; num > 0; num--) { rv += va_arg( argptr, int); } va_end( argptr); return rv; } int answer = sum( 4, 4, 3, 2, 1); opravdu '...' inicializace přístup ukončení korektnostparametrů musí zajistit uživatel funkce Náhrada v C++: Variadic Templates
Parametry příkazové řádky C:\> myprog.exe -n -w a.txt b.txt pole řetězců (ukazatelů na char) int main( intargc, char** argv) argv 5 argc Počet parametrů včetně názvu programu ! = počet ukazatelů v argv vector<string>arg( argv, argv+argc);
Zpracovánípříkazové řádky usage: myprog [-n] [-w] fileA fileB int main( int argc, char** argv) { int n=0, w=0; while( *++argv && **argv=='-') { switch( argv[0][1]) { case 'n': n = 1; break; case 'w': w = 1; break; default: error(); } } if( !argv[0] || !argv[1]) error(); doit( argv[0], argv[1], n, w); return 0; } options nastavení přepínače zbývající parametry výkonná funkce argv
Zpracovánípříkazové řádky int main( int argc, char** argv) { int n=0, w=0; int x = 0; char* f = 0; while( *++argv && **argv=='-') { switch( argv[0][1]) { case 'n': n = 1; break; case 'w': w = 1; break; case 'x': x = atoi( argv[0]+2); break; case 'f': f = argv[0]+2; break; default: error(); } } if( !argv[0] || !argv[1])error(); doit( argv[0], argv[1], n, w, x, f); return 0; } usage: myprog [-n] [-w][-x123] [-ffilename] fileA fileB číselný parametr řetězcový parametr argv
Zpracovánípříkazové řádky int main( int argc, char** argv) { int n=0, w=0; int x = 0; char* f = 0; while( *++argv && **argv=='-') { switch( argv[0][1]) { case 'n': n = 1; break; case 'w': w = 1; break; case 'x': x = atoi( argv[0]+2); break; case 'f': if( argv[0][2]) f = argv[0]+2; else f = *++argv; break; default: error(); } } if( !argv[0] || !argv[1])error(); doit( argv[0], argv[1], n, w, x, f); return 0; } usage: myprog [-n] [-w][-x123] [-ffilename] fileA fileB -ffile -ffile argv
printf int printf(const char * format, ...); • % [flags] [width] [.precision] [opt_pref] type • %c - char - znak • %d - int - decimálně • %x - int - šestnáctkově • %ld - long • %f - double - s desetinnoutečkou • %g - double - s exponentem • %s - char * - řetězec místo width a/nebo precision znak * hodnota následujícího parametru width: min. počet míst na výstupu precision: max. počet zpracovávaných znaků #include <stdio.h> printf( "Ahoj %s dnes je %d.%d.","Babi", 8, 1); fprintf( FILE*, sprintf( char*, swprintf( wchar_t*, _snprintf( char*, int n, _scprintf( ... shodu formátu a parametrů musí zajistit programátor neshoda: nedefinované chování
printf :a:▫▫▫▫17:000004D2 :▫▫-1234.560:▫▫-1234.568: :ahoj:▫▫ahoj:ahojbabi :ahoj▫▫:aho▫▫▫:aho: :▫▫▫▫17:aho▫▫▫: :A::78263451:● printf(":%c:%6d:%08X:%7.3f:%7.3f:\n", 'a', 17, 1234, -1234.56, -1234.5678); printf(":%s:%6s:%6s:%-6s:%-6.3s:%.3s:\n", "ahoj","ahoj","ahojbabi", "ahoj","ahoj","ahoj"); printf(":%*d:%-*.*s:\n", 6, 17, 6, 3, "ahoj"); printf(":%c:%c:%d:%s:\n", 0xffeedd41, "ahoj", "ahoj", 'a'); Unhandled exception at 0x0041470c :Access violation reading location 0x00000061
Práce se soubory typ 'soubor' (ukazatelnastrukturu) otevřenísouboru konvence dle OS w: soubor se smaže r: soubor musí existovat #include <stdio.h> FILE* fp; int c; if( !(fp = fopen("c:\\f.txt", "r"))) error(); while( (c = getc( fp)) != EOF) putchar( c); fclose( fp); pozorna '\\' !!! a: otevřít na konci konstanta v stdio.h různá od všech možných dat +: vždy čtení i zápis
Textové vs. binární soubory • Textový soubor • konverze konců řádek ('\n') naplatformově závislou vnější reprezentaci • typicky 0x0D 0x0A(Win)nebo 0x0A(Unix) • konverze je automatická, programátor se o to nemusí starat • vhodné pro ukládání lidsky čitelných dat • getc/putc, fgets/fputs, fprintf, ... • chovánífseek/ftellna'\n' nedefinován - nepoužívat • Binární soubor • žádné konverze se neprovádí • v souboru je přesný binární obraz zapisovaných dat • vhodné pro ukládání vnitřních datových struktur • lidsky přímo nečitelné • typickyfread/fwrite, lzeigetc/putc (přístup po bajtech) • fseek/ftell OK
Funkce pro práci se soubory FILE*fopen( const char* fname, const char* mode); intfclose( FILE* fp); intfprintf( FILE* fp, const char* format, ...); intgetc( FILE* fp); intputc( int c, FILE* fp); char* fgets( char* buffer, size_t limit, FILE* fp); intfputs( const char* buffer, FILE* fp); size_tfread( void* ptr, size_t size, size_t n, FILE* fp); size_tfwrite( const void* ptr, size_t size, size_t n, FILE* fp); long ftell( FILE* fp); int fseek( FILE* fp, long offset, int whence); whence:SEEK_SET, SEEK_CUR, SEEK_END int fflush( FILE *fp);
Souborový vs. standardní v/v funkce pro práci se standardním vstupem/výstupem intgetchar( void); intputchar( int c); intprintf(const char* format, ...); char* gets( char* buffer); standardní vstup/výstup FILE* stand. otevřený na čtení/zápis před vstupem do main FILE *stdin; FILE *stdout; getchar() getc(stdin) putchar(c) putc(c, stdout) FILE* fp = stdout; if( ...) fp = fopen( "...", "r"); c = getc( fp); Nikdy nepoužívat! Nelzeohlídat přetečení bufferu všechny souborové funkce lze použít i pro std. v/v jednotný zápis na std výstup nebo do souboru
Knihovní funkce C C++ namespacestd <string.h><cstring> strlen, strcmp, stricmp, strcpy, strncpy, strcat, strchr, strrchr, strstr, memset, memcmp, memcpy, memchr <stdio.h> <cstdio> getchar, putchar, fopen, tmpfile, fclose, getc, putc, fgets, fputs, fread fwrite,ftell, fseek, printf, fprintf, vfprintf, fflush, ungetc FILE, stdin, stdout, EOF, SEEK_SET, ... <stdlib.h> <cstdlib> malloc, free, atoi, atof, strtol, qsort, rand, exit <ctype.h> <cctype> isalpha, isdigit, isxdigit, isalnum, isspace, ispunct, iscntrl, islower, isupper, tolower, toupper <math.h> <cmath> abs, floor, sin, sqrt, exp, exp, log, ... <time.h> <ctime> time, gmtime, strftime, asctime, clock, ... ... a mnohodalších <assert.h> <errno.h> <limits.h> <locale.h> <stdarg.h> <setjmp.h>
Co (a proč )dělá tento program? #define _ F-->00||F-OO--; int F=00,OO=00;main(){F_OO();printf("%1.3f\n",4.*-F/OO/OO);}F_OO() { _-_-_-_ _-_-_-_-_-_-_-_-_ _-_-_-_-_-_-_-_-_-_-_-_ _-_-_-_-_-_-_-_-_-_-_-_-_-_ _-_-_-_-_-_-_-_-_-_-_-_-_-_-_ _-_-_-_-_-_-_-_-_-_-_-_-_-_-_ _-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_ _-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_ _-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_ _-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_ _-_-_-_-_-_-_-_-_-_-_-_-_-_-_ _-_-_-_-_-_-_-_-_-_-_-_-_-_-_ _-_-_-_-_-_-_-_-_-_-_-_-_-_ _-_-_-_-_-_-_-_-_-_-_-_ _-_-_-_-_-_-_-_ _-_-_-_ } Je v C, nikoliv v C++
Interoperabilita • vlastní C moduly • obj, lib, dll/so • jak linkovat C a C++ moduly • jak dělat společné C/C++ headery • cizí C knihovny • jak z C++ volat C knihovny • callback z C knihoven do C++ • mandlování, volací konvence • dynamicky linkované knihovny
Překlad více modulů knihovní headery knihovny vlastní headery .h .h .obj .obj .obj .obj .obj .lib .cpp CC .obj Link .exe kompilace jednoho modulu .obj .obj .c .obj .c .cpp další moduly
Vytvoření vlastní knihovny knihovní headery vlastní headery .h .h .cpp CC .obj Lib .lib kompilace jednoho modulu .obj .obj .c .obj .c .cpp další moduly
C++ exe / C lib zdrojový text /překladač C lib.c CC .obj Lib .lib lib.h exe.cpp CPPC .obj Link .exe zdrojový text /překladač C++
C++ exe / C lib • error LNK2019: unresolved external symbol • "int __cdecllib_fnc(int)" (?lib_fnc@@YAHH@Z) • referenced in function _main lib.c CC .obj Lib .lib lib.h exe.cpp CPPC .obj Link .exe what the ... ... hell???
Mandlování int a; int a( void); int a( int, int); class a {}; class a { int a; }; class a { int a( int); }; • mangling • mandlování • znetvoření • name-decoration • syntaktická a sémantická informace o symbolu • zjednoznačnění identifikátoru • proměnná / funkce / operator / metoda • typy a typové konstrukce parametrů a návratové hodnoty • třída, další atributy (const, volatile, ...) • volací konvence • formátjednotně nedefinovaný • závislý na platformě, překladači, ... • obecně nepřenositelné
C++ exe / C lib /* pureclib.h*/ #ifndef PURECLIB__H_ #define PURECLIB__H_ extern intlib_x; intlib_fnc( int x); #endif /* pureclib.c*/ #include "pureclib.h" intlib_x; intlib_fnc( int x) { return old_x; } // cppexe.cpp #include "pureclib.h" int main(....) { inti = lib_fnc( 1); } CC různé překladačerůzné jazyky různá implementace CPPC _lib_fnc ?lib_fnc@@YAHH@Z
Společné hlavičkové soubory symboly C /* pureclib.h*/ #ifndef PURECLIB__H_ #define PURECLIB__H_ #ifdef __cplusplus extern "C" { #endif extern intlib_x; intlib_fnc( int x); #ifdef __cplusplus } #endif #endif /* pureclib.c*/ #include "pureclib.h" intlib_x; intlib_fnc( int x) { return old_x; } // cppexe.cpp #include "pureclib.h" int main(....) { inti = lib_fnc( 1); } CC CPPC _lib_fnc CPPC - definované CC - nedefinované _lib_fnc
Volací konvence • způsob implementace volání funkcí • registry vs. zásobník • zachovávání registrů • pořadí předávání parametrů • návratová hodnota • příprava a úklid zásobníku • konkrétní konvence • není součástí normy - rozšíření • __cdecl- default for C and C++, varargs • __stdcall- Win32 API functions • __fastcall- arguments in registers, faster • __thiscall- this • __clrcall- C++/CLI, .Net, managed code f( 1, 2); moveax, 1 movebx, 2 call ?f@@X moveax, [ebp+08] movebx, [ebp+04] ...
C++ callback callbackknihovní kód volá klientskou funkci /* pureclib.h*/ #ifdef __cplusplus extern "C" { #endif int lib_cb( int x, int (*cb_fnc)( int)); #ifdef __cplusplus } #endif extern "C" určuje i volací konvenci // cppexe.cpp #include "pureclib.h" extern "C" intcpp_fnc( int x){ return x+1; } int main(){ lib_cb( i, cpp_fnc); } /* pureclib.c*/ #include "pureclib.h" intlib_cb( int x, int (*cb_fnc)( int)) { return cb_fnc( x); } CC očekává C funkci CC očekává C funkci
Dynamicky linkované knihovny • použití funkcí dodaných až za běhu • není součástí normy • použití na různých platformách ideově podobné • ale nepřenositelné • pomocí preprocesoru lze multiplatformní rozhraní • ale netriviální • Windows • .dll • chová se jako .exe • vlastní zásobník, heap, standardní knihovny • Linux / Unix • .so • chová se jako .lib • balíček .o more details: http://www.symantec.com/connect/articles/dynamic-linking-linux-and-windows-part-one ...-part-two
Dynamicky linkované knihovny // dll.cpp extern "C" __declspec(dllexport) int add( int a, int b) { return a + b; } BOOL APIENTRY DllMain(....) { return TRUE; } // exe_explicit.cpp HINSTANCE dll = LoadLibrary( TEXT("dll.dll")); if( dll == NULL) return 1; typedefintdll_fnc(int, int); dll_fnc* add = (dll_fnc*) GetProcAddress( dll, "add"); if( add == NULL) { FreeLibrary( dll); return 1; } int result = add(1, 2); FreeLibrary( dll); load dll explicit runtime linking běžné volání statické slinkovánís dll.libjen proxy, kód v .dll // exe_import.cpp extern "C" __declspec(dllimport) int add(int a, int b); int result = add(1, 2);