Skrypt do Podstaw Programowania

Wybrane podstawowe zagadnienia ze strukturalnego programowania w języku C (i C++), pomocne w wykonywaniu ćwiczeń z Podstaw Programowania.

Spis treści:

  1. Budowa programu.
  2. Wybrane biblioteki i ich funkcje.
  3. Funkcja główna „main”.
  4. Typy danych.
  5. Dane globalne a lokalne.
  6. Pobieranie danych z klawiatury.
  7. Wyświetlanie danych na ekranie.
  8. Instrukcja warunku „if”.
  9. Instrukcja wyboru „switch”.
  10. Pętla „for”.
  11. Pętla „while”.
  12. Pętla „do” .. „while„.
  13. Menu programu.
  14. Tablica statyczna.
  15. Zapis do tablicy.
  16. Odczyt z tablicy.
  17. Łańcuch znaków.
  18. Przestawianie elementów tablicy.
  19. Wskaźnik.
  20. Dostęp do zmiennej przez wskaźnik.
  21. Dostęp do tablicy przez wskaźnik.
  22. Funkcja bezargumentowa.
  23. Funkcja z argumentami przekazywanymi przez wartość.
  24. Funkcja z argumentami przekazywanymi przez referencję.
  25. Funkcja z argumentami przekazywanymi przez adres.
  26. Tablica dynamiczna jednowymiarowa.
  27. 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) lub void, 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 lub 1 (prawda), false lub 0 (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 biblioteki string.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).

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 typu int),
  • %f – liczba rzeczywista (do zmiennej typu float),
  • %lf – liczba rzeczywista (do zmiennej typu double),
  • %c – znak (do zmiennej typu char),
  • %s – łańcuch znaków (do zmiennej typu string lub tablicy typu char).

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 typu int),
  • %f – liczba rzeczywista (ze zmiennej typu float lub typu double),
  • %c – znak (ze zmiennej typu char),
  • %s – łańcuch znaków (ze zmiennej typu string lub tablicy typu char).

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:

  1. zawartość jednej komórki kopiowana jest do pomocniczej zmiennej,
  2. zawartość drugiej komórki kopiowana jest do pierwszej komórki,
  3. 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łowa void, 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.

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ą aFunkcja 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:

  1. utworzenie tymczasowego wskaźnika na dynamiczną nową tablicę tego samego typu, co zmieniana stara tablica.
  2. Utworzenie nowej, o nowym rozmiarze, dynamicznej tablicy dla tymczasowego wskaźnika.
  3. Przekopiowanie zawartości starej tablicy do nowej, uwzględniając różnicę w rozmiarze i przyjęte zachowanie tablicy przy zmianie rozmiaru.
  4. Usunięcie starej tablicy.
  5. 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

Comments are closed.