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