Wieczorowe Studia Licencjackie Wrocław, 12.12.2006 Wstęp do programowania Wykład nr 11 Struktury Podstawowe pojęcia Struktura pozwala na zebranie razem danych różnych typów (np. danych osobowych). Definicja określa elementy zwane polami. Poniżej przykładowa definicja struktury: struct dana{ int wiek; ; Definiuje ona nowy typ danych, do którego odwoływać się będziemy poprzez struct dana Na przykład deklaracje struct dana x, y; struct dana tab[100]; tworzą zmienne x i y składające się z jednego elementu typu struct dana oraz tablicę tab składającą się ze 100 takich elementów. Każdy element typu struct dana składa się z pól nazw i wiek. Dostęp do tych elementów uzyskujemy w sposób przedstawiony na poniższych przykładach: x.nazw tab[15].nazw x.wiek tab[15].wiek Elementy te jak i całe struktury mogą być wykorzystywane w podstawieniach, porównaniach, wyrażeniach, itp., np.: tab[10]=x; y=tab[i]; x.nazw=z; tab[j].wiek=j; gdzie z jest typu char nazw[dlnazw], i, j są typu int. Przykład program wykorzystujący tablicę struktur #include <stdio.h> #define dlnazw 10 #define lnazw 100 /* sortowanie tablicy struktur */ struct dana { int wiek; ; struct dana tab[lnazw]; //tablica struktur int n; FILE *fi, *fo;
void czytslowo(char *slowo) // czyta slowo az do napotkania spacji { int i=0; char ch; while (i<dlnazw && (ch=fgetc(fi))!=' ') //getc czyta jeden znak slowo[i++]=ch; void czyt(void) fscanf(fi, "%d\n", &n);//pierwszy wiersz pliku oznacza liczbe osob { //jeden wiersz pliku to nazwisko i wiek jednej osoby czytslowo(tab[i].nazw); fscanf(fi, "%d\n",&tab[i].wiek); void insertsort() { int i,j; struct dana x; for (i=1; i<n; i++) {x=tab[i]; j=i-1; while (x.wiek < tab[j].wiek && j>=0) //sortujemy według wieku tab[j+1]=tab[j--]; tab[j+1]=x; void pisz(void) { //jeden wiersz pliku to nazwisko i wiek jednej osoby fprintf(fo, "%s %d\n", tab[i].nazw, tab[i].wiek); main() { fi=fopen("dane.txt","r"); fo=fopen("wynik.txt","w"); czyt(); insertsort(); pisz() fclose(fi); fclose(fo); Listy Załóżmy, że chcemy przechowywać dane (np. osobowe), przy czym: - nie jest z góry znany rozmiar danych (np. liczba osób) - dość często niektóre informacje są usuwane i dodawane są nowe. Wówczas, zastosowanie tablic pociąga za sobą następujące problemy: - konieczne jest zadeklarowanie ( zajęcie ) rozmiaru tablicy odpowiadającego maksymalnemu przewidywanemu rozmiarowi danych;
- każda operacja wstawiania i usuwania wymaga przemieszczania znacznej części danych. W takich sytuacjach (i innych), stosujemy listy. Z punktu widzenia typów danych, możliwość utworzenia listy wymaga jedynie utworzenia typu struktur zawierających (oprócz danych właściwych ) jednego specyficznego pola oznaczającego wskaźnik na następny element listy. Na przykład struct elem { //dane wlasciwe int wiek struct elem *nast; // wskaznik na nastepny element ; Przypomnijmy, że deklaracja TYP *ZMIENNA; tworzy zmienną, która jest wskaźnikiem na element typu TYP. A zatem int *i; oznacza, że zmienna i jest wskaźnikiem na element typu int, a struct elem *nast; // wskaznik na nastepny element oznacza, że zmienna nast jest wskaźnikiem na element typu struct elem. Natomiast zadeklarowanie nast jako pola struktury typu struct elem pozwoli nam tworzyć listy. Dostęp do listy uzyskujemy poprzez wskaźnik na jej pierwszy element: struct elem *lis; Zauważmy, zadeklarowana powyżej zmienna lis ma taki sam typ jak pole nast w strukturze struct elem. Przykład lis nazw Kowal nazw Nowak nazw Smith nazw Ondracek wiek 25 wiek 17 wiek 44 wiek 17 nast. nast. nast nast. NULL Jak powstaje lista... Zadeklarowanie zmiennej struct elem *lis; nie powoduje utworzenia struktury typu struct elem a jedynie zmiennej, która jest wskaźnikiem na taką strukturę. Utworzenie struktury wymaga zaalokowania pamięci: lis = (struct elem *) malloc(sizeof(struct elem)); gdzie: - malloc(i) rezerwuje pamięć o rozmiarze i jednostek pamięci - sizeof(typ) podaje liczbę jednostek pamięci zajmowanych przez elementy typu TYP - (struct elem *) to rzutowanie typu, które powoduje, że wynik funkcji malloc(...) traktowany będzie jako wskaźnik na element typu struct elem.
Odwołania do pól struktur raz jeszcze... Mamy następujące deklaracje: struct elem x; struct elem *y; A zatem odwołania do pól wyglądają następująco: x.nazw *y.nazw x.wiek *y.wiek x.nast *y.nast operator * przy y jest konieczny, ponieważ y nie jest strukturą lecz jedynie wskaźnikiem na nią. Jednak zapis ten ma inną, bardziej intuicyjną alternatywę: *y.nazw y->nazw *y.wiek y->wiek *y.nast y->nast
Przykład program umieszczający dane o osobach na liście W pliku dane.txt umieszczamy informacje o osobach w nastepujacym formacie: - pierwszy wiersz pliku zawiera jedną liczbę liczbę osób - każdy kolejny wiersz pliku zawiera nazwisko o osoby i jej wiek, oddzielone spacją. Poniżej prezentujemy program, który dane z pliku umieszcza w liscie i wypisuje je na ekran. #include <stdio.h> #include <stdlib.h> #define dlnazw 10 #define llat 100 /* lista nazwisk osob i ich wiek */ struct dana { int wiek; struct dana *nast; ; struct dana *lis;//lis oznacza poczatek listy int n; void czyt(void) {int i,wiek; char nazwisko[dlnazw]; struct dana *x; lis=null; //na poczatku lista jest pusta printf("ile osob : "); scanf("%d\n", &n); { x=(struct dana *) malloc(sizeof(struct dana)); printf("podaj nazwisko : "); scanf("%s", x->nazw); //bez kontroli dlugosci!!! printf("podaj wiek : "); scanf("%d", &x->wiek); x->nast=lis; // podpinamy liste do nowego elementu lis=x; //zmieniamy wskaznik na poczatek listy void druk(void) struct dana *x, *pom; x=lis; printf("\n"); while (x!=null) { printf("%s %d\n",x->nazw,x->wiek); pom=x; x=x->nast; //przesuwamy się na nastepny element listy free(pom); // zwalniamy pamiec main() { czyt(); druk();