Struktury czyli rekordy w C/C++
Transkrypt
Struktury czyli rekordy w C/C++
Wprowadzenie do programowania w języku C — struktury. pola bitowe, unie Struktury czyli rekordy w C/C++ Struktury (rekordy) są złożonymi zmiennymi, składającymi się z elementów różnych typów zwanych polami, taką grupę pól można traktować jako jeden obiekt a nie zestaw obiektów oddzielnych. struct MOUSE_EVENT { int x, y; unsigned char buttons; int double_click; }; struct WINDOW_INFO { int x, y; int w, h; int frame_type; char * title; }; /* /* /* /* /* Pozycja kursora myszki na ekranie */ /* Informacja o stanie przycisku */ /* Czy podwójne kliknięcie ? */ Pozycja lewego górnego narożnika okna */ w – szerokość okna, h – wysokość okna */ Typ wzorca ramki */ Wskaźnik do obszaru tytułu okna */ Odwoływanie się do pól struktur — hipotetyczna funkcja obsługi void process_mouse_event( struct MOUSE_EVENT me ) { . . . if( me.buttons & M_LEFT_BUTTON_PRESSED ) if( me.double_click ) find_object_on_pos( me.x, me.y ); . . . } Odwoływanie się do pól struktur, wskaźniki — hipotetyczna funkcja obsługi void process_mouse_event( struct MOUSE_EVENT * me ) { . . . if( me->buttons & M_LEFT_BUTTON_PRESSED ) /* zamiast (*me).buttons */ if( me->double_click ) find_object_on_pos( me->x, me->y ); . . . } Struktury mogą być zagnieżdżane struct EVENT { int event_kind; /* Kod rodzaju zdarzenia */ struct MOUSE_EVENT mouse_ev; /* Rekord opisu zdarzenia dot. myszki */ struct KEYBD_EVENT keybd_ev; /* Rekord opisu zdarzenia dot. klawiatury */ }; . . . struct EVENT ev; . . . if( ev.kind == FROM_MOUSE ) if( ev.mouse_ev.duble_click ) find_object_on_pos( ev.mouse_ev.x, ev.mouse_ev.y ); . . . Wprowadzenie do programowania w języku C — struktury. pola bitowe, unie Deklarowanie struktur, specyfikacja typedef Alternatywne sposoby deklaracji zmiennych strukturalnych struct POINT struct POINT { { int x, y; int x, y; } p1, p2, p3; }; struct POINT p1, p2, p3; POINT to etykieta struktury, w języku C nie jest to nazwa typu (w C++ tak). Deklaracja struktury może nie zawierać etykiety, lecz to mało użyteczna rzecz. struct { int x, y, radius; } c1, c2, c3; Aby skrócić zapis można używać deklaracji typedef: typedef struct { int x, y, radius } CIRCLE; typedef struct MOUSE_EVENT MOUSE_EVENT_STRUCT; . . . CIRCLE c1, c2, c3; MOUSE_EVENT_STRUCT ev, * ev_ptr = NULL; Aby uniknąć zastanawiania się nad nazwami: struct _POINT_3D { int x, y, z; }; typedef struct _POINT_3D POINT_3D; Składowa struktury (pole) nie może być tego samego typu co właśnie deklarowana struktura ale może być wskaźnikiem: struct A ( struct A a ); /* Błąd */ struct A ( struct A * a ); /* OK */ Ogólnie, w przypadku wskaźników można użyć niekompletnej deklaracji: struct A; /* Niekompletna deklaracja */ struct B { struct A * pa; }; Pola bitowe struct EVENT_TYPE { unsigned char from_mouse : 1; unsigned char from_keybd : 1; unsigned char from_timer : 1; } event_source; . . . event_source.from_mouse = 1; event_source.from_keybd = event_source.from_timer = 0; . . . if( event_source.from_mouse ) . . . Wprowadzenie do programowania w języku C — struktury. pola bitowe, unie Pola bitowe zachowują się jak małe zmienne całkowite. Mogą być deklarowane tylko w strukturach, uniach i klasach (C++). Nie wolno pobierać adresu pola bitowego: &event_source.from_mouse jest niedozwolone. Ogólnie, deklaracja pola bitowego przyjmuje postać: specyfikator-typu <identyfikator-pola-bitowego> : szerokość-pola gdzie specyfikator-typu to char, unsigned char, int lub unsigned int. Specyfikacja szerokość-pola musi wystapić, nie może ona przekraczać rozmiaru typu określonego przez specyfikator-typu. struct BIT_FIELDS { int i unsigned int j int int k } a; : : : : a.i = 3 —— a.i == -1 ( bin: 11 ) a.i = 6 —— a.i == -2 ( bin: 10 ) 2; 2; 3; 1; a.k = 0 —— a.k == 0 ( bin: 0 ) a.k = 1 —— a.k == -1 ( bin: 1 ) 7 6 5 4 3 2 1 0 1 0 0 0 0 0 1 1 k nieu¿ywane j i Pola bitowe są rozwiązaniem mocno zależnym od implementacji. Zatem mogą one zachowywać się inaczej po zmianie środowiska systemowego lub nawet tylko kompilatora. Ograniczają one przenośność kodu źródłowego. Unie Unia jest zmienna, która w różnych momentach może zawierać obiekty różnych typów i rozmiarów. W damym momencie tylko jeden obiekt jest „aktywny”. Wszystkie składowe unii są pamiętane w tym samym miejscu pamięci, rozmiar unii jest równy rozmiarowi największej składowej. union EVENT_INFO { struct MOUSE_EVENT mouse_ev; /* Rekord opisu zdarzenia dot. myszki struct KEYBD_EVENT keybd_ev; /* Rekord opisu zdarzenia dot. klawiatury struct TIMER_EVENT timer_ev; /* Rekord opisu zdarzenia dot. klawiatury }; struct EVENT { unsigned char from_mouse : 1; /* 1 gdy zdarzenie pochodzi od myszki unsigned char from_keybd : 1; /* 1 gdy zdarzenie pochodzi od klawiat. unsigned char from_timer : 1; /* 1 gdy zdarzenie pochodzi od timera union EVENT_INFO ei; /* Unia zdarzeń -- 3 in 1 } event; . . . get_event_info( &event ); . . . if( event.from_mouse ) if( event.ei.mouse_ev.double_click ) find_object_on_pos( event.ei.mouse_ev.x, event.ei.mouse_ev.y ); . . . */ */ */ */ */ */ */ Wprowadzenie do programowania w języku C — dynamiczne struktury danych Dynamiczny przydział pamięci void * malloc( size_t size ); Rezultatem funkcji malloc jest wskaźnik do obszaru pamięci przeznaczonego dla obiektu o rozmiarze size. Rezultatem jest NULL jeżeli polecenie nie może być zrealizowane. Obszar nie jest inicjowany. void * calloc( size_t nitems, size_t size ); Rezultatem funkcji calloc jest wskaźnik do obszaru pamięci przeznaczonego dla nitems obiektów o rozmiarze size. Rezultatem jest NULL jeżeli polecenie nie może być zrealizowane. Obszar jest inicjowany zerami. void * realloc( void * ptr, size_t size ); Funkcja dokonuje próby zmiany rozmiaru bloku wskazywanego przez ptr, który był poprzednio przydzielony wywołaniem funkcji calloc lub malloc. Zawartość wskazywanego obszaru pozostaje niezmieniona. Jeżeli nowy rozmiar jest większy od poprzednio przydzielonego, dodatkowe bajty mają nieokreśloną wartość. Jeżeli nowy rozmiar jest mniejszy, bajty z różnicowego obszaru są zwalniane. Jeżeli ptr == NULL to funkcja działa jak malloc. Rezultatem funkcji jest wskaźnik na obszar pamięci o nowym rozmiarze (może być ulokowany w pamięci w innej lokalizacji niż poprzednio). Rezultatem jest NULL w przypadku błędu lub próby przydziału bloku o zerowym rozmiarze. void free( void * ptr ); Zwalnia obszar pamięci wskazywany przez ptr. Parametr musi być wskaźnikiem do obszaru pamięci przydzielonego uprzednio przez malloc, calloc lub realloc. int n = 300; char * s; . . . if( ( s = ( char * )malloc( n * sizeof( char ) ) ) != NULL ) { strcpy( s, "Dynamicznie przydzielony napis" ); for( i = 0; s[ i ] != ‘\0’; i++ ) putchar( s[ i ] ); . . . free( s ) } Alternatywne konstrukcje lecz calloc zeruje pamięć a malloc nie s = (char *)malloc(n*sizeof(char)) s = (char *)calloc(n,sizeof(char)) int queue_len = 32; struct EVENT * queue; . . . if( (queue=(struct EVENT *)calloc(queue_len,sizeof(struct EVENT)))!= NULL ) set_application_queue( queue ); . . . free( queue ); Wprowadzenie do programowania w języku C — dynamiczne struktury danych Rekurencyjne struktury danych Jednokierunkowa lista inwersyjna. Program przykładowy wczytuje linie tekstu z stdin, magazynuje je w liście, następnie wyświetla w odwrotnej kolejności. Wprowadzanie kończy naciśnięcie znaku odpowiadającego znacznikowi końca pliku (^Z — Dos/Windows, ^D — Unix). #include <stdio.h> #include <string.h> #include <stdlib.h> #define MAX_LEN 128 struct _NAME { char name[ MAX_LEN ]; struct _NAME * prev; }; /* Pole do przechowywania napisu */ /* Odsyłacz do poprzedniego elementu listy */ typedef struct _NAME NAME; int main() { NAME * last = NULL; NAME * work = NULL; char buffer[ MAX_LEN ]; /* Wskaźnik na koniec tworzonej listy */ /* Wskaźnik pomocniczy */ /* Bufor odczytywanej linii */ /* Czytaj linie dopóki nie wprowadzono znaku konca pliku (^Z lub ^D) while( gets( buffer ) != NULL ) { if( ( work = ( NAME * )malloc( sizeof( NAME ) ) )!= NULL ) { strcpy( work->name, buffer ); /* Zapamiętaj linię w el-cie listy work->prev = last; /* Dołącz element do listy last = work; /* Teraz nowy element jest ostatni } } . . . /* Wykorzystanie napisów zgromadzonych w liście */ for( work = last; work != NULL; work = work->prev ) printf( "\n%s", work->name ); /* Zwolnienie pamięci przydzielonej elementom listy*/ while( last != NULL ) { work = last; last = last->prev; free( work ); } return 0; } */ */ */ */ Wprowadzenie do programowania w języku C — dynamiczne struktury danych WskaŸniki przed rozpoczêciem tworzenia listy NAME * last = NULL; NAME * work = NULL; work work = ( NAME * )malloc( sizeof( NAME ) ) work last last Przydzia³ pamiêci dla nowego elementu work->prev = last; work last Ustalenie odsy³acza do poprzedniego elementu last = work; work last Do³¹czenie elementu do listy work = ( NAME * )malloc( sizeof( NAME ) ); work Przydzia³ pamiêci dla kolejnego elementu last work->prev = last; last Ustalenie odsy³acza do poprzedniego elementu last = work; Do³¹czenie elementu do listy work work last Wprowadzenie do programowania w języku C — dynamiczne struktury danych Lista dwukierunkowa — program przykładowy, temat jak poprzednio #include <stdio.h> #include <string.h> #include <stdlib.h> #define MAX_LEN 80 typedef struct _NAME { char name[ MAX_LEN ]; struct _NAME * next; struct _NAME * prev; } NAME; /* Pole do przechowywania napisu */ /* Odsyłacz do następnego elementu listy */ /* Odsyłacz do poprzedniego elementu listy */ int main() { NAME * first = NULL, * last = NULL; NAME * work; char buffer[ MAX_LEN ]; /* Początek i koniec listy */ while( gets( buffer ) != NULL ) { if( ( work = ( NAME * )malloc( sizeof( name ) ) )!= NULL ) { strcpy( work->name, buffer ); if( first == NULL ) /* Czy lista jest pusta? */ { work->next = work->prev = NULL; /* Nie ma poprz. i następnego */ first = last = work; /* Początek == koniec */ } else { work->next = NULL; /* Nie ma nastepnego – to ostatni element */ work->prev = last; /* Ostatni elem. staje się poprz. dla nowego */ last->next = work; /* Nowy element staje się następnym dla ostat.*/ last = work; /* Teraz nowy element staje się ostatnim */ }/* if-else */ }/* if */ }/* while */ /* Maszerujemy po liście – od początku do końca */ for( work = first; work != NULL; work = work->next ) printf( "\n%s", work->name ); /* Maszerujemy po liście – od końca do początku*/ for( work = last; work != NULL; work = work->prev ) printf( "\n%s", work->name ); /* Zwolnienie pamięci przydzielonej elementom listy*/ while( first != NULL ) /* Zwolnienie pamięci */ { work = first; first = first->next; free( work ); } return 0; } Wprowadzenie do programowania w języku C — dynamiczne struktury danych WskaŸniki przed rozpoczêciem tworzenia listy NAME * first = NULL; NAME * last = NULL; NAME * work = NULL; last work work = ( NAME * )malloc( sizeof( NAME ) ) work first first last Przydzia³ pamiêci dla nowego elementu work->next = work->prev = NULL; work first last Ustalenie odsy³acza do nastêpnego/poprzedniego elementu first = last = work; work first last Do³¹czenie elementu do listy work = ( NAME * )malloc( sizeof( NAME ) ); work last first last = work; work Przydzia³ pamiêci dla kolejnego elementu last last->next = work; Do³¹czenie elementu do listy work->next = NULL; work->prev = last; first