cpp2 LEKCJA14


LEKCJA 14. Jak tworzyć i stosować struktury.

________________________________________________________________

W trakcie tej lekcji poznasz pojęcia:
* Klasy zmiennej.
* Struktury.
* Pola bitowego.
* Unii.
Dowiesz się także więcej o operacjach logicznych.
________________________________________________________________


CO TO JEST KLASA ZMIENNEJ?

W języku C i C++ programista ma większy wpływ na rozmieszczenie
zmiennych w pamięci operacyjnej komputera i w rejestrach
mikroprocesora. Może to mieć decydujący wpływ na dostępność
danych z różnych miejsc programu i szybkość działania programu.
Należy podkreślić, że TYP ZMIENNEJ (char, int, float itp.)
decyduje o sposobie interpretacji przechowywanych w pamięci zer
i jedynek, natomiast KLASA ZMIENNEJ decyduje o sposobie
przechowywania zmiennej w pamięci. W C++ występują cztery klasy
zmiennych.

ZMIENNE STATYCZNE - static.

Otrzymują stałą lokalizację w pamięci w momencie uruchamiania
programu. Zachowują swoją wartość przez cały czas realizacji
programu, chyba, że świadomie zażądamy zmiany tego stanu - np.
instrukcją przypisania.
Przykład deklaracji: static float liczba;

W większości kompilatorów C++ zmienne statyczne, które nie
zostały jawnie zainicjowane w programie, otrzymują po
zadeklarowaniu wartość ZERO.

ZMIENNE AUTOMATYCZNE - auto.

Otrzymują przydział miejsca w pamięci dynamicznie - na stosie
procesora, w momencie rozpoczęcia wykonania tego bloku programu,

w którym zmienne te zostały zadeklarowane. Przydzielenie pamięci

nie zwalnia nas z obowiązku zainicjowania zmiennej (wcześniej
wartość zmiennej jest przypadkowa). Zmienne automatyczne
"znikają" po zakończeniu wykonywania bloku. Pamięć im
przydzielona zostaje zwolniona. Przykład: auto long suma;

ZMIENNE REJESTROWE - register.

Zmienne rejestrowe są także zmiennymi lokalnymi, widocznymi
tylko wewnątrz tego bloku programu, w którym zostały
zadeklarowane. C++ może wykorzystać dwa rejestry mikroprocesora
- DI i SI do przechowywania zmiennych. Jeśli zadeklarujemy w
programie więcej zmiennych jako zmienne rejestrowe - zostaną one

umieszczone na stosie. Znaczne przyspieszenie działania programu

powoduje wykorzystanie rejestru do przechowywania np. licznika
pętli.

Przykład:

register int i;
.....
for (i=1; i<1000; i++) {.....}

ZMIENNE ZEWNĘTRZNE - extern.

Jeśli zmienna została - raz i TYLKO RAZ - zadeklarowana w
pojedynczym segmencie dużego programu, zostanie w tymże
segmencie umieszczona w pamięci i potraktowana podobnie do
zmiennych typu static. Po zastosowaniu w innych segmentach
deklaracji extern zmienna ta może być dostępna w innym segmencie

programu.

Przykład: extern int NUMER;


STRUKTURY.

Poznane wcześniej tablice mogą zawierać wiele danych, ale
wszystkie te dane muszą być tego samego typu. Dla zgrupowania
powiązanych ze sobą logicznie danych różnego typu C/C++ stosuje
STRUKTURY, deklarowane przy pomocy słowa struct. Kolejne pola
struktury są umieszczane w pamięci zgodnie z kolejnością ich
deklarowania. Strukturę, podobnie jak zmienną, MUSIMY
ZADEKLAROWAĆ. Struktura jest objektem bardziej złożonym niż
pojedyncza zmienna, więc i deklaracja struktury jest bardziej
skomplikowana. Deklaracja struktury składa się z następujących
elementów:

1. Słowo kluczowe struct (obowiązkowe).
2. Nazwa (opcjonalna). Jeśli podamy nazwę, to nazwa ta będzie
oznaczać dany typ struktury.
3. Nawias klamrowy {
4. Deklaracje kolejnych składników struktury.
5. Nawias klamrowy }
6. Lista nazw struktur określonego powyżej typu (może zostać
zadeklarowana oddzielnie).

Przykład. Deklaracja ogólnego typu struktury i określenie
wewnętrznej postaci struktury.

struct Ludzie
{
char Imiona[30];
char Nazwisko[20];
int wiek;
char pokrewienstwo[10]
};

Jeśli określimy już typ struktury - czyli rodzaj, wielkość i
przeznaczenie poszczególnych pól struktury, możemy dalej tworzyć

- deklarować i inicjować konkretne struktury danego typu.

Przykład. Deklaracja zmiennych - struktur tego samego typu.

struct Ludzie Moi, Twoi, Jego, Jej, Szwagra;

Deklarację struktur można połączyć.

Przykład. Połączona deklaracja struktur.

struct Ludzie
{ char pokrewienstwo[10];
char Imiona[30];
int wiek;
} Moi, Twoi, Szwagra;

Struktury statyczne

* mają stałe miejsce w pamięci w trakcie całego programu;
* są "widoczne" i dostępne w całym programie.

Zadeklarujemy teraz typ struktury i zainicjujemy dwie struktury.


Przykład. Zainicjowanie dwu struktur statycznych.

struct Ludzie
{ char pokrewienstwo[10];
char Imiona[30];
int wiek;
};

struct Ludzie Moi, Szwagra;
static struct Ludzie Moi = { "Stryjek", "Walenty", 87 };
static struct Ludzie Szwagra = { "ciotka", "Ala", 21 };

Zapis

static struct Ludzie Szwagra;

oznacza:
statyczna struktura typu "Ludzie" pod nazwą "Szwagra".

Do struktury w całości możemy odwoływać się za pomocą jej nazwy
a do poszczególnych elementów struktury poprzez nazwę struktury
i nazwę pola struktury - ROZDZIELONE KROPKĄ ".". Zademonstrujmy
to na przykładzie. Zwróć uwagę na różne sposoby przekazywania
danych pomiędzy strukturami:

C4.Wiek=Czlowiek2.Wiek; - przekazanie zawartości pojedynczego
pola numerycznego;
C4=Czlowiek3; - przekazanie zawartości całej struktury Czlowiek3

do C4.

Przykład. Program manipulujący prostą strukturą.

[P037.CPP]

int main()
{

struct Ludzie
{
char Imie[20];
int Wiek;
char Status[30];
char Tel_Nr[10];
};

static struct Ludzie
Czlowiek1={"Ala", 7, "Ta, co ma Asa","?"},
Czlowiek2={"Patrycja", 13, "Corka", "8978987"},
Czlowiek3={"Krzysztof", 27, "Kolega z przedszkola", "23478"};

struct Ludzie C4, C5;

C4=Czlowiek3;
C4.Wiek=Czlowiek2.Wiek;
C5=Czlowiek1;

clrscr();

printf("%s %d %s\n", C4.Imie, C4.Wiek, C4.Status);
printf("%s %s",C5.Imie, C5.Status);

return 0;
}

Tablice mogą być elementami struktur, ale i odwrotnie - ze
struktur, jak z cegiełek można tworzyć konstrukcje o wyższym
stopniu złożoności - struktury struktur i tablice struktur.
Jeśli tablica składa się z liczb typu int, to deklarujemy ją:

int TABLICA[10];

jeśli tablica składa się ze struktur, to deklarujemy ją:

struct TABLICA[50];

W przykładzie poniżej przedstawiono

* deklarację jednowymiarowej tablicy LISTA[50],
* elementami tablicy są struktury typu SCzlowiek,
* jednym z elementów każdej struktury SCzlowiek jest struktura
"niższego rzędu" typu Adres;

[P038.CPP]

int main()
{

struct Adres
{
char Ulica[30];
int Nr_Domu;
int Nr_Mieszk;
};

struct SCzlowiek
{
char Imie[20];
int Wiek;
struct Adres Mieszkanie;
};

struct SCzlowiek LISTA[50];

LISTA[1].Wiek=34;
LISTA[1].Mieszkanie.Nr_Domu=29;
printf("%d", LISTA[1].Mieszkanie.Nr_Domu);

return 0;
}

Zapis

printf("%d", LISTA[1].Mieszkanie.Nr_Domu

oznacza:
* wybierz element nr 1 z tablicy LISTA;
(jak wynika z deklaracji tablicy, każdy jej element będzie miał
wewnętrzną strukturę zorganizowaną tak, jak opisano w deklaracji

struktury SCzlowiek);
* wybierz ze struktury typu SCzlowiek pole Mieszkanie;
(jak wynika z deklaracji, pole Mieszkanie będzie miało
wewnętrzną organizację zgodną ze strukturą Adres);
* ze struktury typu Adres wybierz pole Nr_Domu;
* Wydrukuj zawartość pola pamięci interpretując ją jako liczbę
typu int - w formacie %d.

Słowo struktura tak doskonale pasuje, że chciałoby się
powiedzieć:
jeśli struktura struktur jest wielopoziomowa, to podobnie, jak
przy wielowymiarowych tablicach, każdy poziom przy nadawaniu
wartości musi zostać ujęty w dodatkową parę nawiasów klamrowych.


[???] A CO Z ŁAŃCUCHAMI ZNAKOWYMI ?
________________________________________________________________
Język C++ oferuje do kopiowania łańcuchów znakowych specjalną
funkcję strcpy(). Nazwa funkcji to skrót STRing CoPY (kopiuj
łańcuch). Sposób wykorzystania tej funkcji:

strcpy(Dokąd, Skąd); lub
strcpy(Dokąd, "łańcuch znaków we własnej osobie");

Szczegóły - patrz Lekcja o łańcuchach znakowych.
________________________________________________________________


STRUKTURY I WSKAŹNIKI.

Wskaźniki mogą wskazywać strukturę w całości lub element
struktury. Język C/C++ oferuje specjalny operator -> który
pozwala na odwoływanie się do elementów struktury. W przykładzie

poniżej przedstawiono różne sposoby odwołania się do elementów
trzech identycznych struktur STA, STB, STC.

[P039.CPP]

int main()
{

struct
{
char Tekst[20];
int Liczba1;
float Liczba2;
} STA, STB, STC, *Pointer;

STA.Liczba1 = 1;
STA.Liczba2 = 2.2;
strcpy(STA.Tekst, "To jest tekst");

STB=STA;

Pointer = &STC;
Pointer->Liczba1 = 1;
Pointer->Liczba2 = 2.2;
strcpy(Pointer->Tekst, STA.Tekst);

printf("\nLiczba1-STA Liczba2-STB Tekst-STC\n\n");
printf("%d\t", STA.Liczba1);
printf("%f\t", STB.Liczba2);
printf("%s", Pointer->Tekst);

return 0;
}

Rozszyfrujmy zapis:

strcpy(Pointer->Tekst, STA.Tekst);

Skopiuj łańcuch znaków z pola Tekst struktury STA do pola Tekst
struktury wskazywanej przez pointer. Prawda, że to całkiem
proste?

[???] CZY MUSIMY TO ROZDZIELAĆ ?
________________________________________________________________
Jak zauważyłeś, liczby moglibyśmy zapisywać także jako łańcuchy
znaków, ale wtedy nie moglibyśmy wykonywać na tych liczbach
działań. Konwersję liczba - łańcuch znaków lub odwrotnie łańcuch

znaków - liczba wykonują w C specjalne funkcje np.:
atoi() - Ascii TO Int.;
itoa() - Int TO Ascii itp.
Więcej informacji na ten temat i przykłady znajdziesz w dalszej
części książki.
________________________________________________________________


Elementami struktury mogą być zmienne dowolnego typu, łądznie z
innymi strukturami.

Ciekawostka:
________________________________________________________________
Wskaźnik do deklarowanej struktury może być w języku C/C++ jak
jeden z jej WŁASNYCH elementów. Jeśli wskaźnik wchodzący w skład

struktury wskazuje na WŁASNĄ strukturę, to nazywa się to
AUTOREFERENCJĄ STRUKTURY.
________________________________________________________________

POLA BITOWE.

Często zdarza się, że jakaś zmienna ma zawężony zakres wartości.

Dla przykładu zmienne logiczne (tzw. flagi) to zawsze tylko 0
lub 1. Wiek rzadko przekracza 255 lat a liczba dzieci zwykle nie

jest większa niż 15. Nawet najbardziej niestali panowie nie
zdążą ożenić się i rozwieść więcej niż 7 razy. Gdybyśmy zatem
chcieli zapisać informacje

* płeć 0 - mężczyzna, 1 - kobieta ( 1 bit );
* wiek 0 - 255 lat (8 bitów);
* ilość dzieci 0 - 15 (4 bity);
* kolejny numer małżeństwa 0 - 7 (3 bity);

to przecież wszystkie te informacje mogą nam się zmieścić w
jednym szesnastobitowym rejestrze lub w dwu bajtach pamięci.
Takie kilka bitów wydzielone i mające określone znaczenie to
właśnie pole bitowe. C++ pozwala także na uwzględnianie znaku w
polach bitowych. Pola bitowe mogą być typu int i unsigned int
(czyli takie jak w przykładzie poniżej). Jeśli jakieś dane
chcemy przechowywać w postaci pola bitowego, w deklaracji
struktury sygnalizujemy to dwukropkiem. Stwarza to dwie istotne
możliwości:
* bardziej ekonomicznego wykorzystania pamięci;
* łatwego dodatkowego zaszyfrowania danych.

[P040.CPP]

//Pamietaj o dolaczeniu plikow naglowkowych !

int main()
{

struct USC {
int Sex : 1;
unsigned Wiek : 8;
unsigned Dzieci : 4;
unsigned Ktora : 3; } Facet;

int bufor;
clrscr();
Facet.Sex = 0;
printf("\n Ile ma lat ? : ");
scanf("%d", &bufor); Facet.Wiek = bufor;
printf("\n Ktore malzenstwo ? : ");
scanf("%d", &bufor); Facet.Ktora = bufor;
printf("\n Ile dzieci ? : ");
scanf("%d", &bufor); Facet.Dzieci = bufor;
printf("\n\n");
if (Facet.Ktora) printf("Facet ma %d zone", Facet.Ktora);
printf("\nPlec: Dzieci: Wiek (lat): \n\n");
printf("%d\t%d\t%d", Facet.Sex, Facet.Dzieci, Facet.Wiek);
getch();

return 0;
}

Uruchom program i sprawdź co się stanie, jeśli Facet będzie miał

np. 257 lat lub 123 żonę. Przekroczenie zadeklarowanego zakresu
powoduje obcięcie części bitów.

Aby uzyskać "wyrównanie" pola bitowego do początku słowa należy
przed interesującym naspolem bitowym zdefiniować tzw. pole
puste:

* pole bitowe bez nazwy;
* długość pola pustego powinna wynosić 0.

Poniżej przedstawiam przykład pola bitowego zajmującego trzy
kolejne słowa 16 bitowe. Dodanie pola pustego wymusza
rozpoczęcie pola pole_IV od początku trzeciego słowa maszynowego

(zakładamy, że pracujemy z komputerem 16 bitowym).

struct
{
unsigned pole_I:4;
unsigned pole_II:10;
unsigned pole_III:4;
unsigned :0; /* to jest pole puste */
unsigned pole_IV:5;
} pole_przykladowe;

Zwróć uwagę, że część bitów w drugim i trzecim słowie maszynowym

nie zostanie wykorzystana.

UNIE czyli ZMIENNE WARIANTOWE.

Unie to specyficzne struktury, w których pola pamięci
przeznaczone na objekty różnego typu nakładają się. Jeśli jakaś
zmienna może być reprezentowana na kilka sposobów (wariantów) to

sensowne jest przydzielenie jej nie struktury a unii. W danej
chwili pole pamięci należące do unii może zawierać TYLKO JEDEN
WARIANT. W przykładzie - albo cyfrę (która znakowo jest widziana

jako znak ASCII o kodzie 2,3,4 itd.) albo napis. Do
zadeklarowania unii służy słowo kluczowe union.

[P041.CPP]

#include "string.h"
#include "stdio.h"

int BUFOR, i;

int main()
{

union
{
int Cyfra;
char Napis[20];
} Unia;

for (i=1; i<11; i++)
{
printf("\n Podaj liczbe jednocyfrowa: ");
scanf("%d", &BUFOR);
if (BUFOR<0 || BUFOR>9)
strcpy(Unia.Napis, "TO NIE CYFRA !");
else
Unia.Cyfra = BUFOR;
printf("\n Pole jako Cyfra Pole jako Napis \n");

/* Tu wyswietlimy warianty: Pole jako cyfra i jako napis*/
/* Petla pozwoli Ci przeanalizowac wszystkie cyfry 0...9 */

printf(" %d\t\t\t%s", Unia.Cyfra, Unia.Napis);
}
return 0;
}


Pętla w przykładzie nie ma znaczenia. Służy tylko dla Twojej
wygody - dzięki niej nie musisz uruchamiać programu
przykładowego wielokrotnie. Podobnie zmienne BUFOR oraz i mają
znaczenie pomocnicze. Zwróć uwagę, że nieprawidłowa
interpretacja zawartości pola unii może spowodować wadliwe
działanie programu.

[Z]
________________________________________________________________
1. W programie przykładowym zamień unię na strukturę. Porównaj
działanie.
2 Przydziel na Wiek w strukturze Facet o jeden bit mniej. Ile
lat może teraz mieć Facet ?
3. Zmodyfikuj program przykładowy tak, by napis o liczbie
mężów/żon zależał od płci - pola Sex.
4. Zamieniwszy unię na strukturę w programie, sprawdź, czy
wpływa to na wielkość pliku *.EXE.
________________________________________________________________

OPERACJE LOGICZNE.

Zaczniemy od operacji logicznych na pojedynczych bitach liczb
całkowitych. W C++ mamy do dyspozycji następujące operatory:

~Zaprzeczenie (NOT) ~0=1; ~1=0;
|Suma (OR) 0|0=0; 0|1=1; 1|0=1; 1|1=1;
&Iloczyn (AND) 0&0=0; 0&1=0; 1&0=0; 1&1=1;
^Alternatywa wyłączna ALBO...ALBO (XOR)
0^0=0; 0^1=1; 1^0=1; 1^1=0;
<<< 00001000 = 00010000 dzieś. 8<<1=16
>>Przesunięcie bitów w prawo (Shift Right)
>> 00001000 = 00000100 dzieś. 8>>2=2

Miło byłoby pooglądać to trochę dokładniej w przykładowych
programach, ale potrzebne nam do tego będą funkcje. Zajmijmy się
więc uważniej funkcjami.


Wyszukiwarka

Podobne podstrony:
cpp2 LEKCJA43
cpp2 LEKCJA33
cpp2 LEKCJA15
cpp2 LEKCJA10
cpp2 LEKCJA38
cpp2 LEKCJA18
cpp2 LEKCJA19
cpp2 LEKCJA2
cpp2 LEKCJA48
cpp2 LEKCJA44
cpp2 LEKCJA37
cpp2 LEKCJA1
cpp2 LEKCJA22
cpp2 LEKCJA46
cpp2 LEKCJA35

więcej podobnych podstron