• Najnowsze pytania
  • Bez odpowiedzi
  • Zadaj pytanie
  • Kategorie
  • Tagi
  • Zdobyte punkty
  • Ekipa ninja
  • IRC
  • FAQ
  • Regulamin
  • Książki warte uwagi

Wskaźniki na funkcje, wskaźniki na struktury, wskaźniki na... czego tylko zapragniecie.

Object Storage Arubacloud
0 głosów
476 wizyt
pytanie zadane 29 kwietnia 2020 w C i C++ przez Hubertius Bywalec (2,970 p.)
edycja 29 kwietnia 2020 przez Hubertius

Hej :)

Napotkałem poważny problem w wykonaniu pewnego zadania. Oto jego treść:

Napisz program do przeprowadzania operacji na liczbach zespolonych oraz skalarnych (całkowitych oraz zmiennoprzecinkowych) za pomocą funkcji danych wskaźnikami oraz strukturą, stanowiącą swoisty interfejs API operacji na danym typie liczb.

W tym celu przygotuj strukturę operations_t, która będzie przechowywała wskaźniki do funkcji wykonujących podstawowe operacje arytmetyczne oraz wejścia/wyjścia.

W strukturze operations_t musisz przewidzieć pola na przechowanie:

wskaźników do funkcji wykonujących podstawowe operacje na dwóch liczbach dowolnego typu i zwracających wynik tej operacji. Pola te powinny mieć nazwy:
add - sumowanie,
sub - odejmowanie,
div - dzielenie oraz
mul - mnożenie.
wskaźników od funkcji wykonujących operacje wejścia/wyjścia (wczytywania oraz wyświetlania):
read - wczytywanie liczby oraz
show - wyświetlanie liczby w konsoli.
Przygotuj funkcje do wykonywania podstawowych operacji matematycznych, pobierania danych od użytkownika oraz wyświetlania wyniku na konsoli dla liczb typu int, float i zespolonych (struct complex_t z zadania 4.7 Liczby zespolone - koszmar powraca).

Funkcje dla operacji arytmetycznych powinny być zgodne z następującym prototypem:

void* nazwa_funkcji(const void *left, const void *right);
Wartość zwracana:

Wskaźnik na liczbę będącą wynikiem operacji, lub
NULL jeśli wykonanie operacji arytmetycznej nie jest możliwe.
Funkcje wejścia/wyjścia powinny być zgodne z następującymi prototypami:

int nazwa_funkcji_wczytującej(void *a, void *b);
void nazwa_funkcji_wyświetlającej(const void *a);
Funkcja wczytująca pobiera od użytkownika dwie liczby i umieszcza je pod wskaźnikami a oraz b w takiej kolejności. Wartość zwracana:

1 - operacja wczytywania nie jest możliwa do wykonania,
0 - operacja wczytywania powiodła się.
Funkcja wyświetlająca wyświetla na ekranie liczbę daną wskaźnikiem a lub nie podejmuje żadnej operacji.

W przypadku liczb zespolonych funkcje wejścia/wyjścia powinny być zgodne z następującym formatem:

9.999999 ± i9.999999
Przykładowe wywołanie takich funkcji (dla operacji na liczbach typu float) powinno wyglądać następująco:

float a, b;
read_float(&a, &b);
float *result_float = (float *)mul_float(&a, &b);
show_float(result_float);
free(result_float);
Dodatkowo przygotuj funkcje, które będą przyjmowały wskaźniki na dwie liczby i sprawdzały czy pierwsza z nich jest mniejsza od drugiej, zgodnie z następującym prototypem:

int is_smaller_???(const void *a, const void *b);
Wartość zwracana:

-2 - gdy do funkcji przekazano błędne dane,
1 - kiedy pierwsza liczba (a) jest mniejsza od drugiej (b),
0 - w przeciwnym przypadku.
Nazwy funkcji powinny być utworzone analogicznie do przykładowej: is_smaller_float dla typu float.

Deklaracje wszystkich funkcji (oraz struktury complex_t w przypadku liczb zespolonych) należy umieścić w oddzielnych plikach nagłówkowych dla każdego typu. Nazwa pliku nagłówkowego ma wyglądać następująco: int_operations.h (dla typu int) a definicje należy umieścić w odpowiadających im plikach C (np. int_operations.c).

Przygotuj funkcję find_extremum która zwróci najmniejszy z wyników podstawowych operacji arytmetycznych na dwóch przekazanych liczbach. Prototyp powinien mieć następującą postać:

void* find_extremum(const void *a, const void *b, const struct operations_t *op, int(* comp)(const void *, const void *));
Parametry:

a, b - lewy i prawy argument operacji arytmetycznych, dane wskaźnikami,
op - struktura dostarczająca wskaźniki na funkcje, realizująca podstawowe operacje arytmetyczne dla danego typu,
comp - komparator określający która z liczb, danych mu argumentami wskaźnikowymi, jest mniejsza.
Wartość zwracana:

Wskaźnik na liczbę będącą najmniejszym wynikiem jednej z czterech operacji na a oraz b, lub
NULL jeśli znalezienie minimum nie jest możliwe.
Funkcja powinna nazywać się find_extremum, a jej przykładowe wywołanie powinno wyglądać następująco:

int a = 5, b = 1;
int *result_int = (int *)find_extremum(&a, &b, op, is_smaller_int);
// op - struktura ze wskaźnikami na operacje dla liczb całkowitych int
Deklaracja funkcji find_extremum oraz struktury operations_t powinna znaleźć się w pliku operations.h, a definicja funkcji w pliku operations.c.

Napisz program, który przetestuje działanie przygotowanych przez Ciebie funkcji.

W funkcji main utwórz dynamicznie tablicę struktur operations_t i przypisz do niej adresy poszczególnych funkcji. Sugerowana kolejność: float, int, complex.

W przypadku niepowodzenia program powinien wyświetlić komunikat Failed to allocate memory i zakończyć działanie z kodem błędu 8.
Następnie program powinien pobrać od użytkownika typ liczb do wczytania:

0 - liczby zmiennoprzecinkowe,
1 - liczby całkowite,
2 - liczby zespolone.
wczytać je i wyświetlić najmniejszy wynik jednej z operacji na tych liczbach (dodawania, odejmowania, dzielenia, mnożenia).

W przypadku podania błędnych danych program powinien wyświetlić komunikat Incorrect input i zakończyć działanie z kodem błędu 1.
W przypadku problemów z alokacją pamięci program powinien wyświetlić komunikat Failed to allocate memory i zakończyć działanie z kodem błędu 8.
Przykładowa interakcja z programem -- sukces:

Enter data type: 1⏎
Enter values: -80 47⏎
-3760
Enter data type: 2⏎
Enter values: 0.168847+i0.142800 5.174851+i1.480399⏎
0.037457 + i0.016879⏎
Enter data type: 0⏎
Enter values: -53.146552 65.326841⏎
-3471.896484
Przykładowa interakcja z programem -- brak pamięci:

Limit sterty: 0 bajtów

Failed to allocate memory⏎
Limit starty: 144 bajty

Enter data type: 0⏎
Enter values: -6.071656 2.593162⏎
Failed to allocate memory⏎
Przykładowa interakcja z programem -- błąd danych wejściowych:

Enter data type: 2⏎
Enter values: 4.422757-8.385103 4.422757-i8.385103⏎
Incorrect input
Enter data type: 3⏎
Incorrect input
Uwaga

W programie nie wolno używać operatora [] (dotyczy to również komentarzy jak i stringów formatujących)!

Poniżej zamieszczam to wszystko, co powinno być zrobione dla np. operacji na intgerach.

Strukturry:

struct operations_t
{
    void * (*add)(const void * left, const void * right);
    void * (*sub)(const void * left, const void * right);
    void * (*mul)(const void * left, const void * right);
    void * (*div)(const void * left, const void * right);
    int (*read)(void * a, void * b);
    void (*show)(const void * a);
};

struct complex_t
{
    float re;
    float im;
};

Funkcje na operacjach dla integer:

#include <stdio.h>
#include <stdlib.h>
#include "int_operations.h"


int read_int(void *a, void *b)
{
    if( a == NULL || b == NULL )
    {
        return 1;
    }
    if(scanf("%d %d",a,b)!=2)
    {
        return 1;
    }
    return 0;
}

void* add_int(const void *left, const void *right)
{
    if( left == NULL || right == NULL )
    {
        return NULL;
    }
    int * result = (int *) malloc(sizeof(int));
    *result = (*(int *)left) + (*(int *)right);
    return result;
}

void* sub_int(const void *left, const void *right)
{
    if( left == NULL || right == NULL )
    {
        return NULL;
    }

    int * pointer = (int *) malloc(sizeof(int));
    *pointer = (*(int *)left) - (*(int *)right);
    return pointer;
}

void* div_int(const void *left, const void *right)
{
    if( left == NULL || right == NULL )
    {
        return NULL;
    }
    int * pointer = (int *) malloc (sizeof(int));
    *pointer = (*(int *)left) / (*(int *)left);
    printf("%d\n",*pointer);
    return pointer;
}

void* mul_int(const void *left, const void *right)
{
    if( left == NULL || right == NULL )
    {
        return NULL;
    }
    int * pointer = (int *) malloc(sizeof(int));
    *pointer = (*(int *)left) * (*(int *)right);
    return pointer;
}

void show_int(const void *a)
{
    if(a == NULL)
    {

    }
    else
    {
        printf("%d",(*(int *)a));
    }
}

int is_smaller_int(const void *a, const void *b)
{
    if( a == NULL || b == NULL )
    {
        return -2;
    }
    if( *(int*)a < *(int*)b)
    {
        return 1;
    }
    return 0;
}

Funkcja do znajdowania ekstremum:

Fragment funkcji "find_extremum" dla integer:
if( comp == is_smaller_int )
    {
        int * pointer_on_result = (int*)malloc(sizeof(int));
        if( pointer_on_result == NULL )
        {
            return NULL;
        }
        int * result_int_add=(int *)(op->add)(&a,&b);
        if( result_int_add == NULL )
        {
            return NULL;
        }
        int * result_int_sub=(int *)(op->sub)(&a,&b);
        if( result_int_sub == NULL )
        {
            return NULL;
        }
        int * result_int_div=(int *)(op.div)(&a,&b);
        if( result_int_div == NULL )
        {
            return NULL;
        }
        int * result_int_mul=(int *)(op->mul)(&a,&b);
        if( result_int_mul == NULL )
        {
            return NULL;
        }
        int wynik_1 = comp(result_int_add,result_int_sub);
        int wynik_2 = comp(result_int_div,result_int_mul);
        int wynik;
        if( wynik_1 == 1)
        {
            if(wynik_2 == 1)
            {
                wynik = comp(result_int_add,result_int_div);
                if( wynik == 1)
                {
                    *pointer_on_result=*result_int_add;
                }
                else
                {
                    *pointer_on_result=*result_int_div;
                }
            }
            if(wynik_2 == 0)
            {
                wynik = comp(result_int_add,result_int_mul);
                if( wynik == 1)
                {
                    *pointer_on_result = *result_int_add;
                }
                else
                {
                    *pointer_on_result = *result_int_mul;
                }
            }
        }
        if(wynik_1 == 0)
        {
            if(wynik_2 == 1)
            {
                wynik = comp(result_int_sub,result_int_div);
                if( wynik == 1 )
                {
                    *pointer_on_result =  *result_int_sub;
                }
                else
                {
                    printf("Tutaj!");
                    *pointer_on_result = *result_int_div;
                }
            }
            if(wynik_2 == 0)
            {
                wynik = comp(result_int_sub,result_int_mul);
                if( wynik == 1 )
                {
                    *pointer_on_result = *result_int_sub;
                }
                else
                {
                    *pointer_on_result = *result_int_mul;
                }
            }
        }
        return pointer_on_result;

Fragment kodu dla integer w main:

if( wybor == 1 ) //float
    {
        strct_on_function++;
        (strct_on_function->add)=add_int;
        (strct_on_function->sub)=sub_int;
        (strct_on_function->div)=div_int;
        (strct_on_function->mul)=mul_int;
        printf("Enter values: ");
        int a,b;
        if(read_int(&a,&b)!=0)
        {
            printf("Incorrect input");
            return 1;
        }
        int * result = (int * )find_extremum(&a,&b,strct_on_function,is_smaller_int);
        if( result == NULL )
        {
            printf("Failed to allocate memory");
            return 8;
        }
        printf("%d",*result);

Wczytywanie liczb na pewno przebiega dobrze (sam to sprawdziłem). Odnoszę wrażenie, że coś się knoci na etapie albo przypisywania do pointerów w strukturze poszczególnych funkcji, albo w samej funkcji find_extremum. Dość powiedzieć, że ot choćby dla void* div_int(const void *left, const void *right) w main dla a pokazuje mi dla moją wczytaną wartość, a w samej funkcji... najprawdopodobniej adres (strasznie duża liczba). Podpowiecie mi gdzie popełniłem błąd?  :/

P.S Pierwszy błąd, jaki sam znalazłem przed chwilą to 

(op->add)(&a,&b)

nie powinno być tych adresów, bo a i b to są już wskaźniki.

komentarz 29 kwietnia 2020 przez j23 Mędrzec (195,220 p.)
strct_on_function++;
(strct_on_function->add)=add_int;
(strct_on_function->sub)=sub_int;

Po co ta inkrementacja?

W sumie nie zwalniasz pamięci, którą te funkcje zwracają. Cieknie strasznie.

komentarz 29 kwietnia 2020 przez Hubertius Bywalec (2,970 p.)
Sugerujesz, żeby po zakończonych operacjach przed końcem pliku źródłowego zwolnić pamięć dla poszczególnych pointerów w tej strukturze?
komentarz 29 kwietnia 2020 przez j23 Mędrzec (195,220 p.)
Nie chodzi mi o te pointery w strukturze, tylko o pamięć, którą zwracają te funkcje jako wynik operacji,

Jeszcze raz: po co ta inkrementacja?

Zaloguj lub zarejestruj się, aby odpowiedzieć na to pytanie.

Podobne pytania

0 głosów
3 odpowiedzi 1,379 wizyt
0 głosów
1 odpowiedź 164 wizyt
pytanie zadane 19 kwietnia 2021 w C i C++ przez MrChick888 Obywatel (1,020 p.)
0 głosów
2 odpowiedzi 886 wizyt

92,757 zapytań

141,679 odpowiedzi

320,441 komentarzy

62,101 pasjonatów

Motyw:

Akcja Pajacyk

Pajacyk od wielu lat dożywia dzieci. Pomóż klikając w zielony brzuszek na stronie. Dziękujemy! ♡

Oto polecana książka warta uwagi.
Pełną listę książek znajdziesz tutaj.

Akademia Sekuraka

Niedawno wystartował dodruk tej świetnej, rozchwytywanej książki (około 940 stron). Mamy dla Was kod: pasja (wpiszcie go w koszyku), dzięki któremu otrzymujemy 10% zniżki - dziękujemy zaprzyjaźnionej ekipie Sekuraka za taki bonus dla Pasjonatów! Książka to pierwszy tom z serii o ITsec, który łagodnie wprowadzi w świat bezpieczeństwa IT każdą osobę - warto, polecamy!

...