ISO/ANSI C ostęp o plików ISO/ANSI C ostęp o plików Tryb binarny size_t frea( voi *buffer, size_t size, size_t count, FILE *stream ); size_t fwrite( const voi *buffer, size_t size, size_t count, FILE *stream ); Oczytuje lub zapisuje określoną liczbę porcji anych o/z bufora azywanego przez buffer z/o pliku azywanego przez stream Zwraca liczbę porcji wczytanych/zapisanych. Jeżeli liczba nie zgaza się z zaeklarowaną, to w przypaku oczytu znaczy, że być może w pliku było mniej anych niż się spoziewano, lub wystąpił błą oczytu (należy użyć funkcji feof lub ferror, żeby to rozstrzygnąć) 111 ISO/ANSI C Klasy pamięci Zmienne mogą być efiniowane z moyfikatorami: auto, extern, static auto zmienna lokalna istniejąca tylko w obrębie bloku: tworzona w momencie gy sterowanie wchozi o anego bloku i usuwana gy opuszcza (wszystkie zmienne zaeklarowane w obrębie bloku są z efinicji lokalne, latego tego moyfikatora się nie używa). extern eklaracja zmiennej zefiniowanej w innym pliku (la kou programu, który jest pozielony na pliki) static zmienne statyczne, istniejące w jenym egzemplarzu 113 Czym różnią się zmienne static o globalnych? Zmienne static są wiziane tylko w moule/pliku, w którym zostały zaeklarowane; nie można się o nich owołać z innego pliku za pomocą extern zmienna static zaeklarowana w bloku nie jest usuwana po wyjściu z tego bloku. Po powrocie sterowania o tego bloku wartość zmiennej static jest zachowana 114 zmienne volatile może ulec zmianie bez wiezy programu, np. ponieważ reprezentuje obszar anych bęący po kontrolą interfejsu o urzązenia zewnętrznego, lub gy programujemy wielowątkowo i zmienna pozwala na wymianę informacji mięzy wątkami Kompilator traktują ją jako niewną, tj. prze każym użyciem oczytuje jej wartość z pamięci (a nie np. z cache u), a każa moyfikacja musi zostać wykonana prze przystąpieniem o wykonania następnych instrukcji (żanego przyspieszania wykonania programu przy pomocy potokowego wykonania instrukcji przez zaawansowany procesor, itp.) 115 1
zmienne const wartość zmiennej nie może ulec zmianie po jej utworzeniu jest inicjalizowana w momencie jej efiniowania Komentarz na marginesie: moyfikator const wykorzystywany jest również przy eklarowaniu argumentów funkcji la zagwarantowania nietykalności zmiennych poawanych w argumentach wykonania, np.: char *strcpy( char *strdestination, const char *strsource ); Uwaga: typ char* to nie to samo co const char* Pamięć programu Pamięć jest pozielona na cztery segmenty: ane, sterta, stos i ko zmienne globalne ane zmienne statyczne ane zmienne const ko i/lub ane zmienne lokalne (zaeklarowane i zefiniowane w funkcjach) stos zmienne zaeklarowane i zefiniowane w main stos aźniki ane lub stos, w zależności o kontekstu pamięć alokowana ynamicznie sterta 116 117 ISO/ANSI C Zmienne ynamiczne Zmienne eklarowane w kozie programu są tworzone: w momencie uruchomienia programu (zmienne globalne), lub w momencie wejścia sterowania o bloku instrukcji, w którym zostały zaeklarowane (zmienne lokalne) Zmienne te są ientyfikowane przez nazwy Programista musi przewizieć, ile zmiennych bęzie potrzebował la swojego algorytmu A co robić, jeżeli zarzy się, że programista nie jest w stanie tego przewizieć? 119 Sytuacje, kiey nie wiemy nic wnego o rozmiarze zbioru anych, na którym bęziemy orować: praca na anych z pliku (rozmiar pliku nie jest znany) wczytywanie anych, co o których nie znamy ograniczenia na ilość (wprowazanie faktur, prouktów o cennika) obiór anych z interfejsu zewnętrznego (billingi telefoniczne, w meycynie: pomiary ciągłe stanu pacjenta temratura, tętno, itp.) Algorytm musi być przygotowany na to, że opiero w trakcie ziałania owie się, jaki jest rozmiar anych roboczych i musi umieć opowienio o tego rozmiaru na bieżąco przygotować struktury anych na jego przyjęcie Dynamiczne ostosowanie się programu o anych wejściowych jest możliwe z pomocą zmiennych ynamicznych. Zmienne ynamiczne są tworzone w trakcie ziałania programu za pomocą polecenia alokacji anych. Ich rozmiar może być określony opiero czasie ziałania programu (nie musi być wcześniej znany programiście), a po ich wykorzystaniu, zaalokowany obszar pamięci można zwolnić i z powrotem oać o yspozycji systemowi. Alokowanie zmiennej ynamicznej (<stlib.h> i <malloc.h>): voi* malloc( size_t size ); Funkcja zwraca ares o obszaru pamięci o rozmiarze size. Zwracany typ voi* pozwala na zapisanie wyniku o zmiennej aźnikowej owolnego typu. 120 121 2
Przykła: char *string1 = malloc( 80 ); if( string1 == NULL ) printf("brak wystarczającej ilości pamięci\n"); else printf("zaalokowano zmienną ynamiczną\n"); Aby zagwarantować przenośność kou, zamiast samemu obliczać całkowity rozmiar pamięci, lepiej wykorzystać funkcję sizeof: string1 = malloc( 80 *sizeof(char) ); Usuwanie zaalokowych zmiennych Kiey zmienna przestaje być potrzebna zaalokowane zasoby pamięci należy zwolnić. voi free( voi *memblock ); Argumentem wywołania jest aźnik na zmienną. Zapominanie o zwalnianiu zaalokowanych zasobów jest typowym błęem, powouje, że w trakcie pracy aplikacji zużywa ona coraz więcej i więcej zasobów. Takie zjawisko jest nazywane wyciekaniem pamięci. 122 123 Usuwanie zaalokowych zmiennych Przykła: char *string1 = malloc( 80 ); if( string1 == NULL ) printf( Brak wystarczającej ilości pamięci\n" ); else { printf( Zaalokowano zmienną ynamiczną\n" ); /* ko programu wykorzystujący zaalokowaną zmienną */ free( string1 ); Rozmiar zaalokowanej pamięci jest zawarty w oatkowym, niewielkim nagłówku ołączonym o obszaru zaalokowanego przez malloc. Dlatego funkcja free nie musi rozpoznawać rozmiaru pamięci o zwolnienia po typie aźnika. Zgubienie aźnika o zmiennej ynamicznej powouje całkowitą utratę kontroli na tą zmienną, ale nie zwalnia zasobów. Przykła z tablicami ynamicznymi: ouble * t1 = malloc(100*sizeof(ouble)); ouble * t2 = malloc(100*sizeof(ouble)); /* zamieniamy się tablicami */ t1 = t2; t2 = t1; /* Bez sensu. Tak się nie robi.. */ /* prawiłowo zamieniamy się tablicami */ ouble *t3; /* tworzymy zmienną pomocniczą t3 */ t3 = t1; t1 = t2; t2 = t3; /* zamieniamy */ 124 125 Dwuwymiarowe tablice ynamiczne Alokowanie: int i; ouble **t1 = malloc(100*sizeof(ouble*)); for (i=0; i<100; i++) t1[i] = malloc(100*sizeof(ouble)); Zwalnianie: int i; for (i=0; i<100; i++) free( t1[i] ); free( t1 ); Alokowanie i inicjalizacja zerami voi *calloc( size_t num, size_t size ); long *buffer; buffer = calloc( 40, sizeof( long ) ); if (buffer == NULL) { printf( "Can't allocate memory\n" ); exit(1); /* ziałania na zmiennej buffer */ free( buffer ); 126 127 3
Zmiana rozmiaru zaalokowanego bloku voi *realloc(voi *memblock, size_t size); long *buffer; buffer = malloc( 1000 * sizeof( long ) ); if (buffer == NULL) exit( 1 ); /* Realokacja */ buffer = realloc( buffer, 2000 * sizeof( long )); if (buffer == NULL) exit( 1 ); Zwraca ares o tego samego bloku pamięci, jeżeli uało się go rozszerzyć. Zwraca NULL jeżeli poano size=0 a memblock!=null, bąź jeżeli nie uało się rozszerzyć zaalokowanego bloku o żąanych rozmiarów. Jeżeli poano memblock==null, ziała jak zwykły malloc. 128 voi *memcpy( voi *est, const voi *src, voi *memmove( voi *est, const voi *src, Kopiuje z obszaru pamięci azanego przez src liczbę count bajtów o obszaru pamięci azanego przez est memcpy szybsze (różnica wioczna przy wielkich rozmiarach anych kopiowanych po wielokroć) memmove zabezpiecza poprawne ziałanie w przypaku, kiey azane obszary pamięci mogą się częściowo pokrywać 129 voi *memcpy( voi *est, const voi *src, voi *memmove( voi *est, const voi *src, Przykła: char tab[] = "Lorem ipsum olor sit amet, consectetur aipisicing elit"; char*ptab = calloc(100,sizeof(char)); int i = sizeof(tab); /* zwróci rozmiar całej tablicy */ memcpy(ptab,tab, i); /* tak jest szybciej: */ memmove(tab+5,tab, i); /* tak bezpieczniej: */ 130 Dygresja: ziałanie funkcji sizeof( ) la tablic i aźników - Zwraca rozmiar całej tablicy (w bajtach) tylko jeżeli kompilator jenoznacznie ientyfikuje argument jako tablicę: char tab[] = "Zakopane na pokaz"; int i = sizeof(tab); // i = 18 (tablica rozpoznana!) long ouble *ptab = calloc(100, sizeof(long ouble)); ptab[0] = sizeof(ptab); // ptab[0] = 4 ptab[1] = sizeof(*ptab); // ptab[1] = 8 - Jeżeli tablicę przekażemy jako argument wywołania funkcji np.: voi fun(char *w) tj. zostanie przekazana za pomocą argumentu typu aźnik, to sizeof(w) wywołany w kozie funkcji zwróci rozmiar zmiennej aźnikowej 131 Porównanie znaków w wóch buforach: int memcmp( const voi *buf1, const voi *buf2, char first[] = "12345678901234567890"; char secon[] = "12345678901234567891"; int result = memcmp( first, secon, 19 ); if( result < 0 ) printf( Pierwszy jest mniejszy niż rugi.\n" ); else if( result == 0 ) printf( Pierwszy i rugi są równe.\n" ); else printf( Pierwszy jest większy niż rugi.\n" ); Wyłnienie bufora znakami: voi *memset( voi *est, int c, char buffer[] = To tylko taki mały test memset"; printf( Prze: %s\n", buffer ); memset( buffer, '*', 14 ); printf( Po: %s\n", buffer ); Prze: To tylko taki mały test memset Po: **************mały test memset 132 133 4
Rekurencyjne typy anych ISO/ANSI C Zmienne ynamiczne: Rekurencyjne typy anych struct element { ouble ; struct element * ; 135 Rekurencyjne typy anych struct element { ouble ; struct element * ; ; struct element * = malloc(sizeof(struct element)); -> = malloc(sizeof(struct element)); Wskaźników używa się np. o buowy grafów skończonych, rzew, list (jeno- i wukierunkowych) i o manipulacji na nich. Poniżej pokazany jest przykła listy interpretowanej jako graf liniowy zorientowany. struct element { struct Dane_t ane; struct element * ; 136 137 Lista jest szczególnym przypakiem rekurencyjnego typu anych, w którym przyjmuje się, że: zbiór pusty wierzchołków jest listą, jeśli h jest wierzchołkiem listy, a t jest listą, to para uporząkowana (h,t) jest listą, w której h jest nazywana głową listy, a t ogonem listy. Lista jest szczególnym przypakiem rekurencyjnego typu anych, w którym przyjmuje się, że: zbiór pusty wierzchołków jest listą, jeśli h jest wierzchołkiem listy, a t jest listą, to para uporząkowana (h,t) jest listą, w której h jest nazywana głową listy, a t ogonem listy. 138 139 5
Głową listy może być zmienna aźnikowa, przechowująca ares pierwszego elementu listy, np.: struct element* gp; // notacja ANSI C element *gp; // notacja C++ Tworzenie nowej listy glowa lub zmienna typu struct, np.: struct element g; element g; // notacja ANSI C // notacja C++ Początek wstawiania W rugim przypaku głowa listy jest jenocześnie pierwszym elementem przechowującym ane 140 141 Tworzenie nowej listy 1. utwórz aźniki: na głowę listy glowa, oraz na ostatni element listy. 2. while (są ane o wprowazenia) { 1. utwórz nowy element listy 2. jeżeli glowa nie azuje na nic, niech glowa i azują na nowy element. W przeciwnym przypaku niech pole aźnikowe elementu listy azywanego przez azuje na nowy. Następnie zmień tak, aby również azywał na nowy 3. wyłnij anymi zawartość nowego elementu 4. zapisz NULL w polu azującym na następny element listy w nowym elemencie glowa glowa Prze ołączeniem: Po ołączeniu: 142 143 Przykłaowy program tworzący listę struct film_t { char tytul[80]; int rok; struct film_t *nast; ; struct film_t *, *glowa = NULL; char tytul[80]; int rok; FILE* stream; if( (stream = fon( "filmy.txt", "r" )) == NULL ) exit(1); /* zakłaamy, że w pliku filmy.txt wiersze zawierają tytuł i rok proukcji. Tytuł jest jenym slowem */ fscanf(stream, "%s %i", tytul, &rok); while (!feof( stream )) { if (glowa == NULL) glowa = = malloc(sizeof(struct film_t)); else { ->nast = malloc(sizeof(struct film_t)); = ->nast; strcpy(->tytul,tytul); ->rok = rok; ->nast = NULL; fscanf(stream, "%s %i", tytul,&rok); 144 fclose(stream); 6