• Nie Znaleziono Wyników

Podstawy programowania. Wykład 7 Tablice wielowymiarowe, SOA, AOS, itp.

N/A
N/A
Protected

Academic year: 2021

Share "Podstawy programowania. Wykład 7 Tablice wielowymiarowe, SOA, AOS, itp."

Copied!
20
0
0

Pełen tekst

(1)

Podstawy programowania.

Wykład 7

Tablice wielowymiarowe, SOA, AOS, itp.

(2)

Tablice wielowymiarowe

C umożliwia definiowanie tablic wielowymiarowych

najczęściej stosowane są tablice dwuwymiarowe

matematycznym odpowiednikiem tablic dwuwymiarowych są macierze

Definicja tablicy k-wymiarowej ma postać:

typ nazwa[N_1][N_2]..[N_k]; // + ewentualne inicjowanie, np.

int tab_int_2D[2][3] = { {0,1,2}, {4,5,6} };

np. dla tablicy znaków przechowującej wiele linii teksu:

#define MAX_DL_L 55 // maksymalna liczba znaków w jednej linii tekstu

#define MAX_N_L 15 // maksymalna liczba linii tekstu char tekst_wieloliniowy[MAX_N_L][MAX_DL_L];

Tablice wielowymiarowe przechowywane są w kolejnych komórkach pamięci

istnieje tylko jeden symbol (nie zmienna) w programie zawierający adres tablicy wielowymiarowej

(3)

Tablice wielowymiarowe

Dostęp do elementu tablicy wykorzystuje notację:

int linia = 5; int znak_w_linii = 27;

tekst_wieloliniowy[ linia ][ znak_w_linii ] = 'q';

printf("%c", tekst_wieloliniowy[ 5 ][ 27 ]);

Standardowy sposób przechowywania tablic dwuwymiarowych w C oznacza przechowywanie macierzy wierszami

indeksy elementu tablicy służą do prostego obliczenia jego adresu:

adres = początek + ( linia*MAX_DL_L + znak_w_linii ) * rozmiar_typu

z tego względu tablice wielowymiarowe często zamieniane są na tablice jednowymiarowe:

char tekst_wieloliniowy_1D[ MAX_N_L*MAX_DL_L ];

int linia = 5; int znak_w_linii = 27;

tekst_wieloliniowy_1D[ linia*MAX_DL_L + znak_w_linii ] = 'q';

printf("%c", tekst_wieloliniowy_1D[ 5*MAX_DL_L + 27 ]);

(4)

Tablice wielowymiarowe

Przesyłanie tablic wielowymiarowych jako argumentów funkcji realizuje prosta notacja:

funkcja_tab_wielo( nazwa_tab_wielo );

wczytaj_tekst ( tekst_wieloliniowy );

Każdorazowo wewnątrz funkcji musi być możliwe obliczenie adresu dowolnego elementu posługując się indeksami

elementu, jak np. w przypadku przykładowej tablicy dwuwymiarowej:

adres = początek + ( linia*MAX_DL_L + znak_w_linii ) * rozmiar_typu

oznacza to, że poza nazwą tablicy (czyli adresem jej początku)

muszą zostać przekazane dodatkowe parametry określające rozmiar tablicy, np. w przypadku przykładowej tablicy dwuwymiarowej

parametr MAX_DL_L

parametr ten musi znaleźć się w deklaracji funkcji, co oznacza, że musi być znany w trakcie kompilacji! (w klasycznym C)

(5)

Tablice wielowymiarowe

Deklaracje funkcji przyjmujących jako argument tablicę

wielowymiarową mogą mieć jedną z równoważnych postaci:

wczytaj_tekst ( char tekst_wieloliniowy [N_L] [MAX_DL_L] );

wczytaj_tekst ( char tekst_wieloliniowy [ ] [MAX_DL_L] );

wczytaj_tekst ( char (*tekst_wieloliniowy)[MAX_DL_L] );

ostatnia postać wykorzystuje utożsamienie nazwy tablicy ze wskaźnikiem

W przypadku alternatywnego przechowywania macierzy w postaci tablic jednowymiarowych:

parametry (wymiary) tablicy nie muszą być znane w czasie

kompilacji – muszą jednak zostać w jakiś sposób przekazane funkcji

deklaracja funkcji:

wczytaj_tekst_1D( char* tekst_wieloliniowy, int max_dl_l);

wywołanie:

wczytaj_tekst_1D( tekst_wieloliniowy, max_dl_l);

(6)

Tablice wskaźników

Tablice wielowymiarowe nie są elastycznym i często używanym elementem C

Znacznie częściej stosowane są bardziej elastyczne tablice wskaźników, np. dla poprzedniego przykładu:

#define MAX_N_L 15 // maksymalna liczba linii tekstu

char* tekst_wieloliniowy_tab_wsk[MAX_N_L] = {NULL}; // notacja!

wskaźniki są zainicjowane dla bezpiecznego użycia

ich wartości muszą zostać podstawione jako adresy istniejących zmiennych lub za pomocą funkcji alokacji pamięci, np.

char napis_tab[10] = "..."; tekst_wieloliniowy_tab_wsk[0] = napis_tab;

int dl_l = 25; int linia =5;

tekst_wieloliniowy_tab_wsk[linia] = malloc( dl_l*sizeof(char));

w powyższym przypadku tekst_wieloliniowy_tab_wsk jest symbolem oznaczającym tablicę (jej adres), natomiast składowe tej tablicy są zmiennymi, które mogą przyjmować różne wartości (adresów)

(7)

Tablice wskaźników

Wskaźniki będące składowymi tablicy wskaźników mogą pokazywać na rozmaite obiekty w pamięci

wskaźniki muszą zachować zgodność typu wskazywanych obiektów z deklaracją tablicy:

int* tab_wsk_int[10] = {NULL};

double* tab_wsk_d[10] = {NULL};

char* tekst_wieloliniowy_tab_wsk[MAX_N_L] = {NULL};

Dostęp do obiektów wskazywanych przez elementy tablicy wskaźników zależy od tego na co wskazują elementy:

elementy mogą pokazywać na pojedyncze zmienne, np.:

int liczba_int=5; int* tab_wsk_int[10] = {NULL};

tab_wsk_int[0] = &liczba_int;

printf("%d", *tab_wsk_int[0]); // lub printf("%d", tab_wsk_int[0][0]);

• taki sposób daje możliwość dostępu do pojedynczej zmiennej z wielu miejsc (funkcji, struktur) w programie

(8)

Tablice wskaźników

Dostęp do obiektów wskazywanych przez elementy tablicy wskaźników zależy od tego na co wskazują elementy:

elementy mogą pokazywać na tablice zmiennych:

double tab_d[10] = {1.0, 2.0, 0};

double* tab_wsk_d[10] = {NULL};

tab_wsk_d[0] = tab_d;

printf("%lf", tab_wsk_d[0][1]);

• taki sposób użycia daje elastyczność stosowania – wskazywane tablice mogą mieć różną długość (jak w poprzednim przykładzie)

elementy mogą pokazywać na struktury

typedef struct mg_punkt_2D{ double x; double y; } mg_punkt_2D;

mg_punkt_2D* koniec[2] = {NULL}; mg_punkt_2D p1 = {0.0,0.0};

koniec[0] = &p1; koniec[0]->x = 1.0;

printf("%lf\n", koniec[0]->x);

• tablice wskaźników do struktur mogą służyć do konstruowania złożonych struktur danych

(9)

Tablice wskaźników

Przesyłanie tablic wskaźników jako argumentów funkcji wykorzystuje notacje:

deklaracji funkcji (2 możliwe warianty):

wypisz_tekst( char* tekst_wieloliniowy_tab_wsk[] );

wypisz_tekst( char** tekst_wieloliniowy_tab_wsk );

• notacja char** zwraca uwagę na fakt, że na stosie funkcji

wypisz_tekst będzie znajdowała się zmienna będąca wskaźnikiem do wskaźnika – czyli wskaźnikiem do pierwszego elementu tablicy wskaźników)

wywołania funkcji

wypisz_tekst( tekst_wieloliniowy_tab_wsk );

wewnątrz funkcji musi istnieć sposób poprawnego realizowania dostępu do obiektów wskazywanych przez elementy tablicy

• dla tablic napisów można wykorzystać fakt istnienia '\0' na końcu napisu

(10)

Struktury, wskaźniki i lista powiązana

Lista powiązana (linked list) jest strukturą danych zawierającą elementy, tworzące węzły listy, zawierające dowolne dane

lista powiązana jest strukturą dynamiczną, liczba elementów listy może się zmieniać w trakcie wykonania programu

dostęp do elementów listy wykorzystuje zasadę, że każdy element listy wskazuje na element następny

najważniejszym elementem listy jest jej "głowa" – uchwyt (wskaźnik) do elementu początkowego

• "głowa" jest reprezentacją listy w funkcjach korzystających z listy

• chcąc uzyskać dostęp do dowolnego elementu listy należy uzyskać dostęp do jej "głowy", a następnie odwiedzać kolejne węzły

(korzystając ze wskaźników w celu przejścia od jednego węzła do kolejnego), aż do znalezienia szukanego elementu

• inicjowanie listy polega na nadaniu wartości NULL "głowie" listy

końcem listy jest węzeł, który nie wskazuje na żaden kolejny węzeł

(11)

Struktury, wskaźniki i lista powiązana

Lista powiązana (linked list)

struktury są wygodnym typem danych do przechowywania zawartości elementów listy:

typedef struct element_listy{

char* nazwa_wezla;

// dowolne inne składniki pojedynczego elementu listy

wsk_el_list nastepny_wezel; // wskaźnik do kolejnego elementu } el_list;

wskaźnik do następnego elementu może posługiwać się typem:

typedef struct element_listy* wsk_el_list;

pojedyncze elementy listy są obiektami typu el_list:

el_list element_1 = { "Węzeł 1", NULL };

operacje na elementach mogą posługiwać się wskaźnikami:

el_list* element_1_wsk = &element_1;

miejsce w pamięci dla elementów listy można alokować dynamicznie

(12)

Struktury, wskaźniki i lista powiązana

Lista powiązana (linked list)

zmiana liczby elementów listy polega na wstawianiu kolejnych elementów i ich usuwaniu

węzeł listy można wstawić w dowolne miejsce: na początek

(zmieniając wartość wskaźnika będącego "głową" listy), w środek (pomiędzy dwa węzły) lub na koniec

wstawienie i usunięcie węzła polega zawsze na modyfikacji powiązań pomiędzy węzłami, np.:

int wstaw_na_poczatek( // funkcja zwraca kod sukcesu lub błędu el_list** Glowa_wsk, // lista - identyfikowana przez wskaźnik el_list* Element // wskaźnik do elementu wstawianego na listę ){

// na początku: obsługa błędów danych wejściowych; następnie:

Element->nastepny_wezel=*Glowa_wsk;

*Glowa_wsk=Element; // modyfikacja Głowy używając *Głowa_wsk return(0); }

(13)

Struktury, wskaźniki i lista powiązana

Lista powiązana (linked list)

niektóre z operacji na liście mogą wymagać przeglądania kolejnych elementów listy:

int usun_element_listy( // funkcja zwraca kod sukcesu lub błędu el_list** Glowa_wsk, // lista - identyfikowana przez wskaźnik el_list* Element // wskaźnik do elementu usuwanego z listy ) {

// na początku: obsługa błędów danych wejściowych

if(*Glowa_wsk==Element) *Glowa_wsk=Element->nastepny_wezel;

else{

el_list* aktualny_wezel = *Glowa_wsk;

while(aktualny_wezel->nastepny_wezel != Element){

aktualny_wezel = aktualny_wezel->nastepny_wezel; } aktualny_wezel->nastepny_wezel = Element->nastepny_wezel;

} // funkcja może także zwalniać pamięć jeśli była zaalokowana

(14)

Przykład złożonej struktury danych

// definicje struktur w pakiecie mg – moja grafika – wersja 1 typedef struct mg_punkt_2D{ // punkt w przestrzeni 2D

double x; // współrzędna x double y; // współrzędna y } mg_punkt_2D;

typedef struct mg_krawedz_2D{ // krawędź w przestrzeni 2D mg_punkt_2D koniec[2]; // punkty będące wierzchołkami } mg_krawedz_2D;

typedef struct mg_trojkat_2D{ // trójkąt w przestrzeni 2D mg_krawedz_2D krawedzie[3]; // krawędzie trójkąta } mg_trojkat_2D;

Uwaga: nie ma punktów wspólnych dla krawędzi, krawędzi wspólnych dla trójkątów itp.

Uwaga: zmiana współrzędnych punktów nie zmienia położenia krawędzi

(15)

Przykład złożonej struktury danych

// definicje struktur w pakiecie mg – moja grafika – wersja 2 typedef struct mg_punkt_2D{ // punkt w przestrzeni 2D

double x; // współrzędna x double y; // współrzędna y } mg_punkt_2D;

typedef struct mg_krawedz_2D{ // krawędź w przestrzeni 2D mg_punkt_2D* koniec[2]; // punkty będące wierzchołkami } mg_krawedz_2D;

typedef struct mg_trojkat_2D{ // trójkąt w przestrzeni 2D mg_krawedz_2D* krawedzie[3]; // krawędzie trójkąta } mg_trojkat_2D;

Uwaga: mogą istnieć punkty wspólne dla krawędzi, krawędzie wspólne dla trójkątów itp.

(16)

Przykład złożonej struktury danych

// definicje struktur w pakiecie mg – moja grafika – wersja 2.1 typedef struct mg_punkt_2D{ // punkt w przestrzeni 2D double x; // współrzędna x

double y; // współrzędna y

struct mg_trojkat_2D* *trojkaty; // trójkąty zawierające punkt // tablica o długości nieznanej w trakcie kompilacji } mg_punkt_2D;

typedef struct mg_krawedz_2D{ // krawędź w przestrzeni 2D

struct mg_punkt_2D* koniec[2]; // punkty będące wierzchołkami struct mg_trojkat_2D* trojkaty[2]; // trójkąty zawierające krawędź } mg_krawedz_2D;

typedef struct mg_trojkat_2D{ // trójkąt w przestrzeni 2D struct mg_krawedz_2D* boki[3]; // krawędzie (boki) trójkąta struct mg_trojkat_2D* sasiedzi[3]; // sąsiednie trójkąty

(17)

Przykład złożonej struktury danych

Definicja struktury obejmującej całą siatkę trójkątów

typedef struct mg_siatka_2D{

int liczba_punktow;

mg_punkt_2D* tablica_punktow;

int liczba_krawedzi;

mg_krawedz_2D* tablica_krawedzi;

int liczba_trojkatow;

mg_trojkat_2D* tablica_trojkatow;

} mg_siatka_2D;

Parametry siatki (liczba punktów, krawędzi, trójkątów) zadawane są w trakcie wykonania programu

Tablice alokowane są w trakcie wykonania

Składowe struktur zawierają wskaźniki do odpowiednich

(18)

Przykład złożonej struktury danych

Zastosowanie tablic struktur pozwala na posługiwanie się indeksami w tablicach zamiast wskaźnikami do struktur:

// definicje struktur w pakiecie mg – moja grafika – wersja 2.2 typedef struct mg_punkt_2D{ // punkt w przestrzeni 2D

double x; // współrzędna x double y; // współrzędna y

int *trojkaty; // indeksy trójkątów zawierających punkt } mg_punkt_2D;

typedef struct mg_krawedz_2D{ // krawędź w przestrzeni 2D int koniec[2]; // indeksy punktów będących wierzchołkami int trojkaty[2]; // indeksy trójkątów zawierających krawędź } mg_krawedz_2D;

typedef struct mg_trojkat_2D{ // trójkąt w przestrzeni 2D int boki[3]; // indeksy krawędzi (boków) trójkąta

int sasiedzi[3]; // indeksy sąsiednich trójkątów } mg_trojkat_2D;

(19)

Przykład złożonej struktury danych

Zdefiniowane wcześniej struktury danych pozwalają na szybkie wykonywanie dużej liczby operacji na całej siatce (dodawanie nowych punktów, krawędzi, trójkątów)

Istnieją inne możliwe struktury danych wystarczające do realizacji operacji, np. minimalistyczna struktura danych:

// definicje struktur w pakiecie mg – moja grafika – wersja 3 typedef struct mg_punkt_2D{ // punkt w przestrzeni 2D

double x; double y; // współrzędne x i y } mg_punkt_2D;

typedef struct mg_trojkat_2D{ // trójkąt w przestrzeni 2D

int wierzcholki[3]; // indeksy wierzchołków trójkąta w tablicy punktów } mg_trojkat_2D;

typedef struct mg_siatka_2D{

int liczba_punktow; mg_punkt_2D* tablica_punktow;

int liczba_trojkatow; mg_trojkat_2D* tablica_trojkatow;

(20)

Przykład złożonej struktury danych

W definicji siatki zastosowane zostały tablice struktur (AOS – array of structures)

W celu zwiększenia wydajności obliczeń, stosuje się często

alternatywne warianty struktur danych zawierające pojedynczą strukturę z tablicami zmiennych elementarnych typów

liczbowych (SOA – structure of arrays)

W przypadku programu grafiki 2D struktura danych SOA mogłaby mieć postać:

typedef struct mg_siatka_2D{

int liczba_punktow; double* wspolrzedne_punktow;

int liczba_trojkatow; int* punkty_trojkatow;

} mg_siatka_2D;

Tak zdefiniowana struktura eliminuje potrzebę definiowania jakichkolwiek innych struktur

ceną jest zmniejszenie czytelności kodu operacji na siatce trójkątów

Cytaty

Powiązane dokumenty

Wszystkie urządzenia nowej seria SLIM-DUAL-PET zostały wyposażone w zaawansowany algorytm detekcji ruchu nowej generacji.. Zapewniają wykrywanie intruzów w obrębie

Przywiązanie klienta do konkretnego portu Port, na którym klient nasłuchuje. Przepięcie klienta na nowy port, w celu zwolnienia portu

• jeśli liczba wartości jest mniejsza od rozmiaru, uzupełnia się przez 0. ➔ Operacji na napisach

double oblicz_pierwiastek ( double arg ); // deklaracja, prototyp funkcji // zwyczajowo, mniej ściśle, także: nagłówek funkcji void main( void ) {. double liczba

 dynamiczne alokowanie pamięci najczęściej stosowane jest dla tablic, których rozmiar znany jest dopiero w trakcie wykonania programu. int* tab_int = NULL; // tab_int[0]=1; -

➔ Zmienne mogą posiadać określenie const informujące, że ich wartości nie mogą być zmieniane w trakcie wykonania.  zmienne stałe muszą zostać

Marek Cała – Katedra Geomechaniki, Budownictwa i Geotechniki Ściśliwość gruntu opisuje się zależnością porowatości od naprężenia..

Zasadniczym elementem programu są funkcje pole1, pole2 i pole3 - wszystkie funkcje mają jedno podstawowe zadanie - liczą powierzchnię prostokąta, którego jeden bok ma