Wykłady z podstaw programowania f& Język C/C++
10. Typy strukturowe
Najprostszym agregatem danych w językach programowania jest tablica
elementów jednakowego typu. Tablica przechowywana jest we spójnym
obszarze pamięci. Dostęp do kolejnych jej elementów jest możliwy poprzez
indeksowanie elementów.
W wielu językach istnieje również możliwość stosowania
niejednorodnych struktur danych w postaci agregatu, zwanych rekordami
danych. Wprowadzenie rekordu danych do programu wymaga zdefiniowania
przez użytkownika języka nowego typu strukturowego, którego konstrukcja
opiera się głównie na typach wbudowanych w język.
Każdy rekord będąca instancją danego typu strukturowego jest zmienną
zajmującą pewien obszar pamięci. Wartości danych przechowywane w tym
obszarze zorganizowane są w postaci odrębnych pól, czyli podzmiennych lub
składowych rekordu, których typ może być jednym z typów wbudowanych w
język lub może być innym typem strukturowym.
Wszystkie rekordy danego typu strukturowego posiadają identyczny
zestaw pól. W definicji nowego typu strukturowego należy podać deklarację
(opis) poszczególnych pól.
Definiowanie typu i zmiennych strukturowych
W języku C definicja nowego typu strukturowego o podanej nazwie
posiada następującą ogólną składnię:
struct nazwa {
deklaracja_składowej;
deklaracja_składowej;
1
PDF created with pdfFactory Pro trial version www.pdffactory.com
Wykłady z podstaw programowania f& Język C/C++
...
deklaracja_składowej;
};
Deklaracja składowej struktury na ogół przyjmuje postać deklaracji
pola, czyli zwykłej zmiennej - prostej, tablicy, wskaznika itp. W specyfikacji
typu strukturowego można również podać definicją innego typu strukturowego
lub wyliczeń (enum).
Wszystkie nazwy zadeklarowane w typie strukturowym (nazwy pól,
innych typów) mają zasięg lokalny związany z danym typem i nie wchodzą w
konflikt z nazwami zadeklarowanymi w zakresie zewnętrznym.
Nazwę typu strukturowego można pominąć. W takim przypadku należy
po klamrze zamykającej strukturę podać deklaracje zmiennych, czyli rekordów
związanych z tą strukturą:
struct [nazwa] {
deklaracja_składowej;
deklaracja_składowej;
...
deklaracja_składowej;
} deklarator_zmiennej, ... ;
Nazwa typu strukturowego może być użyta do zadeklarowania
odpowiednich zmiennych poza definicja typu:
struct nazwa deklarator_zmiennej, ... ;
gdzie słowo kluczowe struct może być pominięte jedynie w języku C++.
2
PDF created with pdfFactory Pro trial version www.pdffactory.com
Wykłady z podstaw programowania f& Język C/C++
Deklarowane w obydwu p/w przypadkach zmienne mogą być w
ogólności nie tylko rekordami (zmiennymi strukturowymi) danego typu, ale
również tablicami rekordów lub wskaznikami na rekordy.
W języku C++ można również w podobny sposób zadeklarować
referencje do rekordów lub funkcje zwracające jako rezultat rekord.
Deklaracje zmiennych związanych z typem strukturowym można
poprzedzić specyfikatorem const, podobnie jak deklaracje zwykłych
zmiennych, co prowadzi do zmiennych ustalonych, np. niemodyfikowalnych
rekordów.
Dla obydwu p/w przypadków deklaracji rekordu danego typu
strukturowego inicjator rekordu przyjmuje następującą postać:
deklaracja_rekordu = {
wyrażenie, wyrażenie, ..., wyrażenie
};
Brak inicjatora w deklaracji rekordu powoduje, że wartość początkowa
wszystkich pól struktury jest nieokreślona.
Jeżeli jednak zostanie podany ciąg wyrażeń w nawiasach klamrowych,
to kolejnym polom rekordu zostaną przypisane wartości początkowe równe
wartościom kolejnych wyrażeń.
Liczba wyrażeń nie może być większa od liczby pól. Jeżeli liczba ta jest
mniejsza od liczby pól struktury, to brakujące wyrażenia zastępuje się
wartościami zerowymi.
Typy strukturowe i ich zmienne można definiować w zakresie
globalnym lub w ciele dowolnej funkcji.
Można również definiować typ i pola strukturowe w zakresie innej
struktury, co prowadzi do zagnieżdżeń struktur.
3
PDF created with pdfFactory Pro trial version www.pdffactory.com
Wykłady z podstaw programowania f& Język C/C++
Przykład :
struct osoba {
char nazwisko[25];
char imie[10];
int wiek;
struct adres_t {
char miasto[20];
char ulica[30];
int dom;
int mieszkanie;
} adres;
} kierownik = { "Kowalski", "Marek", 45,
{ "Bytom", "Lipowa", 12, 9 } };
struct osoba pracownicy[100], dostawca;
struct osoba klienci[2] = {
{ "Nowak", "Jerzy", 37,
{ "Chorzow", "Cicha", 132, 11 }
},
{ "Kansy", "Zbigniew", 55,
{ "Katowice", "Mlynska", 22, 5 }
} };
struct osoba *biezaca;
W przykładzie zadeklarowano nowy typ strukturowy o nazwie osoba.
Dowolna struktura (rekord) tego typu posiada pola: nazwisko, imie, wiek,
a także adres jako pole strukturowe składające się z podpól: miasto,
ulica, dom oraz mieszkanie. Następnie zadeklarowano zmienne
związane z tym typem: pojedynczy rekord z inicjatorem kierownik, tablica
100 rekordów pracownicy, pojedynczy rekord dostawca, tablica 2
rekordów z inicjatorem klienci oraz wskaznik biezaca wskazujący na
rekordy typu osoba.
4
PDF created with pdfFactory Pro trial version www.pdffactory.com
Wykłady z podstaw programowania f& Język C/C++
Dostęp do składowych struktury
W wielu językach rekord jako zmienna typu strukturowego zachowuje
się podobnie do zwykłych zmiennych, co oznacza że między innymi istnieje
możliwość kopiowania całych rekordów stosując zwykły operator przypisania.
W takim przypadku kopiowane są poszczególne pola rekordu.
Dowolne pole rekordu z osobna również odgrywa rolę zmiennej, a więc
może podlegać wszystkim operacjom dotyczącym zwykłych zmiennych, np.
można przypisać wartość pewnego wyrażenia polu lub skopiować wartość pola
do innej zmienne o identycznym lub podobnym typie.
Dostęp bezpośredni do dowolnego pola danego rekordu w celu odczytu
lub zapisu jego wartości wymaga zastosowania operatora selekcji w postaci
kropki. Istnieje również możliwość dostępu pośredniego do pola rekordu
poprzez wskaznik na rekordy danego typu strukturowego; wymaga to
zastosowania operatora selekcji w postaci strzałki. Obydwa operatory
posiadają bardzo wysoki priorytet.
W języku C bezpośrednie operacje odczytu i zapisu wartości pola
danego rekordu można w ogólności przedstawić następująco:
Gdy wyrażenie selekcji o postaci
rekord.pole przedstawiają
nieustalone pole - zmienną, wtedy
.... = rekord.pole
może występować z lewej strony
operatora przypisania, a więc
stanowi kolejny przykład złożonej
rekord.pole = .... ;
l-wartości.
Podobnie pośrednie operacje odczytu i zapisu wartości pola pewnego
rekordu poprzez jego wskaznik można w ogólności przedstawić następująco:
Wyrażenie wskaznik->pole
jest kolejnym przykładem złożonej
l-wartości, gdyż dla nieustalonego
.... = wskaznik->pole
rekordu reprezentuje zwykłą
zmienną (tj. pole rekordu) i stąd
wskaznik->pole = .... ;
może występować z lewej strony
operatora przypisania.
5
PDF created with pdfFactory Pro trial version www.pdffactory.com
Wykłady z podstaw programowania f& Język C/C++
Jeżeli w definicji typu strukturowego zadeklarowano nazwę innego typu
lub wyliczenia, to dostęp do tych nazw wymaga użycia operatora zakresu ::
posiadającego bardzo wysoki priorytet.
W języku C dostęp do nazwy dowolnego typu lub wyliczenia
zadeklarowanego w definicji typu strukturowego odbywa się zgodnie z
poniższą składnią:
typ::nazwa
Konstrukcja ta jest jedynie nazwą z dodatkową kwalifikacją
identyfikatorem typu i wobec tego może być używane jak zwykła, pojedyncza
nazwa, w szczególności można jej użyć w roli typu do deklarowania
zmiennych.
Przykład :
struct vec { int x,y,z; }; // typ strukturowy wektor 3D
int y; // zwykła zmienna całkowita
vec v; // rekord z polami całkowitymi x,y,z o nieokreślonej wartości
v.z = 10; // bezpośredni zapis wartości do pola v.z (v.z jest l-wartością)
v.x = v.y = 20;
y = v.x+v.y+v.z; // bezpośredni odczyt wartości ze v.z
6
PDF created with pdfFactory Pro trial version www.pdffactory.com
Wykłady z podstaw programowania f& Język C/C++
Przykład :
Przyjmując z poprzedniego paragrafu przykładową definicję typu osoba oraz
jego zmiennych można wykonać następujące operacje na polach i rekordach:
strcpy(dostawca.imie,"Artur");
dostawca.wiek = 34;
dostawca.adres.dom = 267;
for (int i=0; i<100; i++) {
cout << "Pracownik " << i << endl;
cout << " imie: "; cin >> pracownicy[i].imie;
cout << " wiek: "; cin >> pracownicy[i].wiek;
cout << " adres" << endl;
cout << " miasto: ";
cin >> pracownicy[i].adres.miasto;
cout << " ulica: ";
cin >> pracownicy[i].adres.ulica;
}
biezaca = &klienci[0];
strcpy(biezaca->imie,"Roman");
biezaca->wiek = 47;
biezaca->adres.dom = 67;
klienci[1] = dostawca;
struct
osoba::adres_t adres_kierownika = kierownik.adres;
Dodatkowo w przykładzie zadeklarowano nową zmienną typu strukturowy o
nazwie adres_t. Typ ten jest zagnieżdżony w strukturze osoba, stąd dostęp
do jego nazwy odbywa się poprzez konstrukcję osoba::nazwa.
7
PDF created with pdfFactory Pro trial version www.pdffactory.com
Wykłady z podstaw programowania f& Język C/C++
Deklarowanie nazw typów
Często istnieje potrzeba stosowania synonimów nazw typów
wbudowanych lub zdefiniowanych przez użytkownika języka. Nowe nazwy
typów ułatwiają zrozumienie przeznaczenia typu, a w przypadku typów
złożonych (np.: wskaznikowych, tablicowych) - upraszczają zapis deklaracji
odpowiednich zmiennych.
Deklaracja nazwy typu w języku C ma postać deklaracji zwykłej
zmiennej danego typu poprzedzonej specyfikatorem typedef:
typedef deklaracja_zmiennej;
Przykład:
typedef int integer;
integer i, j;
typedef double real;
real x, y;
typedef char napis[80];
napis napis1, napis2;
typedef struct {
double x,y,z;
} wektor;
typedef wektor *wektwsk;
wektor v1, v2;
wektwsk p1, p2;
8
PDF created with pdfFactory Pro trial version www.pdffactory.com
Wykłady z podstaw programowania f& Język C/C++
Powiązanie algorytmów ze danymi
W paradygmacie programowania strukturalnego istnieje możliwość
zarówno podziału kodu programu na niezależne procedury będące realizacją
pewnych algorytmów, jak i podziału zbioru danych na osobne struktury w
większym stopniu odpowiadające elementom lub ogólniej pojęciom z
dziedziny zastosowań programu (np. struktura danych opisujących osobę).
Często w programach istnieje pewne logiczne powiązanie pomiędzy
procedurami (algorytmami) a strukturami danych, na których te procedury
działają. W nowoczesnym paradygmacie programowania obiektowego
występują odpowiednie konstrukcje składniowe wspierające powiązanie
algorytmów ze strukturami danych. Mianowicie, składnia klasy, czyli
deklaracja typu obiektowego, pozwala tworzyć programy za pomocą obiektów
posiadających swój stan, czyli pola danych, oraz algorytmy w postaci metod do
odczytu i zmiany stanu.
W ten sposób klasa staje się w programie obiektowym wyraznym
odpowiednikiem jakiegoś pojęcia z dziedziny zastosowań programu, zaś jej
obiekt reprezentuje pojedynczy byt (rzecz, osobę) objęty danym pojęciem.
Programowanie obiektowe pozwala zatem bardziej precyzyjnie modelować w
programie otaczającą rzeczywistość.
9
PDF created with pdfFactory Pro trial version www.pdffactory.com
Wykłady z podstaw programowania f& Język C/C++
Natomiast w zwykłym programowaniu strukturalnym brak jest
wyraznego wsparcia składniowego dla powiązania algorytmów ze danymi,
mimo to można takie powiązanie umownie określić w programie. Zwykle
stosuje się w tym celu typ strukturowy pełniący rolę klasy z programu
obiektowego oraz luzny zbiór funkcji, czyli metod do przetwarzania rekordów
tego typu. Funkcje te powinny zatem posiadać przynajmniej jeden parametr
typu wskaznika do przetwarzanych rekordów.
Przykład:
// Struktura definiującą rekord danych osobowych:
struct osoba {
char nazwisko[25];
char imie[10];
int wiek;
// ...
};
10
PDF created with pdfFactory Pro trial version www.pdffactory.com
Wykłady z podstaw programowania f& Język C/C++
// deklaracje funkcji do przetwarzania rekordów typu osoba:
void inicjuj(struct osoba *x); // konstruktor danych rekordu
void zakoncz(struct osoba *x); // destruktor danych rekordu
void odczytaj(struct osoba *x); // odczytuje dane rekordu
void zapisz(const struct osoba *x); // zapisuje dane rekordu
// ...
// kod główny używający rekordów typu osoba i wywołujący jego funkcje:
osoba *X = (osoba *)malloc( sizeof(osoba) );
inicjuj(X);
odczytaj(X);
zapisz(X);
zakoncz(X);
free(X);
X = NULL;
// definicje funkcji do przetwarzania rekordów typu osoba:
void inicjuj(struct osoba *x)
{
strcpy(x->imie,"osoba");
strcpy(x->nazwisko,"nieokreślona");
dostawca.wiek = -1;
// ... przydziela dla x pewne zasoby systemu
}
void zakoncz(struct osoba *x)
{
// ... zwalnia zasoby systemu przydzielone dla x
}
void odczytaj(struct osoba *x)
{
cout << "podaj imie: "; cin >> x->imie;
cout << "podaj nazwisko: "; cin >> x->nazwisko;
cout << "podaj wiek: "; cin >> x->wiek;
}
11
PDF created with pdfFactory Pro trial version www.pdffactory.com
Wykłady z podstaw programowania f& Język C/C++
void zapisz(const struct osoba *x)
{
cout << x->imie << " " << x->nazwisko;
cout << ", lat " x->wiek;
}
Uwaga: Liczba bajtów pamięci zajmowanej przez rekord, czyli rozmiar pamięci zwracany
przez operator sizeof zastosowany do nazwy rekordu lub jego typu strukturowego, jest
równy sumie rozmiarów poszczególnych pól rekordu:
sizeof rekord == sizeof(struktura)
== sizeof rekord.pole1 + sizeof rekord.pole2 + ...
== sizeof(typ_pola1) + sizeof(typ_pola2) + ...
12
PDF created with pdfFactory Pro trial version www.pdffactory.com
Wyszukiwarka
Podobne podstrony:
w08 PodstPrzy roznorKOMUNIKACJA PODSTPSYCH WYK2podstprog01w02 PodstPrzy zyciepodstprog02podstprw04 PodstPrzy proddekompw07 PodstPrzy krajobrazypodstprog07podstprog04podstprog09w05 PodstPrzy cyklepodstprog06w06 PodstPrzy klimatwięcej podobnych podstron