This shows you the differences between two versions of the page.
Both sides previous revision Previous revision | |||
dydaktyka:cprog:2016:arrays-adv [2017/01/02 08:17] pkleczek |
— (current) | ||
---|---|---|---|
Line 1: | Line 1: | ||
- | ~~NOTRANS~~ | ||
- | |||
- | ====== Tablice (zaawansowane) ====== | ||
- | |||
- | ===== Tablice wielowymiarowe ===== | ||
- | |||
- | ==== Deklarowanie ==== | ||
- | |||
- | Uogólniona deklaracja tablicy wielowymiarowej: | ||
- | <code> | ||
- | typ_danych nazwa_tablicy[rozmiar_1][rozmiar_2]...[rozmiar_n]; | ||
- | </code> | ||
- | |||
- | na przykładzie dwuwymiarowym: | ||
- | <code> | ||
- | typ_danych nazwa_tablicy[ilosc_wierszy][ilosc_kolumn]; | ||
- | </code> | ||
- | |||
- | Przykład: | ||
- | <code c> | ||
- | int tab[8][3]; // tablica o osmiu wierszach i trzech kolumnach, wartosci typu calkowitego | ||
- | </code> | ||
- | |||
- | Komputer przechowuje w pamięci taką tablicę dwuwymiarową liniowo, jako kilka następujących po sobie tablic jednowymiarowych. | ||
- | |||
- | ==== Definiowanie ==== | ||
- | |||
- | Tablicę wielowymiarową można zdefiniować na dwa sposoby: | ||
- | * używając "zagnieżdżonych" klamer (pierwszy zestaw -> piewszy rząd, drugi zestaw -> drugi rząd itd.) | ||
- | * stosując "płaską" definicją -- wszystkie wartości znajdują się w jednych klamrach, przypisywane są do kolejnych elementów wierszami (najpierw całkowicie zapełniany jest pierwszy wiersz, potem drugi itd.) | ||
- | |||
- | Przykład sposobu z "zagnieżdżaniem" klamer: | ||
- | <code c> | ||
- | int tab[2][3] = { | ||
- | {3, 2, 6}, | ||
- | {5, 1, 9} | ||
- | }; | ||
- | </code> | ||
- | |||
- | Przykład sposobu "płaskiego": | ||
- | <code c> | ||
- | int tab[2][3] = {3, 2, 6, 5, 1, 9}; // zdecydowanie mniej czytelne, | ||
- | // w miare mozliwosci unikac! | ||
- | </code> | ||
- | |||
- | |||
- | ==== Dostęp do elementów ==== | ||
- | |||
- | Podobnie jak w przypadku tablicy jednowymiarowej, dostęp uzyskuje się przez podanie w nawiasach kwadratowych numerów pozycji dla kolejnych wymiarów. \\ | ||
- | Dla przypadku dwuwymiarowego, podaje się najpierw numer wiersza, a potem numer kolumny. | ||
- | |||
- | Przykład: | ||
- | <code c> | ||
- | int tab[3][2]; | ||
- | tab[2][0] = 5; // przypisz wartosc elementowi w 3. wierszu w 1. kolumnie | ||
- | </code> | ||
- | |||
- | Poniższy rysunek pomoże Ci zrozumieć tę zasadę:\\ | ||
- | {{:dydaktyka:cprog:2015:2d-array_indexing.png?nolink|}} | ||
- | |||
- | ===== Jak "elastycznie" określać rozmiar tablicy? – Derektywa #define ===== | ||
- | |||
- | Jak wspomniano w poprzednim laboratorium o tablicach, standard ANSI C __nie pozwala__ na używanie zmiennych bądź stałych do określania rozmiaru tablicy podczas deklaracji: | ||
- | <code c> | ||
- | int n = 3; | ||
- | int tab[n]; // blad | ||
- | </code> | ||
- | <code c> | ||
- | const int n = 3; | ||
- | int tab[n]; // blad | ||
- | </code> | ||
- | |||
- | :!: Oznacza to więc, że **nie ma możliwości utworzenia (__statycznie__) tablicy o rozmiarze zadanym przez użytkownika**! -- Nie pobieraj od użytkownika rozmiaru tablicy.\\ | ||
- | Można to zrobić korzystając z dynamicznej alokacji pamięci, ale to temat na jedno z ostatnich laboratoriów... | ||
- | |||
- | Z pomocą przychodzą //stałe symboliczne//. | ||
- | |||
- | Stałą symboliczną tworzy się z użyciem //derektywy preprocesora// ''#define'', na przykład: | ||
- | <code c> | ||
- | #define N 3 | ||
- | </code> | ||
- | |||
- | Preprocesor to program uruchamiany przed właściwą kompilacją (stąd jego nazwa: __pre__procesor), a zajmuje się on m.in. dołączaniem plików nagłówkowych (poprzez znaną Ci derektywę ''#include''). Derektywa ''#define'' mówi "zamień w __tekście__ programu ciąg znaków po lewej stronie na ciąg znaków po prawej stronie". W tym przypadku -- ciąg znaków ''N'' na ciąg znaków ''3''. \\ | ||
- | Zatem po tym, jak preprocesor skończy swoją robotę, kod | ||
- | <code c> | ||
- | #define N 3 | ||
- | ... | ||
- | int tab[N]; | ||
- | </code> | ||
- | wygląda następująco (to widzi kompilator): | ||
- | <code c> | ||
- | int tab[3]; | ||
- | </code> | ||
- | |||
- | Preprocesor działa więc momentami jak cenzor, którzy przed dostarczeniem listu do adresata zmienia to i owo -- coś wykreśla, coś dodaje... ;-) | ||
- | |||
- | Derektywy ''#define'' umieszcza się na początku kodu programu, zaraz pod derektywami ''#include''. | ||
- | |||
- | Można tworzyć wiele stałych symbolicznych -- każdą w osobnej linii programu: | ||
- | <code c> | ||
- | #define R 3 | ||
- | #define C 3 | ||
- | </code> | ||
- | |||
- | Taki mechanizm jest przydatny właśnie podczas tworzenia wielowymiarowych tablic, szczególnie w przypadku gdy jeden z wymiarów ma być określony automatycznie. | ||
- | |||
- | :!: Stosuj stałe symboliczne (bądź ilość elementów obliczoną z użyciem ''sizeof'') wszędzie tam, gdzie normalnie powinien występować rozmiar tablicy (tj. dany jej wymiar) -- w deklaracjach, w pętlach ''for'' itd. | ||
- | |||
- | ==== Zadania ==== | ||
- | |||
- | === Zadanie SUM2D-1 === | ||
- | |||
- | Zadeklaruj tablicę dwuwymiarową $A_{N \times M}$. Przypisz do każdego z jej elementów $a_{ij}$ wartość $i + 2 \cdot j$, gdzie $i$ -- numer wiersza, a $j$ -- numer kolumny. Oblicz sumę elementów leżących w co drugim wierszu, zaczynając od pierwszego wiersza. | ||
- | |||
- | //Wskazówka 1: Użyj zagnieżdżonych pętli ''for'' zarówno do inicjalizacji elementów tablicy, jak i do obliczenia sumy elementów.// \\ | ||
- | //Wskazówka 2: Zastosuj osobny zestaw pętli dla inicjalizacji i osobny dla sumowania.// | ||
- | |||
- | === Zadanie SUM2D-2 === | ||
- | |||
- | Zadeklaruj tablicę dwuwymiarową $A_{N \times M}$. Przypisz do każdego z jej elementów losową wartość z przedziału $\langle 0, 9 \rangle$. Wyświetl tablicę zawierającą wylosowane elementy. Oblicz i wyświetl sumę elementów w każdym rzędzie oraz w każdej kolumnie tej tablicy. | ||
- | |||
- | ===== Tablice bezwymiarowe ===== | ||
- | |||
- | Różnica w tworzeniu tablic "bezwymiarowych" w przypadku tablicy o wymiarze większym niż 1 jest następująca: | ||
- | |||
- | :!: Kompilator jest w stanie obliczyć tylko jeden wymiar tablicy -- pierwszy od lewej. Pozostałe wymiary muszą zostać określone przez programistę: | ||
- | <code c> | ||
- | int tab[][2] = {{1,2}, {3,4}, {5,6}}; // OK | ||
- | int tab[][] = {{1,2}, {3,4}}; // blad | ||
- | </code> | ||
- | |||
- | ===== Łańcuchy znaków ===== | ||
- | |||
- | Język C nie posiada specjalnego typu łańcuchowego. Zamiast tego, łańcuchy przechowywane są w tablicach elementów typu ''char''. Kolejne znaki, z których składa się łańcuch, znajdują się w kolejnych komórkach pamięci, a na ich końcu znajduje się **znak zerowy** (//null character//). | ||
- | |||
- | Znak zerowy nie jest cyfrą zero, tylko znakiem niedrukowanym o kodzie ASCII równym 0. Łańcuchy w języku C są przechowywane razem z tym znakiem. Oznacza to, że tablica musi mieć długość przynajmniej o jedną komórkę większą niż długość zapisanego w niej łańcucha. \\ | ||
- | Literał znaku zerowego to ''%%'\0'%%''. | ||
- | |||
- | Przykład -- łańcuch znaków ''%%"Hello"%%'': \\ | ||
- | {{:dydaktyka:cprog:2015:string.png?nolink|}} | ||
- | |||
- | :!: Znaki cudzysłowu nie są częścią łańcucha. Informują one jedynie kompilator, że to, co znajduje się pomiędzy nimi, jest łańcuchem -- podobnie jak apostrofy sygnalizują stałą znakową. | ||
- | |||
- | |||
- | ==== Wyświetlanie i wczytywanie ==== | ||
- | |||
- | Do wyświetlania łańcuchów znaków służy specyfikator konwersji ''%s'': | ||
- | <code c> | ||
- | printf("Miasto: %s\n", "Krakow"); | ||
- | </code> | ||
- | |||
- | Do wczytywania łańcuchów znaków najwygodniej wykorzystać funkcję [[http://www.cplusplus.com/reference/cstdio/gets/?kw=gets|gets()]], która odczytuje znaki do momentu napotkania znaku końca linii (ENTER) i zapisuje je jako łańcuch w tablicy znaków -- sam znak końca linii nie jest kopiowany. Funkcja automatycznie dołącza do wczytanych znaków znak zerowy, tworząc tym samym łańcuch znaków. | ||
- | <code c> | ||
- | char imie[15]; | ||
- | printf("Podaj swoje imie: "); | ||
- | gets(imie); | ||
- | </code> | ||
- | |||
- | Oczywiście możesz też użyć funkcji ''scanf()'' ze specyfikatorem konwersji ''%s'': | ||
- | <code c> | ||
- | char imie[15]; | ||
- | printf("Podaj swoje imie: "); | ||
- | scanf("%s", imie); // `imie` to tablica, a nazwa tablicy to rownoczesnie ADRES jej | ||
- | // pierwszego elementu - nie trzeba zatem dodawac '&' przed nazwa tablicy | ||
- | </code> | ||
- | |||
- | :!: Pamiętaj, żeby wczytywać znaki do wystarczająco dużej tablicy (aby zmieściły się wszystkie znaki i jeszcze znak zerowy)! | ||
- | |||
- | |||
- | ==== Przydatne funkcje ==== | ||
- | |||
- | Do badania długości łańcucha znaków -- czyli ilości znaków faktycznie tworzących łańcuch, z pominięciem znaku zerowego -- służy funkcja [[http://www.cplusplus.com/reference/cstring/strlen/?kw=strlen|strlen()]]. \\ | ||
- | Zwróć uwagę, że ''sizeof'' i ''strlen'' zwracają różne wartości dla tablicy przechowującej łańcuch znaków, gdyż łańcuch może nie zajmować całej tablicy (uruchom poniższy program): | ||
- | <code c> | ||
- | #include <stdio.h> | ||
- | #include <stdlib.h> | ||
- | #include <string.h> | ||
- | |||
- | int main () | ||
- | { | ||
- | char str[10] = "ABC"; // lancuch zajmuje 4 elementy tablicy | ||
- | |||
- | printf("sizeof = %d , strlen = %d\n", sizeof(str), strlen(str)); | ||
- | |||
- | return 0; | ||
- | } | ||
- | </code> | ||
- | |||
- | Aby określić typ znaku (np. czy dany znak jest cyfrą) warto skorzystać z biblioteki [[http://www.cplusplus.com/reference/cctype/|ctype.h]]. | ||
- | |||
- | ==== Zadania ==== | ||
- | |||
- | === Zadanie IDL === | ||
- | |||
- | Napisz program, który pobiera imię i nazwisko użytkownika (używając jednego wywołania funkcji ''scanf()''), a następnie wyświetla: w pierwszym wierszu imię i nazwisko, a w drugim liczbę liter w imieniu oraz liczbę liter w nazwisku. | ||
- | |||
- | Na przykład: | ||
- | <code c> | ||
- | Podaj imie i nazwisko: Jan Nowak | ||
- | |||
- | Jan Nowak | ||
- | 3 5 | ||
- | </code> | ||
- | |||
- | //Wskazówka: Przechowuj imię i nazwisko w osobnych tablicach.// | ||
- | |||
- | === Zadanie PAL === | ||
- | |||
- | Napisz program, który sprawdzi czy dany łańcuch znaków jest [[http://sjp.pwn.pl/sjp/palindrom;2497635.html|palindromem]]. | ||
- | |||
- | Na przykład ''kajak'' jest palindromem, natomiast ''rower'' już nie ;-) | ||