This is an old revision of the document!
Tablica to ciąg wartości tego samego typu (np. liczb) przechowywanych obok siebie.
Uogólniona deklaracja tablicy:
typ_danych nazwa_tablicy[liczba_elementow];
przy czym dostępne typy elementów są identyczne, jak dla “zwykłych” zmiennych (int
, float
itd.)
Przykład:
int tab[8]; // tablica osmiu wartosci typu calkowitego
Definicja tablicy polega na przypisaniu do tablicy podanej w nawiasach klamrowych listy wartości kolejnych elementów (rozdzielonych przecinkami).
Przykład:
int tab[4] = {3, 2, 5, 1};
W przypadku podania zbyt małej liczby wartości do inicjalizacji, pozostałe elementy tablicy otrzymują wartość 0.
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:
int tab[] = {1, 2, 3, 4}; int elem = tab[2]; // przypisz zmiennej `elem` wartosc TRZECIEGO elementu tablicy `tab`
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!).
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
.
Napisz program, który wyznaczy największą wartość w jednowymiarowej tablicy liczb całkowitych.
Standard ANSI C nie pozwala na używanie zmiennych bądź stałych do określania rozmiaru tablicy podczas deklaracji:
int n = 3; int tab[n]; // blad
const int n = 3; int tab[n]; // blad
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:
#define N 3
Preprocesor to program uruchamiany przed właściwą kompilacją (stąd jego nazwa: preprocesor), 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
#define N 3 ... int tab[N];
wygląda następująco (to widzi kompilator):
int tab[3];
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:
#define R 3 #define C 3
(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.
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:
#include <stdio.h> int main() { int tab[3] = {1, 2, 3}; printf("%d\n", tab[-1]); printf("%d\n", tab[3]); return 0; }
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
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:
int s = sizeof(char); // wartosc `s` to 1, bo tyle wynosi rozmiar typu 'char' int s2 = sizeof(s); // wartosc `s2` to (prawdopodobnie) 4
Operator ten można wykorzystać, aby otrzymać ilość elementów tablicy:
int tab[5]; int ilosc_elementow = sizeof(tab) / sizeof(int);
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:
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
Operatory inkrementacji (++
) i dekrementacji (--
) powodują odpowiednio zwiększenie bądź zmniejszenie wartości zmiennej o 1.
Są one dostępne w dwóch trybach:
++i
i++
Przykład:
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);
Zwróć uwagę na to, że np.
i+1
to nie to samo co i++
:
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
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.
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:
Na przykład po wykonaniu kodu
int n = 3; int y = n++ + n++;
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.
(poziom trudności: )
Napisz program, który uzupełni tablicę typu int
(o rozmiarze 10) losowymi wartościami z przedziału $\langle 0, 99 \rangle$.
(poziom trudności: )
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!