ISO/ANSI C klasy pamięci 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 108 ISO/ANSI C klasy pamięci 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 109 ISO/ANSI C klasy pamięci zmienne volatile Zmienna 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.) 110 ISO/ANSI C klasy pamięci Pamięć programu 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ęć 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 111 112 1
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ć? 114 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. 115 116 Przykła: char *string = malloc( 80 ); if( string == 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: string = 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. 117 118 2
Usuwanie zaalokowych zmiennych Przykła: char *string = malloc( 80 ); if( string == NULL ) printf( Brak wystarczającej ilości pamięci\n" ); else { printf( Zaalokowano zmienną ynamiczną\n" ); /* ko programu wykorzystujący zaalokowaną zmienną */ free( string ); 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 */ 119 120 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 ); 121 122 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. 123 Funkcje orujące na pamięci <string.h> 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ć 124 3
Funkcje orujące na pamięci <string.h> 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); memcpy(ptab,tab, i); /* tak jest szybciej: */ memmove(tab+5,tab, i); /* tak bezpieczniej: */ 125 Funkcje orujące na pamięci <string.h> 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" ); 126 Funkcje orujące na pamięci <string.h> 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 ISO/ANSI C Zmienne ynamiczne: Rekurencyjne typy anych 127 Rekurencyjne typy anych struct element { ouble ; struct element * ; Rekurencyjne typy anych struct element { ouble ; struct element * ; ; struct element * = malloc(sizeof(struct element)); -> = malloc(sizeof(struct element)); 129 130 4
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 * ; 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. 131 132 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. 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++ lub zmienna typu struct, np.: struct element g; // notacja ANSI C element g; // notacja C++ W rugim przypaku głowa listy jest jenocześnie pierwszym elementem przechowującym ane 133 134 Tworzenie nowej listy glowa Początek wstawiania 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 135 136 5
glowa glowa Prze ołączeniem: Po ołączeniu: 137 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); 138 fclose(stream); Doanie elementu na końcu listy: nowy= malloc(sizeof(struct film_t)); strcpy(nowy->tytul,"kingsajz"); nowy->rok=1987; nowy->nast = NULL; Doanie o listy uporząkowanej (np.wg roku) Prze oaniem: Po oaniu: ->nast = nowy; = ->nast; 139 140 Doanie o listy uporząkowanej malejąco (np. wg roku) nowy = malloc(sizeof(struct film_t)); strcpy(nowy->tytul,"bogowie"); nowy->rok=2014; nowy->nast = NULL; if(glowa==null) glowa = = nowy; else if (glowa->rok < nowy->rok) { nowy->nast = glowa; glowa = nowy; UKSW, WMP. SNS, Warszawa else { = glowa; while (->nast->rok > nowy->rok) { = ->nast; if (->nast == NULL) break; ; nowy->nast = ->nast; ->nast = nowy; 141 6