PP – Laboratorium 10

Wejściówka

Napisz program w języku C++, który stworzy dwuwymiarową tablicę (NxM) w postaci tablicy wskaźników do tablicy wartości liczb całkowitych. Napisz funkcję f1, która wypełni tą tablicę dowolnymi wartościami.

Rozwiązanie:
#include <iostream>

void f1(int **arr, int n, int m) {
    for(int i = 0; i < n; ++i)
        for(int j = 0; j < m; ++j)
            arr[i][j] = i * j;
}
int main() {
    int n, m;
    std::cin >> n >> m;
    int **arr = new int*[n];
    for(int i = 0; i < n; ++i)
        arr[i] = new int[m];
    f1(arr, n, m);
    for(int i = 0; i < n; ++i)
        delete[] arr[i];
    delete[] arr;
    return 0;
}

Zadanie 1

Napisz program w języku C++, który za pomocą wskaźnika do funkcji wywoła dwie funkcje: add i sub. Funkcja add powinna dodawać dwie liczby, zaś funkcja sub odejmować dwie liczby. Zaimplementowany program powinien sprawdzić poprawność działania tych funkcji.

Rozwiązanie:
#include <iostream>

int add(int a, int b) {
    return a + b;
}
int sub(int a, int b) {
    return a - b;
}
int main() {
    int (*fun)(int, int);
    fun = add;
    std::cout << fun(10, 2) << std::endl;
    fun = sub;
    std::cout << fun(10, 2) << std::endl;
    return 0;
}
Omówienie:

Dotychczas zajmowaliśmy się sytuacją, gdy wskaźnik wskazywał na jakąś zmienną. Jednak nie tylko zmienna ma swój adres w pamięci. Oprócz zmiennej także i funkcja musi mieć swoje określone miejsce w pamięci. Z tego względu nie ma przeszkód, aby i na nią wskazywał jakiś wskaźnik.

Kod maszynowy utworzony po skompilowaniu programu odnosi się właśnie do adresu funkcji. Wskaźnik na funkcję różni się od innych rodzajów wskaźników.

Jedną z głównych różnic jest jego deklaracja. Zwykle wygląda ona następująco:
typ_zwracanej_wartości (*nazwa_wskaźnika)(typ1 argument1, typ2 argument2);

Należy zwrócić uwagę na dwie rzeczy:
– przypisując nazwę funkcji bez nawiasów do wskaźnika, automatycznie informujemy kompilator, że chodzi nam o adres funkcji,
– wskaźnika używamy tak, jak normalnej funkcji, na którą on wskazuje.

Zadanie 2

Napisz program w języku C++, który wczyta od użytkownika dwie liczby zmiennoprzecinkowe a i b, a następnie nieujemną liczbę całkowitą o. Program, w zależności od podanej nieujemnej liczby całkowitej, powinien wyświetlić wynik odpowiedniego działania:
0 – dodawanie,
1 – odejmowanie,
2 – mnożenie,
3 – dzielenie.
Do rozwiązania zadania wykorzystaj wskaźniki na funkcje.

Rozwiązanie:
#include <iostream>

float add(float a, float b) {
    return a + b;
}

float sub(float a, float b) {
    return a - b;
}

float mul(float a, float b) {
    return a * b;
}

float div(float a, float b) {
    return a / b;
}

int main() {
    unsigned int o;
    float a, b;
    float (*f_ptr[4])(float, float) = {add, sub, mul, div}; // tablica wskaźników na funkcje
    std::cin >> a >> b >> o;
    std::cout << f_ptr[o](a, b) << std::endl;
    return 0;
}

Zadanie 3

Wykorzystując wskaźniki na funkcje napisz program w języku C++, który w zależności od wywołania funkcji f znajdzie w tablicy liczb całkowitych wartość minimalną lub maksymalną.

Rozwiązanie:
#include <iostream>

int min(int a, int b) {
    return a < b ? a : b;
}

int max(int a, int b) {
    return a > b ? a : b;
}

int f(int arr[], int n, int (*fun)(int,int)) {
    int ex = arr[0];
    for(int i = 1; i < n; ++i) 
        ex = fun(ex, arr[i]);
    return ex;
}

int main() {
    int arr[] = {1, 2, 3, 5, 15, 0, 12, 13, 5, 7};
    std::cout << f(arr, 10, min) << std::endl;
    std::cout << f(arr, 10, max) << std::endl;
    return 0;
}
Omówienie:
int f(int arr[], int n, int (*fun)(int,int))
//można zamienić na:
typedef int (*p_fun)(int, int);
int f(int arr[], int n, p_fun fun)

Zadanie 4

Napisz program w języku C++, który posortuje tablicę dowolnych liczb rzeczywistych arr1 malejąco oraz tablicę dowolnych liczb całkowitych arr2 rosnąco. Do sortowania wykorzystaj algorytm Quick Sort z pliku nagłówkowego cstdlib.

Rozwiązanie:
#include <cstdlib>
#include <iostream>

int min(const void *a, const void *b) {
    float av = *(float *)a;
    float bv = *(float *)b;
    if(av > bv) return -1;
    else if(av == bv) return 0;
    else return 1;
    //lub
    //return *(float*)b - *(float*)a;
}

int max(const void *a, const void *b) {
    int av = *(int *)a;
    int bv = *(int *)b;
    if(av < bv) return -1;
    else if(av == bv) return 0;
    else return 1;
    //lub
    //return *(int*)a - *(int*)b;
}

int main() {
    float arr1[] = {1.f, 2.f, 3.f, 5.f, 15.f, 0.f, 12.f, 13.f, 5.f, 7.f};
    int arr2[] = {1, 2, 3, 5, 15, 0, 12, 13, 5, 7};

    qsort(arr1, 10, sizeof(float), min);
    for(int i = 0; i < 10; ++i) 
        std::cout << arr1[i] << " "; 
    std::cout << "\n";

    qsort(arr2, 10, sizeof(int), max);
    for(int i = 0; i < 10; ++i) 
        std::cout << arr2[i] << " "; 
    std::cout << "\n";
    
    return 0;
}

Zadanie 5

Napisz program w języku C++, który posortuje malejąco tablicę napisów za pomocą funkcji qsort.

Rozwiązanie:
#include <cstdlib>
#include <cstring>
#include <iostream>

int compare(const void* p1, const void* p2) {
    return -strcmp((char *)p1, (char *)p2);
}

int main() {
    char arr[5][100]={"tygrys syberyjski", "krokodyl nilowy", "panda wielka", "tygrys azjatycki","chomik europejski"};
    qsort(arr, 5, 100, compare);
    for(int i = 0; i < 5; ++i) 
        std::cout << arr[i] << std::endl;
    return 0;
}

Zadanie 6

Napisz program w języku C++, który wczyta od użytkownika liczbę, jako napis. Liczba powinna mieć co najwyżej 10 cyfr. Następnie program powinien zwiększyć tą liczbę o 10 i ją wyświetlić.

Rozwiązanie:
#include <cstdio>
#include <cstdlib>
#include <iostream>

int main() {
    char str[11] = {0}, *ptr = nullptr;
    scanf("%10s", str);
    int value = strtol(str, &ptr, 10) + 10;
    std::cout << std::to_string(value) << std::endl;
    return 0;
}

Zadanie 7

Zaprojektuj funkcję w języku C++, która zwróci indeks szukanej wartości w posortowanej tablicy liczb całkowitych. Funkcja powinna zwrócić -1, jeśli nie uda się znaleźć takiej wartości w tablicy. Rozwiązanie powinno wykorzystywać algorytm Binary Search. Napisz program w języku C++, który przetestuje działanie tej funkcji.

Rozwiązanie:
#include <iostream>
#include <string>
#include <cstdlib>

int binarySearch(int *arr, int l, int r, int x) {
    int mid = (l + r - 1) / 2;
    if(mid <= r && mid >= l) {
        if(arr[mid] == x) return mid;
        else if(arr[mid] > x) return binarySearch(arr, l, mid - 1, x);
        else return binarySearch(arr, mid + 1, r, x);
    }
    return -1;
}

int compare(const void *ap, const void *bp) {
    return *(int*)ap - *(int*)bp;
}

int main() {
    int arr[] = {1, 2, 3, 4, 10, 22, 23, 42};
    int x = 4;
    int n = sizeof(arr) / sizeof(arr[0]);
    int result = binarySearch(arr, 0, n - 1, x);
    int *p = (int *) bsearch(&x, arr, n, sizeof(arr[0]), compare);
    std::cout << (result == -1 ? "Brak" : std::to_string(result)) << std::endl;
    std::cout << (p ? std::to_string(p - arr) : "Brak") << std::endl;
    return 0;
}

Zadanie 8

Napisz funkcję w języku C++, która przyjmuje w argumencie tablicę liczb całkowitych (wskaźnik na kwadratową macierz) oraz jej rozmiar N. Funkcja powinna wypełnić przekazaną w argumencie macierz tabliczką mnożenia. Macierz jest spójnym obszarem pamięci.

Rozwiązanie:
#include <iostream>

void fill_matrix(int *matrix, int n) {
     for(int i = 0; i < n; ++i)
         for(int j = 0; j < n; ++j)
             matrix[i * n + j] = (i + 1) * (j + 1);
}
void print_matrix(int *matrix, int n) {
    for(int i = 0; i < n; ++i) {
        for(int j = 0; j < n; ++j)
            std::cout << matrix[i * n + j] << " ";
        std::cout << "\n";
    }
}
int main() {
    int n = 10;
    int *arr = new int[n * n];
    fill_matrix(arr, n);
    print_matrix(arr, n);
    delete[] arr;
    return 0;
}

Zadanie 9

Napisz funkcję w języku C++, która otrzymuje w argumencie jednowymiarową tablicę liczb zmiennoprzecinkowych, dwie liczby całkowite będące wymiarami obszaru reprezentowanego przez tablicę podaną w pierwszym argumencie. Funkcja powinna wypełnić krawędzie obszaru zerami, a następnie zwrócić dwuwymiarową tablicę z tak zmodyfikowanymi danymi. Napisz program w języku C++, który sprawdzi działanie tej funkcji.

Rozwiązanie:
#include <iostream>

void f(float *arr, int n, int m, float **result) {
    for(int i = 0; i < n; ++i)
        for(int j = 0; j < m; ++j)
            if(i == 0 || j == 0 || i == n - 1 || j == m - 1) 
                result[i][j] = 0.f;
            else 
                result[i][j] = arr[i * m + j];
}

void print_arr(float **arr, int n, int m) {
    for(int i = 0; i < n; ++i) {
        for(int j = 0; j < m; ++j)
            std::cout << arr[i][j] << " ";
        std::cout << "\n";
    }
}

int main() {
    const int n = 3, m = 5;
    float arr[n * m] = {1.f, 1.f, 1.f, 1.f, 1.f, 1.f, 1.f, 1.f, 1.f, 1.f, 1.f, 1.f, 1.f, 1.f, 1.f};
    float **result = new float*[n];
    for(int i = 0; i < n; ++i) 
        result[i] = new float[m];

    f(arr, n, m, result);
    print_arr(result, n, m);

    for(int i = 0; i < n; ++i) 
        delete[] result[i];
    delete[] result;
    return 0;
}

Zadanie 10

Napisz program w języku C++, który wczyta od użytkownika dwie liczby całkowite n i x, a następnie stworzy dwie n-elementowe tablice liczb całkowitych. Następnie program powinien uzupełnić tablicę kolejnymi wartościami zaczynając od x i ustawić te wartości w losowej kolejności (wymieszać). Na koniec program powinien wyświetlić obie tablicę, sumę wartości w tablicach oraz wynik iloczynu skalarnego dwóch tak zdefiniowanych wektorów. Spróbuj rozwiązać zadanie wykorzystując głównie algorytmy i funkcje biblioteczne np. iota z pliku nagłówkowego numeric.

Rozwiązanie:
#include <iostream>
#include <numeric>
#include <algorithm>

void print_arr(int *b, int *e) {
    for(int *i = b; i < e; ++i) 
        std::cout << *i << " "; 
    std::cout << "\n";
}

int main() {
    int n, x, init = 0, *t1 = nullptr, *t2 = nullptr;
    std::cin >> n  >> x;
    t1 = new int[n];
    t2 = new int[n];
    std::iota(t1, t1 + n, x);
    std::iota(t2, t2 + n, x);
    std::random_shuffle(t1, t1 + n);
    std::random_shuffle(t2, t2 + n);
    print_arr(t1, t1 + n);
    print_arr(t2, t2 + n);
    std::cout << std::accumulate(t1, t1 + n, init) << std::endl;
    std::cout << std::inner_product(t1, t1 + n, t2, init) << std::endl;
    delete[] t1;
    delete[] t2;
    return 0;
}

Zadanie 11

Napisz program w języku C++, który wczyta od użytkownika maksymalnie 100 znakowe słowo. Następnie program powinien stworzyć odwrócony odpowiednik napisu wprowadzonego przez użytkownika za pomocą arytmetyki wskaźników.

Rozwiązanie:
#include <cstdio>
#define MAX_SIZE 100

int main() {
    char str[MAX_SIZE + 1] = {0}, reverse[MAX_SIZE + 1] = {0};
    char *s = str;
    char *r = reverse;

    scanf("%100s", str);
    int len = 0;
    while(*(s++)) len++;

    s--;
    while(len >= 0) {
        *(r++) = *(--s);
        len--;
    }
    *r = '\0';

    printf("Original: %s\n", str);
    printf("Reverse: %s\n", reverse);
    return 0;
}
Omówienie:

Ciekawy przykład arytmetyki na wskaźnikach:

#include <iostream> 
using namespace std; 

int main() 
{ 
    int num[5]; 
    int* p; 
    p = num; 
    *p = 10; 
    p++; 
    *p = 20; 
    p = &num[2]; 
    *p = 30; 
    p = num + 3; 
    *p = 40; 
    p = num; 
    *(p + 4) = 50; 
    for (int i = 0; i < 5; i++) 
        cout << num[i] << ", "; 
    return 0; 
}

Przygotuj się na kolejne laboratorium!

W celu przygotowania się na kolejne zajęcia, spróbuj wykonać poniższe zadania samodzielnie.

Zadanie 1

Napisz program w języku C++, który stworzy n-elementową tablicę liczb całkowitych, a następnie wypełni tę tablicę dowolną wartością wykorzystując funkcję fill z biblioteki .

Zadanie 2

Napisz funkcję w języku C++, która przyjmuje wskaźnik na void oraz liczbę całkowitą ptrsize. ptrsize jest liczbą bajtów, która reprezentuje zmienną. Funkcja powinna odpowiednio zrzutować zmienną i wyświetlić jej wartość (obsłuż typ char oraz int).

Zadanie 3

Zapoznaj się z innymi algorytmami znajdującymi się w bibliotece (find, generate, transform, etc).

Dodaj komentarz

Twój adres e-mail nie zostanie opublikowany. Wymagane pola są oznaczone *