Wybrane podstawowe zagadnienia ze strukturalnego programowania w języku C (i C++), pomocne w wykonywaniu ćwiczeń z Podstaw Programowania.
Spis treści:
- Budowa programu.
- Wybrane biblioteki i ich funkcje.
- Funkcja główna „main”.
- Typy danych.
- Dane globalne a lokalne.
- Pobieranie danych z klawiatury.
- Wyświetlanie danych na ekranie.
- Instrukcja warunku „if”.
- Instrukcja wyboru „switch”.
- Pętla „for”.
- Pętla „while”.
- Pętla „do” .. „while„.
- Menu programu.
- Tablica statyczna.
- Zapis do tablicy.
- Odczyt z tablicy.
- Łańcuch znaków.
- Przestawianie elementów tablicy.
- Wskaźnik.
- Dostęp do zmiennej przez wskaźnik.
- Dostęp do tablicy przez wskaźnik.
- Funkcja bezargumentowa.
- Funkcja z argumentami przekazywanymi przez wartość.
- Funkcja z argumentami przekazywanymi przez referencję.
- Funkcja z argumentami przekazywanymi przez adres.
- Tablica dynamiczna jednowymiarowa.
- Tablica dynamiczna dwuwymiarowa.
Budowa programu
Kolejne części typowego programu:
- biblioteki – definicje funkcji użytych w programie,
- dodatkowe polecenia,
- dane globalne – zmienne i stałe dostępne w całym programie,
- funkcje – definicje funkcji napisanych i użytych w programie,
- funkcja główna
main
.
Alternatywna kolejność:
- biblioteki,
- dodatkowe polecenia,
- dane globalne,
- nagłówki funkcji – „pierwsze linie” definicji funkcji napisanych i użytych w programie,
- funkcja główna
main
. - funkcje – definicje funkcji napisanych i użytych w programie.
Komentarz do kodu
Komentarz to fragment programu, który nie jest kodem – jest pomijany przez kompilatora.
Między znakami /*
i */
umieszcza się komentarz, który może zajmować dowolną liczbę linii programu.
Po znakach //
umieszcza się komentarz, który kończy się z końcem tej samej linii programu.
Biblioteka
Biblioteka to zbiór funkcji, z których korzysta się w programie, ale nie napisanych w nim.
Każdą potrzebną bibliotekę dołącza się poleceniem #include
, np.:
#include<iostream> // dołączenie biblioteki iostream
Dodatkowe polecenia
Polecenie using namespace sdt
ustawia klawiaturę jako domyślne źródło pobierania danych, a ekran jako domyślne źródło zwracania danych, jeśli używa się operatorów cin
i cout
z biblioteki iostream
:
using namespace std;
Dana globalna
Dana (zmienna lub stała) globalna dostępna jest w całym programie (z pewnymi wyjątkami).
Funkcja
Definicja funkcji składa się z nagłówka i ciała zawartego w klamrach.
Kolejne części nagłówka funkcji:
- typ zwracanej wartości (np.
int
) lubvoid
, jeśli funkcja niczego nie zwraca; - nazwa funkcji;
- argumenty funkcji zawarte w nawiasach lub puste nawiasy jeśli, brak argumentów.
Ciało funkcji zawiera operacje przez nią wykonywane oraz polecenie return
, jeśli funkcja ma zwracać jakąś wartość. Wykonanie tego polecenia kończy/przerywa wykonanie funkcji.
Przed funkcją, w której dana funkcja jest wykonywana, umieszcza się jej definicję lub przynajmniej jej nagłówek (a całą definicję później), np.
void x() // funkcja x { // jej kod } void y(); // nagłówek funkcji y void z() // funkcja z { x(); // wykonanie funkcji x y(); // wykonanie funkcji y } void y() // funkcja y { // jej kod }
Wybrane biblioteki i ich funkcje
- alloc.h – operacje na pamięci programu:
calloc
– przydzielenie pamięci na dynamiczną tablicę,free
– zwolnienie pamięci przydzielonej wcześniej na dynamiczną tablicę,
- cstdlib – operacje systemowe, w tym na terminalu”
system
– funkcja wykonująca polecenie systemowe.
- iostream – operacje wejścia/wyjścia, w tym pobieranie danych z klawiatury i wyświetlanie ich na ekranie:
cin
– operator pobrania danych,cout
– operator zwrócenia danych.
- math.h – operacje matematyczne:
pow
– potęga liczby o zadanym wykładniku,sqrt
– kwadratowy pierwiastek z liczby.
- stdio.h – operacje wejścia/wyjścia, w tym pobieranie danych z klawiatury i wyświetlanie ich na ekranie:
fgets
– funkcja pobierająca ciąg znaków do tablicy znaków,printf
– funkcja zwrócenia danych,scanf
– funkcja pobrania danych.
- string.h – operacje na łańcuchach znaków:
strlen
– funkcja zwracająca długość łańcucha.
Funkcja główna „main”
Funkcja main
to główna funkcja programu, której ciało definiuje to, co program ma wykonać.
Standardowo funkcja main
zwraca liczbę całkowitą (0
, jeśli wykonała się poprawnie), więc jej nagłówek zawiera typ int
, np.:
int main() { cout << "witaj"; // wyświetlenie słowa return 0; // funkcja main zwraca 0 (zero) }
Niekiedy w nagłówku funkcji main
zamiast typu int
pojawia się void
, co oznacza, że ta funkcja niczego nie zwraca. Jest to niezgodne ze standardem języka C.
Typy danych
Wybrane typy danych:
bool
– wartość logiczna:true
lub1
(prawda),false
lub0
(fałsz),char
– znak: litera, cyfra itp.,int
– liczba całkowita (-2147483648 .. 2147483647),float
– liczba rzeczywista (8 cyfrowa),double
– liczba rzeczywista (15-cyfrowa),string
– łańcuch znaków (wymaga bibliotekistring.h
).
Zapisywanie danych w kodzie:
- w liczbie rzeczywistej część ułamkową oddziela się od całkowitej kropką, np.
2.25
, - znak umieszcza się w apostrofach, np
'a'
,- łańcuch znaków umieszcza się w cudzysłowie, np.
"qwerty"
,"a"
(łańcuch jednoznakowy),""
(łańcuch pusty).
- łańcuch znaków umieszcza się w cudzysłowie, np.
Zmienną tworzy (deklaruje) się, podając jej typ, nazwę i ewentualnie początkową wartość, np:
bool a = true; // zmienna a typu bool zawierająca true float b; // zmienna b typu float double c,d; // zmienne c i d typu double
Stałą tworzy się, podając słowo const
, jej typ, nazwę i wartość, np:
const int a = 12; // stała a typu int zawierająca 12
Dane globalne a lokalne
Zmienna lub stała jest globalna, jeśli jest utworzona poza jakąkolwiek funkcją, tj. na początku programu. Jest ona dostępna dla każdej funkcji programu, która nie zawiera deklaracji innej zmiennej o tej samej nazwie, co nazywa się przesłanianiem.
Zmienna lub stała jest lokalna, jeśli jest utworzona w jakiejś funkcji; jest więc w niej lokalna, o ile nie utworzono tam innej zmiennej o tej samej nazwie (przesłanianie).
Przykład z globalnymi zmiennymi a
i b
oraz lokalnymi zmiennymi b
i c
:
int a; // a jest globalne int b; // b jest globalne int main() { int b; // b jest lokalne // lokalne b przesłania globalne b, // które z tego powodu nie jest dostępne w funkcji main int c; // c jest lokalne return 0; }
Pobieranie danych z klawiatury
Daną z klawiatury pobiera się do wcześniej utworzonej zmiennej odpowiedniego typu, przy pomocy operatora cin
(z biblioteki iostream
) lub funkcji scanf
(z biblioteki stdio.h
).
Operator cin
Przy pomocy operatora cin
(z biblioteki iostream
) można pobrać dane do jednej lub więcej zmiennych, np:
int a; float b; double c; cin >> a; // pobranie liczby całkowitej do a cin >> b >> c; // pobranie liczb rzeczywistych kolejno do b i c
Funkcja scanf
Przy pomocy funkcji scanf
(z biblioteki stdio.h
) można pobrać dane do jednej lub więcej zmiennych, np.:
int a; float b; double c; char d; scanf("%d", &a); // pobranie liczby całkowitej do a scanf("%f %lf %c", &b, &c, &d); // pobranie liczb // rzeczywistych do b i c oraz znaku do d // (wprowadzane dane są oddzielane spacjami) scanf("%f,%lf,%c", &b, &c, &d); // pobranie jak wyżej // (wprowadzane dane są oddzielane przecinkami)
Argumenty funkcji scanf
to formatowanie wprowadzanych danych (w cudzysłowie) i adresy zmiennych, do których są wprowadzane. Adres zmiennej to jej nazwa poprzedzona znakiem &
. Argumenty oddzielane są przecinkami.
Format danej wprowadzanej do zmiennej opisuje znak %
i identyfikator typu zmiennej, opcjonalnie poprzedzony rozmiarem danej (np. długość łańcucha znaków), np.:
%d
– liczba całkowita (do zmiennej typuint
),%f
– liczba rzeczywista (do zmiennej typufloat
),%lf
– liczba rzeczywista (do zmiennej typudouble
),%c
– znak (do zmiennej typuchar
),%s
– łańcuch znaków (do zmiennej typustring
lub tablicy typuchar
).
Więcej o formatowaniu danych: pl.wikibooks.org/wiki/C/scanf.
Wyświetlanie danych na ekranie
Daną na ekranie wyświetla się bezpośrednio (jako fragment kodu) lub z wcześniej utworzonej zmiennej odpowiedniego typu, przy pomocy operatora cout
(z biblioteki iostream
) lub funkcji printf
(z biblioteki stdio.h
).
Operator cout
Przy pomocy operatora cout
(z biblioteki iostream
) można wyświetlić dane bezpośrednio lub z jednej lub więcej zmiennych np:
int a = 15; float b = 7.45; char c = 'k'; cout << "qwerty"; // wyświetlenie napisu qwerty cout << a; // wyświetlenie zawartości a cout << b << " " << c; // wyświetlenie zawartości kolejno b, spacji i c
Polecenie endl
lub znak '\n'
powoduje przejście do nowej linii, np:
cout << "1. linia" << endl << "2. linia\n" << "3. linia";
Funkcja printf
Przy pomocy funkcji printf
(z biblioteki stdio.h
) można wyświetlić dane bezpośrednio lub do jednej lub więcej zmiennych, np.:
int a = 15; float b = 7.45; char c = 'k'; printf("qwerty"); // wyświetlenie napisu qwerty printf("%d", a); // wyświetlenie zawartości a printf("b=%f", a); // wyświetlenie napisu b= i zawartości b printf("%.1f", a); // wyświetlenie zawartości b // z 1 cyfrą części ułamkowej printf("%c %d", c, c); // wyświetlenie zawartości c jako znaku, // spacji i zawartości z jako kodu ASCII
Argumenty funkcji printf
to wyświetlany łańcuch znaków (w cudzysłowie) i zmienne, których zawartość jest wyświetlana. Kolejność zmiennych musi odpowiadać kolejności znaków %
z łańcucha. Argumenty oddzielane są przecinkami.
Format wyświetlania danej ze zmiennej opisuje znak %
i identyfikator typu zmiennej, opcjonalnie poprzedzony modyfikatorem (np. kropką z liczbą cyfr części ułamkowej), np.:
%d
– liczba całkowita (ze zmiennej typuint
),%f
– liczba rzeczywista (ze zmiennej typufloat
lub typudouble
),%c
– znak (ze zmiennej typuchar
),%s
– łańcuch znaków (ze zmiennej typustring
lub tablicy typuchar
).
Znak '\n'
powoduje przejście do nowej linii, np:
printf("1. linia\n2. linia";
Więcej o formatowaniu danych: pl.wikibooks.org/wiki/C/printf.
Instrukcja warunku „if”
Instrukcja warunku if
służy do wyboru operacji do wykonania, zależnie od prawdziwości podanego warunku. Warunek podaje się w nawiasach; musi mieć wartość logiczną, np.:
int a, b, c; if(a<b) // jeśli a<b { c = a+b; // to przypisanie a+b do c }
Opcjonalne polecenie else
definiuje operacje do wykonania, jeśli podany warunek nie jest prawdziwy, np.:
int a, b, c; if(a<b) // jeśli a<b, to: { c = a+b; } else // w przeciwnym razie: { c = a-b; }
Wyrażenia if
można zagnieżdżać, np:
int a, b, c; if(a<b) // jeśli a<b, to: { c = a+b; } else if(a==b) // zaś jeśli a=b, to: { c = 0; } else // w przeciwnym razie: { c = a-b; }
Alternatywnie instrukcję wyboru można zapisać przy pomocy znaków ?
i :
, w postaci warunek ? operacje_gdy_prawdziwy : operacje_gdy_fałszywy
np.:
a<b ? c=a+b : c=a-b;
Instrukcja wyboru „switch”
Instrukcja warunku switch
służy do wyboru operacji do wykonania, zależnie od wartości podanej zmiennej kontrolnej. Dla każdej wartości definiuje się osobny przypadek wyrażeniem case
, np.:
switch(a) // a jest zmienną kontrolną typu int { case 7: // jeśli a=7: a++; case 12: // jeśli a=12: b = 2; a += b; break; case 13: // jeśli a=13: a = 0; default: // w pozostałych przypadkach: a *= b; }
Operacje zawarte w wyrażeniu switch
wykonywane są od pierwszej z pierwszego wyrażenia case
(przypadku) którego wartość zgadza się z zawartością zmiennej kontrolnej, do pierwszego napotkanego polecenia break
lub do ostatniej operacji z ostatniego wyrażenia case
lub wyrażenia default
.
Polecenie break
kończy/przerywa wykonywanie wyrażenia switch
.
Wyrażenie default
jest opcjonalne; odpowiada wszystkim innym przypadkom, niż te zdefiniowane wyrażeniami case
.
Pętla „for”
Instrukcja pętli for
służy do wielokrotnego wykonania jej kodu, zwykle zależnie od wartości zmiennej sterującej pętlą i/lub spełnienia danego warunku.
Po wyrażeniu for
w nawiasach można podać 3 instrukcje oddzielone średnikami:
- instrukcja 1 – operacje wykonywane przed wykonaniem pętli, np.:
- utworzenie zmiennej sterującej (jeśli jeszcze nie została utworzona),
- ustawienie początkowej wartości dla zmiennej sterującej.
- instrukcja 2 – warunek logiczny sprawdzany przed pierwszym/kolejnym wykonaniem kodu pętli (wykona się, jeśli będzie prawdziwy), np:
- sprawdzenie osiągnięcia końcowej wartości przez zmienną sterującą,
- sprawdzenie innego warunku.
- instrukcja 3 – operacje wykonywane po każdym wykonaniu kodu pętli, np:
- inkrementacja wartości zmiennej sterującej.
Przykład pętli wykonującej się 8 razy, tj. dopóki zmienna sterująca i≤8
. Po każdym wykonaniu kodu i jest inkrementowane (i++
).
int i; // zmienna sterująca for (i=1; i<=8; i++) { //kod pętli }
Jeśli w kodzie pętli zostanie wykonane polecenie break
, to dalsze jego operacje zostaną pominięte, a pętla opuszczona.
Jeśli w kodzie pętli zostanie wykonane polecenie continue
, to dalsze jego operacje zostaną pominięte, a kod pętli rozpocznie się od nowa (o ile warunek pętli będzie spełniony), np:
int j; for (int i=1; i<=8; i++) { cin >> j; if (j<i) break; // opuszczenie pętli else if (j==i) continue; // przejście do kolejnej iteracji else // reszta kodu pętli }
Pętla „while”
Instrukcja pętli while
służy do wielokrotnego wykonania jej kodu, zależnie od spełnienia podanego warunku. Każde wykonanie kodu następuje po sprawdzeniu, że warunek jest prawdziwy; w przeciwnym razie pętla się kończy. Czyli kod pętli wykonywany jest dopóki warunek jest prawdziwy.
Warunek zapisuje się po wyrażeniu while
w nawiasach, np:
char z = ' '; // początkowo z zawiera spację while(z != 'q') // warunek: z nie zawiera 'g' { scanf("%c", &z); }
Wyrażenia break
i continue
, umieszczone w kodzie pętli, działają tak samo, jak w przypadku pętli for
.
Pętla „do” .. „while„
Instrukcja pętli do
.. while
służy do wielokrotnego wykonania jej kodu, zależnie od spełnienia podanego warunku. Drugie i każde kolejne wykonanie kodu następuje po sprawdzeniu, że warunek jest prawdziwy; w przeciwnym razie pętla się kończy. Czyli pierwsze wykonanie kodu nie zależy od warunku; poza tym kod pętli wykonywany jest dopóki warunek jest prawdziwy.
Warunek zapisuje się po wyrażeniu while
w nawiasach, np:
char z; // początkowa zawartość z nie jest ważna do { scanf("%c", &z); } while(z != 'q'); // warunek: z nie zawiera 'g'
Wyrażenia break
i continue
, umieszczone w kodzie pętli, działają tak samo, jak w przypadku pętli for
.
Menu programu
Przykład menu programu, które pozwala na wybór funkcji do wykonania.
Wybranie numeru 1–3 powoduje wykonanie odpowiedniej funkcji, po czym ekran jest czyszczony i menu wyświetla się ponownie. Wybranie innej liczby powoduje zakończenie programu.
int a = 0; // a przechowuje wybraną liczbę do { system("cls||clear"); // czyszczenie ekranu cout << "1: Funkcja 1." << endl; cout << "2: Funkcja 2." << endl; cout << "3: Funkcja 3." << endl; cout << "inna cyfra: koniec programu." << endl; cout << "wybierz funkcje: "; cin >> a; switch (a) { case 1: funkcja1(); break; case 2: funkcja2(); break; case 3: funkcja3(); break; } } while (a >= 1 && a<= 3);
Funkcja system("cls||clear")
czyści ekran (dla Windowsa wystarczy cls
, a dla Linuxa – clear
); wymaga bilioteki cstdlib
.
Tablica statyczna
Tablica jest uporządkowaną strukturą danych tego samego typu.
Statyczna tablica ma stały rozmiar i tworzona jest podobnie do zmiennej – lokalnie lub globalnie; z zawartością lub bez. Rozmiar tablicy (liczbę jej komórek) podaje się w nawiasach []
, np.:
char a[4]; // utworzenie a jako tablicę 4 znaków //utworzenie b i c jako tablic 3 znaków z zawartością: char b[3] = {'x','y','z'}; // z podaniem rozmiaru tablicy char c[] = {'x','y','z'}; // bez podania rozmiaru tablicy
Tablica może być też macierzą, jeśli w jej komórkach są tablice, np.:
int d[3][4]; // utworzenie macierzy 3×4 liczb całkowitych int e[3][4] = { // utworzenie takie samej tablicy z liczbami {1,2,3,4}, // wiersz 1 {5,6,7,8}, // wiersz 2 {9,10,11,12}}; // wiersz 3
Zapis do tablicy
Dane do tablicy wstawia się osobno do jej każdej lub wybranej komórki, podając numer (indeks) komórki. Komórki tablicy o rozmiarze n
numeruje się od 0
do n-1
, np.:
int a[7]; a[4] = 15; // wstawienie 15 do komórki nr 4 // wstawienie do każdej komórki jej numeru: for(i=0; i<7; i++) // zmienna kontrolna zaczyna się od 0 { a[i] = i; }
Odczyt z tablicy
Dane z tablicy pobiera się osobno z jej każdej lub wybranej komórki, podając numer (indeks) komórki. Komórki tablicy o rozmiarze n numeruje się od 0 do n-1, np.:
int x = a[4]; // skopiowanie a[4] do zmiennej x // wyświetlenie każdej komórki: for(i=0; i<7; i++) // zmienna kontrolna zaczyna się od 0 { cout << a[i]; }
Łańcuch znaków
Łańcuch znaków to zmienna typu string
lub tablica typu char
; służy do przechowywania ciągu znaków o dowolnej długości (także zerowej).
Jeśli łańcuch znaków zawarty jest w tablicy typu char
, w komórkach numerowanych od 0
do n
, to komórka n+1
zawiera znak końca łańcucha (znak'\0'
czyli liczba 0
w kodzie ASCII), a rozmiar tablicy ≥ n+2
. Pusty łańcuch zawiera znak końca w komórce nr 0
, np.:
char a[20] = "Tresc tekstu."; // utworzenie łańcucha znaków char b[20] = ""; // utworzenie pustego łańcucha znaków b[0] = 'x'; // wpisanie 1. znaku b[1] = 'y'; // wpisanie 2. znaku b[2] = 'z'; // wpisanie 3. znaku b[3] = '\0'; // wpisanie znaku końca po ostatnim znaku cout << "Rozmiar napisu a: " << strlen(a) << endl; cout << "Zawartość napisu a: " << a;
Funkcja strlen
z biblioteki string.h
zwraca długość łańcucha znaków, nie wliczając znaku końca. Argumentem funkcji jest nazwa łańcucha znaków (czyli adres tablicy).
Funkcja fgets
z biblioteki stdio.h
wstawia do tablicy znaków podany łańcuch znaków, np.:
char a[100]; // wstawienie z klawiatury (stdin) ciągu znaków do tablicy a: fgets(a, sizeof(a), stdin);
Inne przydatne funkcje, operujące na łańcuchach znaków, znajdują się w bibliotece string
dla języka C++ lub string.h
dla języka C.
Przestawianie elementów tablicy
Kolejne operacje składające się na zamianę miejscami dwóch komórek tablicy:
- zawartość jednej komórki kopiowana jest do pomocniczej zmiennej,
- zawartość drugiej komórki kopiowana jest do pierwszej komórki,
- zawartość pomocniczej zmiennej kopiowana jest do drugiej komórki, np.:
int a[2] = {11, 22}; // a jest tablicą dwóch liczb całkowitych int b; // pomocnicza zmienna b = a[0]; a[0] = a[1]; a[1] = b;
Wskaźnik
Wskaźnik to zmienna zawierająca adres jakiejś zmiennej (lub początkowej komórki tablicy) w pamięci programu.
Adres zmiennej to jej nazwa poprzedzona znakiem &
.
Adres tablicy (a właściwie jej komórki nr 0
), to nazwa tej tablicy. Adres każdej kolejnej komórki jest większy o 1
od adresu poprzedniej.
Typ wskaźnika to typ zmiennej, której adres ma przechowywać, wraz ze znakiem *
.
Przykład utworzenia zmiennej typu int
i wskaźnika na nią:
int a; int* wa; // wa to wskaźnik na zmienną typu int wa = &a; // teraz wa wskazuje na a
Przykład utworzenia tablicy typu char
i wskaźnika na nią:
char b[10]; char(*wb)[10]; // wb to wskaźnik na tablicę 10 znaków wb = b; // teraz wb wskazuje na b
Dostęp do zmiennej przez wskaźnik
Dostęp do zawartości zmiennej za pośrednictwem wskazującego na nią wskaźnika uzyskuje się przez nazwę wskaźnika poprzedzoną znakiem *
, np.:
int a; int* wa = &a; // wa wskazuje na a cout << *wa; // wyświetlenie zawartości zmiennej a
Dostęp do tablicy przez wskaźnik
Dostęp do zawartości komórki tablicy za pośrednictwem wskazującego na nią wskaźnika uzyskuje się przez nazwę wskaźnika poprzedzoną znakiem *
lub traktując wskaźnik jak tablicę (indeksowo), np.:
char b[10]; char(*wb)[10] = b; // wb wskazuje na b cout << *wb; // wyświetlenie komórki nr 0 cout << *(wb+2); // wyświetlenie komórki nr 2 cout << wb[2]; // wyświetlenie komórki nr 2
Zapisowi indeksowemu b[i]
odpowiada zapis wskaźnikowy *(tab+i)
.
Przykład pętli for
, operującej na tablicy typu int indeksowo, zmniejszającej o 1 zawartość każdej komórki:
int c[10]; for(int i = 0; i < 10; i++) c[i]--;
Analogiczny przykład pętli for
, operującej na tablicy typu int wskaźnikowo, zmniejszającej o 1
zawartość każdej komórki:
int c[10]; for(int i = 0; i < 10; i++) *(c+i)--;
Analogiczny przykład pętli for
, operującej na tablicy typu int wskaźnikowo, zmniejszającej o 1
zawartość każdej komórki, gdzie wc
przyjmuje w kolejnych wykonaniach kodu pętli adresy kolejnych komórek tablicy c
:
int c[10]; for(int* wc = c; wc < c+10; wc++) *wc--;
Funkcja bezargumentowa
Definicja funkcji składa się z nagłówka i ciała funkcji.
Nagłówek funkcji składa się z:
- typu wyniku zwracanego przez funkcję (np.
int
) lub słowavoid
, jeśli funkcja niczego nie zwraca; - nazwy funkcji;
- argumentów funkcji (np. zmiennych z danymi wejściowymi; są opcjonalne) zawartych w nawiasach
()
, oddzielonych przecinkami; argument składa się z:- typu argumentu (np.
float
), - nazwy argumentu.
- typu argumentu (np.
Ciało funkcji zawiera się w klamrach {}
i zawiera jej kod. Jeśli funkcja zwraca wynik, to jedną z jej operacji jest return
.
Funkcja musi być zdefiniowana przed funkcją, w której zostanie wywołana. Ewentualnie przed nią umieszcza się tylko nagłówek definicji funkcji, a całą definicję dowolnie niżej.
Funkcja bezargumentowa nie otrzymuje żadnych danych wejściowych jako argumentów, czyli nawiasy ()
umieszczone po jej nazwie są puste, np.:
// definicja funkcji f: int f() { int a = 12; return a; // ta funkcja zwraca liczbę 12 } // wykonanie funkcji f w funkcji main: int main() { cout << f(); // wyświetlenie liczby 12 return 0; }
Funkcja z argumentami przekazywanymi przez wartość
Funkcja z argumentami przekazywanymi przez wartość otrzymuje wartości (liczby, znaki itp.) jako dane wejściowe, które wpisuje do zmiennych podanych w nawiasach ()
w nagłówku definicji funkcji jako argumenty; te zmienne są lokalnymi zmiennymi tej funkcji, np.:
// definicja funkcji f // otrzymującej dwie liczby całkowite: int f(int a, int b) { return a + b; // funkcja zwraca sumę argumentów a i b } // wykonanie funkcji f w funkcji main: int main() { int c = 4; cout << f(23, c); // wyświetlenie sumy liczb 23 i 4 return 0; }
W powyższym przykładzie wykonanie funkcji f(23,c)
wewnątrz funkcji main
powoduje, że liczba 23
zostaje wpisana do zmiennej a
w funkcji f
, a zawartość zmiennej c
zostaje skopiowana do zmiennej b
w funkcji f
. Funkcja f
nie ma dostępu do zmiennej c
– otrzymuje tylko kopię jej zawartości (jej wartość).
Funkcja z argumentami przekazywanymi przez referencję
Funkcja z argumentami przekazywanymi przez referencję otrzymuje dostęp do zmiennej z funkcji tę funkcję wykonującej. Dostęp do zmiennej jest pod referencyjną (inną) nazwą, zdefiniowaną w nawiasach ()
w nagłówku definicji funkcji jako argument; ta zmienna jest lokalna w tej funkcji, np.:
// definicja funkcji f // otrzymującej referencję do zmiennej typu int: int f(int &a) // int &a to definicja referencyjnej nazwy zmiennej { return a; // funkcja zwraca zawartość zmiennej a } // wykonanie funkcji f w funkcji main: int main() { int c = 4; cout << f(c); // wyświetlenie 4 return 0; }
W powyższym przykładzie wykonanie funkcji f(c)
wewnątrz funkcji main
powoduje, że funkcja f
ma dostęp do zmiennej c
pod referencyjną nazwą a
. Funkcja f
ma dostęp do zmiennej c
.
Funkcja z argumentami przekazywanymi przez adres
Funkcja z argumentami przekazywanymi przez adres otrzymuje dostęp do zmiennej z funkcji tę funkcję wykonującej. Dostęp do zmiennej jest przez jej adres, umieszczony we wskaźniku, zdefiniowanym w nawiasach ()
w nagłówku definicji funkcji jako argument; ta zmienna jest lokalna w tej funkcji, np.:
// definicja funkcji f // otrzymującej adres zmiennej typu int: int f(int *a) // int *a to definicja wskaźnika do zmiennej { return *a; // funkcja zwraca zawartość spod adresu a } // wykonanie funkcji f w funkcji main: int main() { int c = 4; cout << f(&c); // wyświetlenie 4 return 0; }
W powyższym przykładzie wykonanie funkcji f(&c)
(&c
to adres zmiennej c
) wewnątrz funkcji main
powoduje, że funkcja f
ma dostęp do zmiennej c
przez wskaźnik do niej (a
). Funkcja f
ma dostęp do zmiennej c
.
Tablica dynamiczna jednowymiarowa
Dynamiczna tablica jest tablicą tworzoną podczas działania programu; jej rozmiar może się zmieniać. Utworzenie i dostęp do tablicy dynamicznej wymaga wskaźnika przechowującego jej adres. Utworzenie tablicy to przydzielenie jej pamięci programu funkcją calloc
(i nie tylko)z biblioteki alloc.h
lub operatorem new
. Usunięcie tablicy to uwolnienie tej pamięci funkcją free
lub operatorem delete
.
Funkcje calloc i free
Przykład utworzenia, a następnie usunięcia tablicy t
liczb całkowitych:
int r; // zmienna na rozmiar tablicy cin >> r; int *t; // wskaźnik na tablicę liczb całkowitych t = (int*) calloc(r, sizeof(int)); // utworzenie tablicy t free(t); // usunięcie tablicy t
Funkcja calloc
otrzymuje dwa argumenty: liczbę komórek tworzonej tablicy (r
) i rozmiar każdej komórki (sizeof(int)
), a jej wynik rzutowany jest na wskaźnik tablicy ((int*)
).
Funkcja free
otrzymuje jeden argument: wskaźnik tablicy (t
).
Operatory new i delete
Analogiczny przykład utworzenia, a następnie usunięcia tablicy t
liczb całkowitych:
int r; // zmienna na rozmiar tablicy cin >> r; int *t; // wskaźnik na tablicę liczb całkowitych t = new int[r]; // utworzenie tablicy t delete []t; // usunięcie tablicy t
Operator new poprzedza typ komórek tablicy (int
) i ich liczbę ([r]
).
Operator delete
poprzedza wskaźnik tablicy ([]t
).
Zmiana rozmiaru dynamicznej tablicy
Zwiększenie lub zmniejszenie rozmiaru dynamicznej tablicy wykonuje się w następujących krokach:
- utworzenie tymczasowego wskaźnika na dynamiczną nową tablicę tego samego typu, co zmieniana stara tablica.
- Utworzenie nowej, o nowym rozmiarze, dynamicznej tablicy dla tymczasowego wskaźnika.
- Przekopiowanie zawartości starej tablicy do nowej, uwzględniając różnicę w rozmiarze i przyjęte zachowanie tablicy przy zmianie rozmiaru.
- Usunięcie starej tablicy.
- Przypisanie wskaźnikowi starej tablicy adresu nowej tablicy z jej wskaźnika.
Przykład zwiększenia tablicy liczb całkowitych o jedną nową liczbę, umieszczaną na końcu tablicy.
int r; // rozmiar tablicy int a=5; // zmienna z liczbą dodawaną na końcu tablicy int *t = new int[r]; // utworzenie starej tablicy int *tt = new int[r+1]; // utworzenie nowej tablicy // skopiowanie zawartości starej tablicy do nowej: for(int i=0; i<r; i++) tt[i] = t[i]; tt[r] = a; // wstawienie zawartości a do ostatniej komórki tt delete []t; // usunięcie starej tablicy t = tt; // przypisanie wskaźnikowi t adresu nowej tablicy
Tablica dynamiczna dwuwymiarowa
Dwuwymiarowa dynamiczna tablica (macierz) jest analogiczna do jednowymiarowej, z tym że zamiast być tablicą komórek określonego typu, jest tablicą tablic komórek określonego typu.
Przykład utworzenia i usunięcia tablicy tablic liczb całkowitych:
int **t; // wskaźnik na macierz int w, k; // liczba wierszy (w) i kolumn (k) // utworzenie tablicy wskaźników na liczby typu int: t = new int*[w]; for (int i=0; i<w; i++) // dla każdego wiersza macierzy: t[i] = new int[k]; // utworzenie i-tej tablicy typu int // usunięcie macierzy: for (int i=0; i<rozmiar; i++) // dla każdego wiersza macierzy: delete []t[i]; // usunięcie i-tej tablicy delete []t; // usunięcie macierzy