Języki Programowania – Zajęcia 2

Instrukcje warunkowe, pętle, funkcje i tablice

Cel zajęć

Na poprzednich zajęciach poznaliście podstawy pracy z C++: tworzenie plików źródłowych, kompilację programu przy użyciu g++ oraz podstawowe typy danych i operacje na zmiennych.

Dzisiaj poznamy podstawowe konstrukcje programistyczne, które znacie już z Pythona: instrukcje warunkowe, pętle, funkcje oraz tablice. Zobaczymy, jak zapisuje się je w C++ i wykorzystamy je w jednym większym zadaniu.

1. Instrukcja warunkowa if

Instrukcja if pozwala wykonać określony fragment programu tylko wtedy, gdy podany warunek jest spełniony.

Podstawowa składnia wygląda następująco:

if (warunek) {
    // instrukcje wykonywane, gdy warunek jest prawdziwy
}
else {
    // instrukcje wykonywane, gdy warunek jest fałszywy
}

Przykład:

int x = 10;

if (x > 5) {
    cout << "x jest wieksze od 5" << endl;
}
else {
    cout << "x nie jest wieksze od 5" << endl;
}

Możemy również sprawdzać kilka warunków za pomocą else if:

if (x > 10) {
    cout << "x jest wieksze od 10" << endl;
}
else if (x > 5) {
    cout << "x jest wieksze od 5, ale nie od 10" << endl;
}
else {
    cout << "x jest mniejsze lub rowne 5" << endl;
}

Podstawowe operatory porównania:

Operator Znaczenie
==równe
!=różne
>większe
<mniejsze
>=większe lub równe
<=mniejsze lub równe
Uwaga: w C++ pojedynczy znak = oznacza przypisanie, natomiast == oznacza porównanie.

2. Pętla for

Pętla for pozwala wielokrotnie wykonać określony fragment programu.

Podstawowa składnia:

for (inicjalizacja; warunek; zmiana) {
    // instrukcje
}

Przykład:

for (int i = 0; i < 10; i++) {
    cout << i << endl;
}

Program wykona instrukcje dla i = 0, 1, 2, ..., 9. Jest to odpowiednik znanej z Pythona konstrukcji:

for i in range(10):
    print(i)

Warto zwrócić uwagę na trzy części nagłówka pętli:

3. Pętla while

Pętla while wykonuje instrukcje tak długo, jak długo spełniony jest podany warunek.

Składnia:

while (warunek) {
    // instrukcje
}

Przykład:

int i = 0;

while (i < 10) {
    cout << i << endl;
    i++;
}

Operator ++ zwiększa wartość zmiennej o 1:

i++;

jest równoważne:

i = i + 1;

4. Tablice

Tablica pozwala przechowywać wiele wartości tego samego typu. Tablicę o określonym rozmiarze tworzymy na przykład tak:

double measurements[10];

Możemy od razu podać wartości:

double measurements[10] = {
    10.2, 9.8, 10.5, 10.1, 9.9,
    10.3, 11.7, 10.0, 9.6, 10.4
};

Podobnie jak w Pythonie, numerowanie elementów zaczyna się od zera:

measurements[0]   // pierwszy element
measurements[1]   // drugi element
measurements[9]   // dziesiaty element

Elementy tablicy możemy przeglądać za pomocą pętli:

for (int i = 0; i < 10; i++) {
    cout << measurements[i] << endl;
}
Na tych zajęciach używamy klasycznych tablic C++. Nie używamy jeszcze std::vector. Do tematu wektorów wrócimy na kolejnych zajęciach.

5. Funkcje

Funkcja pozwala wydzielić fragment programu wykonujący określone zadanie. Funkcję możemy następnie wielokrotnie wywoływać z różnych miejsc programu.

Ogólna składnia funkcji:

typ_zwracany nazwa_funkcji(parametry) {
    // instrukcje
    return wartosc;
}

Na przykład:

double square(double x) {
    return x * x;
}

Funkcję możemy następnie wywołać:

double result = square(3.0);

cout << result << endl;

Funkcja może również niczego nie zwracać. W takim przypadku używamy typu void:

void printHello() {
    cout << "Hello!" << endl;
}

5.1. Funkcja przyjmująca tablicę

Do funkcji możemy przekazać również tablicę. Przykładowo funkcja która zwiększa wszystkie elementy tablicy o 1 może wyglądać następująco:


void addCalibrationOffset(double measurements[], int length) {
    for (int i = 0; i < length; ++i) {
        measurements[i] += 1;
    }
}

Funkcję możemy wywołać dla naszej tablicy:

addCalibrationOffset(measurements, 10);
Ważne: przy przekazywaniu klasycznej tablicy do funkcji podajemy również jej rozmiar. Sama tablica nie przekazuje funkcji informacji o liczbie przechowywanych elementów.

6. Ćwiczenie – krótkie przykłady

Ćwiczenie 1: instrukcja warunkowa

Napiszcie program, który pobiera od użytkownika liczbę całkowitą i sprawdza, czy liczba jest dodatnia, ujemna czy równa zero.

Program powinien wykorzystać if, else if i else.

Ćwiczenie 2: pętla

Napiszcie program, który wypisuje liczby od 1 do 10 oraz ich kwadraty. Wykorzystajcie pętlę for.

Przykładowy fragment wyniku:

1  1
2  4
3  9
...
10  100

Ćwiczenie 3: funkcja

Napiszcie funkcję:

double suma(double x, double y)

która zwraca sumę dwóch liczb. Wywołajcie ją dla kilku wartości i wypiszcie wyniki.

12. Czym jest std::vector?

Klasyczne tablice w C++ (np. double measurements[10]) mają stały rozmiar, ustalany w momencie pisania kodu lub kompilacji. Ponadto przekazując je do funkcji, należy za każdym razem osobnym parametrem podawać ich rozmiar (int size), ponieważ sama tablica nie przechowuje informacji o liczbie swoich elementów.

std::vector to dynamiczna tablica dostarczana przez bibliotekę standardową C++ (wymaga nagłówka #include <vector>).

Najważniejsze zalety std::vector:

Przykład użycia std::vector

Oto jak wygląda analiza danych przy użyciu struktury std::vector:

#include <iostream>
#include <vector> // Wymagane dla std::vector

// Funkcja przyjmuje referencję do wektora (const std::vector<double>&)
// DZIĘKI TEMU: nie musimy podawać rozmiaru jako osobnego parametru!
double average(const std::vector<double>& data) {
    if (data.empty()) return 0.0; // Ochrona przed dzieleniem przez zero

    double sum = 0.0;
    
    // Pętla range-based for (działa analogicznie do "for x in data" z Pythona)
    for (double val : data) {
        sum += val;
    }

    return sum / data.size(); // data.size() zwraca aktualną liczbę elementów
}

int main() {
    // Inicjalizacja wektora wartościami początkowymi
    std::vector<double> measurements = {
        10.2, 9.8, 10.5, 10.1, 9.9,
        10.3, 11.7, 10.0, 9.6, 10.4
    };

    // Dodawanie nowego elementu na koniec (rozmiar sam rośnie z 10 do 11)
    measurements.push_back(10.8);

    // Obliczenie średniej
    double avg = average(measurements);

    std::cout << "Liczba pomiarow: " << measurements.size() << std::endl;
    std::cout << "Srednia: " << avg << std::endl;

    return 0;
}

Porównanie: Tablica tradycyjna vs std::vector

Cecha Tablica tradycyjna (double arr[10]) Wektor (std::vector<double>)
Rozmiar Stały (znany w momencie kompilacji) Dynamiczny (może rosnąć i maleć)
Odczyt rozmiaru Wymaga osobnej zmiennej data.size()
Przekazywanie do funkcji void fn(double arr[], int size) void fn(const std::vector<double>& v)
Dodawanie elementów Brak możliwości v.push_back(wartość)

13. Najważniejsze rzeczy z dzisiejszych zajęć