This is an old revision of the document!
~~NOTRANS~~
Uogólniona deklaracja tablicy wielowymiarowej:
typ_danych nazwa_tablicy[rozmiar_1][rozmiar_2]...[rozmiar_n];
na przykładzie dwuwymiarowym:
typ_danych nazwa_tablicy[ilosc_wierszy][ilosc_kolumn];
Przykład:
int tab[8][3]; // tablica o osmiu wierszach i trzech kolumnach, wartosci typu calkowitego
Komputer przechowuje w pamięci taką tablicę dwuwymiarową liniowo, jako kilka następujących po sobie tablic jednowymiarowych.
Tablicę wielowymiarową można zdefiniować na dwa sposoby:
Przykład sposobu z “zagnieżdżaniem” klamer:
int tab[2][3] = { {3, 2, 6}, {5, 1, 9} };
Przykład sposobu “płaskiego”:
int tab[2][3] = {3, 2, 6, 5, 1, 9}; // zdecydowanie mniej czytelne, // w miare mozliwosci unikac!
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:
int tab[3][2]; tab[2][0] = 5; // przypisz wartosc elementowi w 3. wierszu w 1. kolumnie
Poniższy rysunek pomoże Ci załapać tę zasadę:
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.
Zadeklaruj tablicę dwuwymiarową $A_{N \times M}$. Przypisz do każdego z jej elementów losową wartość z przedziału $\langle 0, \rangle 9$. 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.
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ę:
int tab[][2] = {{1,2}, {3,4}, {5,6}}; // OK int tab[][] = {{1,2}, {3,4}}; // blad
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:
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
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.
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"
:
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ą.
Do wyświetlania łańcuchów znaków służy specyfikator konwersji %s
:
printf("Miasto: %s\n", "Krakow");
Do wczytywania łańcuchów znaków najwygodniej wykorzystać funkcję 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.
char imie[15]; printf("Podaj swoje imie: "); gets(imie);
Oczywiście możesz też użyć funkcji scanf()
ze specyfikatorem konwersji %s
:
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
Pamiętaj, żeby wczytywać znaki do wystarczająco dużej tablicy (aby zmieściły się wszystkie znaki i jeszcze znak zerowy)!
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 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):
#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; }
Aby określić typ znaku (np. czy dany znak jest cyfrą) warto skorzystać z biblioteki ctype.h.
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:
Podaj imie i nazwisko: Jan Nowak Jan Nowak 3 5
Wskazówka: Przechowuj imię i nazwisko w osobnych tablicach.
Napisz program, który sprawdzi czy dany łańcuch znaków jest palindromem.
Na przykład kajak
jest palindromem, natomiast rower
już nie