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.