Funkcje w akcji

Transkrypt

Funkcje w akcji
Funkcje i ich wykorzystanie
1 Funkcje i ich wykorzystanie
Ćwiczenie to poświęcone jest tworzeniu programu wykorzystującego podprogramy — funkcje —
jako narzędzia do strukturalizacji i hierarchizacji kodu programu. Program ten pozwala na
trening w stosowaniu funkcji. Mimo iż wykonywane w ramach tych ćwiczeń podprogramy są
proste, pozwalają one na wyrobienie umiejętności systematycznego budowania programów
zawierających większą liczbę linii kodu. Materiał teoretyczny zawarty jest w materiałach
wykładowych.
1.1 Figury płaskie
Należy program pozwalający na obliczanie pól i obwodów wybranych figur płaskich: kwadratu,
prostokąta, koła, trójkąta, trapezu.
Scenariusz działania programu:
1. Program wyświetla informację o jego przeznaczeniu.
2. Program wyświetla menu główne pozwalające na wybór figury, dla której mają być
wykonane obliczenia.
3. Po wybraniu figury, program wczytuje wymagane dane oraz wyświetla obliczone pole i
obwód.
4. Program wraca do menu głównego, pozwalając na powtórzenie obliczeń dla dowolnej
figury.
5. Program kończy swoje działanie po naciśnięciu przez użytkownika wybranego klawisza
zakończenia.
Przykładowa organizacja komunikacji z użytkownikiem
Przykładowy przebieg wykonania programu prezentuje, umieszczony dalej Rysunek 1 oraz
Rysunek 2. Prototypowa wersja kodu programu przedstawiona jest dalej. Program zawiera
przykładową
implementację
funkcji
wczytującej
liczbę
rzeczywistą
(funkcja
wczytaj_liczbe_f).
Wymagania
Program ma być podzielony na procedury i funkcje realizujące spójne funkcjonalnie czynności.
Ilustruje to przedstawiony dalej program przykładowy. Po realizacji pierwszej wersji programu,
opartej na poniższym szablonie, należy koniecznie rozbudować program o elementy opisane w
sekcji Rozszerzenia.
1
Funkcje i ich wykorzystanie
Rysunek 1. Przebieg dialogu z użytkownikiem — menu
główne
Rysunek 2. Obliczenia dla kwadratu i powrót do menu
Rozszerzenia
Proszę rozbudować program tak, by dla każdej figury wyświetlał podmenu, pozwalające
użytkownikowi wybrać, czy obliczone ma być pole czy też obwód. Należy zastosować schemat
analogiczny do zastosowanego w programie głównym. Ponieważ takie podmenu będzie się
powtarzać dla każdej z figur, należy obsługę tego podmenu zrealizować np. w postaci funkcji,
tak, aby w programie nie powtarzać podobnych sekwencji kodu.
Proszę rozszerzyć program o kontrolę poprawności prowadzanych parametrów na etapie ich
wpisywania, tak, aby program nie pozwolił na wprowadzenie zerowej lub ujemnej liczby
określającej np. bok, czy promień. Kontrolowane prowadzanie danych należy zawrzeć w
odpowiednim podprogramie.
Program przykładowy
#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
/* Prototypy wykorzystywanych w programie funkcji */
void wyswietl_menu( void );
void obliczenia_dla_kwadratu( void );
void obliczenia_dla_prostokata( void );
void obliczenia_dla_kola( void );
void obliczenia_dla_trojkata( void );
void obliczenia_dla_trapezu( void );
float wczytaj_liczbe_f( char komunikat[] );
void czyszczenie_bufora( void );
float pole_kwadratu( float bok );
float obwod_kwadratu( float bok );
int main()
2
Funkcje i ich wykorzystanie
{
int klawisz;
puts( "\nObliczam parametry figur plaskich" );
do
{
wyswietl_menu();
klawisz = tolower( getchar() );
fflush( stdin );
switch( klawisz )
{
case '1' : obliczenia_dla_kwadratu();
break;
case '2' : obliczenia_dla_prostokata();
break;
case '3' : obliczenia_dla_kola();
break;
case '4' : obliczenia_dla_trojkata();
break;
case '5' : obliczenia_dla_trapezu();
break;
default : if( klawisz != 'z' )
putchar( '\a' );
break;
}
}
while( klawisz != 'z' );
return EXIT_SUCCESS;
}
void wyswietl_menu( void )
{
puts( "\n1.Kwadrat\n2.Prostokat\n3.Kolo\n4.Trojkat\n5.Trapez" );
printf( "\nWybierz numer figury lub Z by zakonczyc: " );
}
void obliczenia_dla_kwadratu( void )
{
float bok;
printf( "\nObliczenia dla kwadratu" );
bok = wczytaj_liczbe_f( "\nPodaj dlugosc boku: "
);
printf( "Pole : %g\nObwod: %g", pole_kwadratu( bok ), obwod_kwadratu( bok ) );
printf( "\nNacisnij Enter by powrocic do menu..." );
( void )getchar();
}
void obliczenia_dla_prostokata( void )
{
}
void obliczenia_dla_kola( void )
{
}
void obliczenia_dla_trojkata( void )
{
}
3
Funkcje i ich wykorzystanie
void obliczenia_dla_trapezu( void )
{
}
float pole_kwadratu( float bok )
{
return bok * bok;
}
float obwod_kwadratu( float bok )
{
return 4 * bok;
}
float wczytaj_liczbe_f( char komunikat[] )
{
char bufor_tekstowy[ 80 ];
printf( komunikat );
fgets( bufor_tekstowy, 80, stdin ) ;
return atof( bufor_tekstowy );
}
Proszę przeanalizować kod i sposób analogiczny zaprogramować brakujące funkcje. Wiem, że
niektóre podprogramy wyglądają na „naciągane”. Są krótkie, gdyby je wyeliminować, program
stałby się krótszy. Nie to jest jednak naszym celem. Celem jest systematyczne i konsekwentne
stosowanie podprogramów do podziału programu na funkcjonalnie spójne fragmenty oraz do
wyeliminowania powtarzających się fragmentów kodu.
1.2 Funkcje identyfikujące rodzaj znaku
Należy napisać funkcje, pozwalające na identyfikowanie typu znaku przekazanego parametrem.
Rozważmy funkcję:
int is_lower( char c );
Rezultatem funkcji ma być wartość 0, jeżeli znak c nie jest małą literą, lub wartość różną od zera
(np. 1), gdy znak c jest małą literą.
Wykorzystanie funkcji może być następujące:
int main()
{
char znak;
printf( "Wprowadz mala litere; " );
znak = getchar();
if( is_lower( znak ) )
printf( "Wprowadziales mala litere %c", znak );
else
printf( "Nie wprowadziles malej litery!");
. . .
}
Pozostałe funkcje mogą być wykorzystane w analogiczny sposób.
int is_upper( char c );
4
Funkcje i ich wykorzystanie
Rezultatem funkcji ma być wartość 0, jeżeli znak c nie jest dużą literą, lub wartość różną od zera
(np. 1), gdy znak c jest dużą literą.
int is_digit( char c );
Rezultatem funkcji ma być wartość 0, jeżeli znak c nie jest cyfrą dziesiętną, lub wartość różną od
zera (np. 1), gdy znak c jest cyfrą dziesiętną.
int is_oct_digit( char c );
Rezultatem funkcji ma być wartość 0, jeżeli znak c nie jest cyfrą ósemkową, lub wartość różną od
zera (np. 1), gdy znak c jest cyfrą ósemkową.
int is_hex_digit( char c );
Rezultatem funkcji ma być wartość 0, jeżeli znak c nie jest cyfrą szesnastkową, lub wartość różną
od zera (np. 1), gdy znak c jest cyfrą szesnastkową.
1.3 Funkcje zamiany wielkości liter
Należy napisać funkcje, pozwalające na zamianę wielkości znaku, będącego literą, odpowiednio z
dużej na małą, oraz z małej na dużą (zakładamy wykorzystanie kodu ASCII):
char to_lower( char c );
char to_upper( char c );
Jeżeli funkcja to_lower otrzyma w parametrze c znak będący dużą literą, rezultatem funkcji ma
być odpowiednia mała litera. Jeżeli znak c nie jest dużą literą, rezultatem funkcji ma być znak c.
Analogicznie, jeżeli funkcja to_upper otrzyma w parametrze c znak będący małą literą,
rezultatem funkcji ma być odpowiednia duża litera. Jeżeli znak c nie jest małą literą, rezultatem
funkcji ma być znak c.
Należy wykorzystać fakt, że przesunięcie pomiędzy literami dużymi a małymi w kodzie ASCII
wynosi 32 (zobacz materiały wykładowe).
Wykorzystanie funkcji to_lower może być następujące:
int main()
{
char znak;
printf( "Wprowadz mala litere; " );
znak = getchar();
if( ! is_lower( znak ) )
printf( "Nie wprowadziles malej litery!");
else
{
znak = to_upper( znak );
printf( "Duzy ddpowiednik wprowadzonej litery to %c", znak );
}
. . .
}
5
Funkcje i ich wykorzystanie
1.4 Wczytywanie liczby z zakresu
Należy napisać funkcję:
float czytaj_liczbe_z_zakresu( float dol, float gora );
Funkcja odczytuje z klawiatury liczbę rzeczywistą, przy czym pozwala tylko na wprowadzenie
liczby należącej do przedziału domkniętego określonego parametrami dol i gora. Odpytywanie
użytkownika trwa tak długo, aż zostanie wprowadzona liczba z odpowiedniego zakresu. Należy
kontrolować wartości parametrów dol i gora oraz zaproponować metodę sygnalizacji błędu.
1.5 Równanie kwadratowe
Dane jest równanie kwadratowe:
Ax2 + Bx + Cx = 0
Należy napisać funkcje wyznaczające deltę, pojedynczy pierwiastek, pierwiastki podwójne.
Funkcja obliczająca deltę:
float delta( float a, float b, float c );
Funkcja obliczająca pierwiastek podwójny (delta równa zero):
float x0( float a, float b, float c );
Funkcja obliczająca dwa pierwiastki (delta większa od zera):
float x1( float a, float b, float c, float delta );
float x2( float a, float b, float c, float delta );
Proszę napisać program rozwiązujący równanie kwadratowe w oparciu o te funkcje.
1.6 Układ równań liniowych
Dany jest układ równań liniowych
A1x + B1y = C1
A2x + B2y = C2
Rozwiązanie układu równań może polegać na wyliczeniu odpowiednich wyznaczników W, Wx,
Wy a następnie ich ilorazów — zgodnie z informacjami poznanym na zajęciach z matematyki.
Proszę napisać funkcje wyznaczające wartości wyznaczników:
float w( float a1, float b1, float a2, float b2 );
float wx( float c1, float b1, float c2, float b2 );
float wy( float a1, float c1, float a2, float c2 );
Należy zaprojektować i zaimplementować program pozwalający na rozwiązywanie dowolnego
układu takich równań. Program powinien umożliwiać wczytanie współczynników A1, B1, C1, B2,
C2, następnie powinien wyznaczyć rozwiązania równań metodą wyznacznikową. Należy
6
Funkcje i ich wykorzystanie
identyfikować i prawidłowo zareagować na sytuację, gdy układ jest nieokreślony.
1.7 Suplement — problem buforowanego wejścia
Funkcja getchar() wczytuje kolejny znak ze strumienia wejściowego stdin. Na poziomie
biblioteki funkcji identyfikowanej przez nagłówek stdio.h następuje buforowanie informacji.
Można podejrzewać, że buforowanie to jest przyczyną braku natychmiastowej reakcji na
naciśnięcie klawisza, konieczne jest potwierdzenie wprowadzanego znaku naciśnięciem klawisza
Enter. Niestety nawet zakaz buforowania strumienia (zobacz funkcja setbuf) nie przynosi
zwykle rezultatu. Przyczyną jest nałożenie się dwóch mechanizmów buforowania —
wewnętrznego, właściwego dla biblioteki standardowego wejścia/wyjścia oraz buforowania na
poziomie systemu operacyjnego. I to właśnie ten ostatni rodzaj buforowania sprawia problem
przy wczytywaniu danych.
Istnieją dwa sposoby czyszczenia bufora wypełnionego niechcianymi znakami. Jeden
wykorzystuje funkcję fflush, przeznaczoną właśnie do zapisywania buforów systemowych.
Wydaje się, że skutecznym sposobem na wyeliminowanie nieodczytanych jeszcze znaków,
oczekujących w buforze, będzie:
c = getchar();
fflush( stdin );
Rozwiązanie to jest skuteczne i zwykle działa. Dlaczego zwykle? Otóż zastosowanie funkcji
fflush powinno się ograniczać do strumieni otwartych dla zapisu i aktualizacji. Dosłownie:
„The function fflush forces a write of all buffered data for the given output or
update stream via the stream's underlying write function. … .”
Zatem funkcja ta nie powinna działać dla strumienia wejściowego stdin, otwartego dla odczytu.
Dlaczego zatem działa? Implementacje funkcji bibliotecznych powinny być zgodne z ustalonymi
standardami zdefiniowanymi odpowiednio w normach ANSI/ISO. Jednak zdarza się, że norma
nie precyzuje pewnych szczególnych przypadków. Powoduje to, że niektóre implementacje
różnie zachowują się w owych, niedoprecyzowanych sytuacjach. Zobaczmy inny opis funkcji
fflush:
„If the given stream has been opened for writing operations the output buffer is
phisically written to the file. If the stream was open for reading operations the
content of the input buffer is cleared. ... .”
Według tego opisu „wymiecienie” bufora strumienia otwartego do odczytu powoduje skasowanie
zawartości bufora. I o to nam właśnie chodzi. Niestety, pisząc oprogramowanie o
maksymalizowanym stopniu przenośności nie możemy oczekiwać, że pewna funkcja zadziała tak
jak chcemy w zakresie swych cech niezdefiniowanych. Zatem dobrze jest zamiast funkcją fflush
posłużyć się własną funkcją o implementacji pokazanej niżej.
7
Funkcje i ich wykorzystanie
/*---------------------------------------------------------------Funkcja:
void clr_input_buffer()
Przeznaczenie i opis dzialania
Funkcja powoduje wyczyszczenie bufora strumienia stdin, poprzez
Odczytanie wszystkich znakow zapisanych w buforze, lacznie ze
znakiem \n.
Funkcja jest bezparametrowa i nie udostepnia rezultatu
----------------------------------------------------------------*/
void clr_input_buffer( void )
{
while( getchar() != '\n' )
;
}
8

Podobne dokumenty