User Tools

Site Tools


dydaktyka:cprog:2016:arrays

Differences

This shows you the differences between two versions of the page.

Link to this comparison view

Next revision
Previous revision
dydaktyka:cprog:2016:arrays [2016/01/30 08:19]
127.0.0.1 edycja zewnętrzna
— (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>​ 
- 
-==== 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 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!). 
-===== 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. 
-===== 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 załapać tę zasadę:\\ 
-{{:​dydaktyka:​cprog:​2015:​2d-array_indexing.png?​nolink|}} 
- 
-==== Zadania ==== 
- 
-=== Zadanie SUM2D === 
- 
-Zadeklaruj tablicę dwuwymiarową $A_{N \times M}$. Przypisz do każdego z jej elementów $a(i,j)$ 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.//​ 
-===== Tablice bezwymiarowe ===== 
- 
-Kompilator C może automatycznie obliczyć rozmiar tablicy, jeśli tablica zostanie zadeklarowana jako bezwymiarowa (czyli programista nie poda ilości elementów tablicy). 
- 
-Jeśli kompilator napotka, przykładowo,​ zapis 
-<code c>int tab[] = {1,2,3}; 
-</​code>​ 
-to utworzy tablicę na tyle dużą, by pomieścić wszystkie te elementy. 
- 
-:!: Kompilator jest w stanie obliczyć tylko jeden wymiar tablicy -- pierwszy od lewej. Pozostałe wymiary muszą zostać podane: 
-<code c> 
-int tab[][2] = {{1,2}, {3,4}, {5,​6}}; ​ // OK 
-int tab[][] = {{1,2}, {3,​4}}; ​ // blad 
-</​code>​ 
- 
-===== 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>​ 
-===== Ł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.//​ 
- 
-===== 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 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!//​ 
dydaktyka/cprog/2016/arrays.1454138381.txt.gz · Last modified: 2020/03/25 11:46 (external edit)