This shows you the differences between two versions of the page.
Both sides previous revision Previous revision Next revision | Previous revision | ||
dydaktyka:cprog:2016:arrays [2016/12/06 08:26] pkleczek |
— (current) | ||
---|---|---|---|
Line 1: | Line 1: | ||
- | ====== Tablice ====== | ||
- | |||
- | Tablica to ciąg wartości tego samego typu (np. liczb) przechowywanych obok siebie. | ||
- | |||
- | ===== Tablice jednowymiarowe ===== | ||
- | |||
- | |||
- | ==== Deklarowanie ==== | ||
- | |||
- | Uogólniona deklaracja tablicy: | ||
- | <code> | ||
- | typ_danych nazwa_tablicy[liczba_elementow]; | ||
- | </code> | ||
- | przy czym dostępne typy elementów są identyczne, jak dla "zwykłych" zmiennych (''int'', ''float'' itd.) | ||
- | |||
- | Przykład: | ||
- | <code c> | ||
- | int tab[8]; // tablica osmiu wartosci typu calkowitego | ||
- | </code> | ||
- | |||
- | ==== Definiowanie ==== | ||
- | |||
- | Definicja tablicy polega na przypisaniu do tablicy podanej w nawiasach klamrowych listy wartości kolejnych elementów (rozdzielonych przecinkami). | ||
- | |||
- | Przykład: | ||
- | <code c> | ||
- | int tab[4] = {3, 2, 5, 1}; | ||
- | </code> | ||
- | |||
- | W przypadku podania zbyt małej liczby wartości do inicjalizacji, pozostałe elementy tablicy otrzymują wartość 0. | ||
- | |||
- | ==== Dostęp do elementów ==== | ||
- | |||
- | Dostęp do poszczególnych elementów tablicy można uzyskać za pomocą indeksu elementu, będącego zawsze liczbą naturalną, umieszczonego w nawiasach kwadratowych. | ||
- | |||
- | :!: W języku C indeksowanie zaczyna się od zera -- czyli aby uzyskać wartość pierwszego elementu piszemy ''tab[0]''! | ||
- | |||
- | Przykład: | ||
- | <code c> | ||
- | int tab[] = {1, 2, 3, 4}; | ||
- | int elem = tab[2]; // przypisz zmiennej `elem` wartosc TRZECIEGO elementu tablicy `tab` | ||
- | </code> | ||
- | |||
- | ===== Jak wyświetlać tablicę? ===== | ||
- | |||
- | Cóż... Niestety, język C nie pozwala na wyświetlanie tablicy jedną instrukcję. \\ | ||
- | Tablicę trzeba wyświetlać element po elemencie. | ||
- | |||
- | Podobnie w przypadku, gdy to użytkownik ma podawać wartości elementów tablicy, trzeba to zrobić element po elemencie (najlepiej -- w pętli!). | ||
- | |||
- | ==== Zadania ==== | ||
- | |||
- | === Zadanie SUM1D === | ||
- | |||
- | Stwórz jednowymiarową tablicę wartości zmiennoprzecinkowych zainicjalizowaną wybranymi przez siebie wartościami, a następnie oblicz sumę jej elementów. | ||
- | |||
- | //Wskazówka: Skorzystaj z pętli ''for''.// | ||
- | |||
- | === Zadanie MAX1D === | ||
- | |||
- | Napisz program, który wyznaczy największą wartość w jednowymiarowej tablicy liczb całkowitych. | ||
- | |||
- | |||
- | ===== Jak "elastycznie" określać rozmiar tablicy? ===== | ||
- | |||
- | 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> | ||
- | (to będzie przydatne przy tworzeniu tablic wielowymiarowych) | ||
- | |||
- | :!: Stosuj stałe symboliczne wszędzie tam, gdzie normalnie powinien występować rozmiar tablicy (tj. dany jej wymiar) -- w deklaracjach, w pętlach ''for'' itd. | ||
- | |||
- | ===== Dlaczego nie należy wychodzić poza zakres tablicy? ===== | ||
- | |||
- | W poniższym przykładzie mimo, że tablica składa się z trzech elementów (a więc poprawne indeksy to 0, 1 i 2), możemy odwołać się do elementów spoza zakresu -- ani kompilator nie zgłosi błędów, ani (w tym przypadku) nie wystąpi błąd podczas wykonania programu: | ||
- | |||
- | <code c> | ||
- | #include <stdio.h> | ||
- | |||
- | int main() | ||
- | { | ||
- | int tab[3] = {1, 2, 3}; | ||
- | |||
- | printf("%d\n", tab[-1]); | ||
- | printf("%d\n", tab[3]); | ||
- | |||
- | return 0; | ||
- | } | ||
- | </code> | ||
- | |||
- | Tyle, że... wartości ''tab[-1]'' i ''tab[3]'' zawierają "śmieci". W dodatku ta pamięć nie należy już do zmiennej ''tab''. \\ | ||
- | W ogólnym przypadku odwoływanie się do nie-swojej pamięci może spowodować błąd działania programu. | ||
- | |||
- | ===== Operator "sizeof" ===== | ||
- | |||
- | Operator ''sizeof'' zwraca rozmiar operandu (w bajtach). Używa się go podobnie do wywołania funkcji, przy czym argumentem może być nazwa zmiennej lub typ danych: | ||
- | <code c> | ||
- | int s = sizeof(char); // wartosc `s` to 1, bo tyle wynosi rozmiar typu 'char' | ||
- | int s2 = sizeof(s); // wartosc `s2` to (prawdopodobnie) 4 | ||
- | </code> | ||
- | |||
- | Operator ten można wykorzystać, aby otrzymać ilość elementów tablicy: | ||
- | <code c> | ||
- | int tab[5]; | ||
- | int ilosc_elementow = sizeof(tab) / sizeof(int); | ||
- | </code> | ||
- | |||
- | ===== Przydatne operatory arytmetyczne ===== | ||
- | |||
- | ==== Arytmetyczne operatory przypisania ==== | ||
- | |||
- | Język C udostępnia //arytmetyczne operatory przypisania// ''<op>='' (gdzie ''<op>'' oznacza operator arytmetyczny: ''+'', ''-'', ''*'', ''/'' bądź ''%''): instrukcja ''x <op>= y;'' jest równoważna ''x = x <op> (y);'' | ||
- | |||
- | Przykład: | ||
- | <code c> | ||
- | int a = 5; | ||
- | int b = 2; | ||
- | a *= 3; // nowa wartosc `a` to 5 * 3 = 15 | ||
- | b *= 3 + 1; // nowa wartosc `b` to 2 * (3 + 1) = 8 | ||
- | </code> | ||
- | |||
- | ==== Inkrementacja i dekrementacja ==== | ||
- | |||
- | Operatory inkrementacji (''++'') i dekrementacji (''%%--%%'') powodują odpowiednio zwiększenie bądź zmniejszenie wartości zmiennej o 1. | ||
- | |||
- | Są one dostępne w dwóch trybach: | ||
- | * przedrostkowym (//pre-incrementation//) -- najpierw następuje zwiększenie wartości zmiennej, potem dalsze operacje na zwiększonej wartości; np. ''++i'' | ||
- | * przyrostkowym (//post-incrementation//) -- najpierw wykonywane są operacje (na pierwotnej wartości), a dopiero potem następuje zwiększenie wartości zmiennej; np. ''i++'' | ||
- | |||
- | Przykład: | ||
- | <code c> | ||
- | int a1 = 3; | ||
- | int a2 = 3; | ||
- | int b_pre = 2 * ++a1; //po wykonaniu instrukcji: b_pre = 6, a1 = 3 | ||
- | int b_post = 2 * a2++; //po wykonaniu instrukcji: b_post = 4, a2 = 3 | ||
- | |||
- | printf("a = %d, b_pre = %d, b_post = %d\n", b_pre, b_post); | ||
- | </code> | ||
- | |||
- | :!: Zwróć uwagę na to, że np. ''i+1'' to nie to samo co ''i++'': | ||
- | <code c> | ||
- | int tab[3] = {1, 2, 3}; | ||
- | int i = 0; | ||
- | printf("%d\n", tab[i++]); // wypisz i-ty element ORAZ (nastepnie) zwieksz `i` o 1 | ||
- | // teraz `i` wynosi 1 | ||
- | printf("%d\n", tab[i+1]); // wypisz (i+1)-wszy element (ale nie zmnieniaj wartosci `i`) | ||
- | // `i` nadal wynosi 1 | ||
- | </code> | ||
- | |||
- | Operatory inkrementacji i dekrementacji działają tylko dla zmiennych (a ściślej: dla modyfikowalnych l-wartości). Oznacza to, że instrukcja ''int a = 6++;'' jest błędna. | ||
- | |||
- | Stosuj powyższe operatory ostrożnie -- zwiększają one zwięzłość kodu, lecz łatwo popełnić błąd w zapisie. | ||
- | |||
- | ==== "Co za dużo to niezdrowo!" ==== | ||
- | |||
- | Nie łącz zbyt wielu operatorów w jednym wyrażeniu, nie rób "optymalizacji" długości kodu kosztem czytelności -- w ten sposób unikniesz wielu błędów. W szczególności: | ||
- | * Nie stosuj operatora inkrementacji lub dekrementacji do zmiennej, która jest częścią więcej niż jednego argumentu funkcji. | ||
- | * Nie stosuj operatora inkrementacji lub dekrementacji do zmiennej, która w wyrażeniu pojawia się więcej niż jeden raz. | ||
- | |||
- | Na przykład po wykonaniu kodu | ||
- | <code c> | ||
- | int n = 3; | ||
- | int y = n++ + n++; | ||
- | </code> | ||
- | ''y'' może mieć albo wartość ''6'' -- jeśli kompilator dwukrotnie użyje starej wartości -- albo wartość ''7'' (''3 + 4''), jeśli zwiększenie nastąpi przed obliczeniem sumy. Standard języka C nie narzuca jednego słusznego sposobu obliczania takiego wyrażenia. | ||
- | |||
- | ===== Zadania podsumowujące ===== | ||
- | |||
- | /* | ||
- | ==== Fotoszop ==== | ||
- | |||
- | ([size=10]poziom trudności:[/size] {{stars>2/4}} / {{stars>3/4}}) | ||
- | |||
- | Wykonaj zadania w ramach polecenia [[dydaktyka:cprog:2015:photoshop#cwiczenia|Rozmycie obrazu]]. | ||
- | |||
- | */ | ||
- | |||
- | ==== Zadanie RAND-ARR ==== | ||
- | |||
- | ([size=10]poziom trudności:[/size] {{stars>1/4}}) | ||
- | |||
- | Napisz program, który uzupełni tablicę typu ''int'' (o rozmiarze 10) losowymi wartościami z przedziału $\langle 0, 99 \rangle$. | ||
- | |||
- | ==== Zadanie REV ==== | ||
- | |||
- | ([size=10]poziom trudności:[/size] {{stars>2/4}}) | ||
- | |||
- | Napisz program, który odwróci kolejność elementów tablicy "w miejscu", czyli bez deklarowania __tablicy__ pomocniczej. \\ | ||
- | Przykład: ''{1, 4, 3}'' → ''{3, 4, 1}''. | ||
- | |||
- | //Wskazówka: Stosowanie "zwykłej" zmiennej pomocniczej jest dozwolone!// | ||