Wprowadzenie do programowania w języku C — struktura programu

Transkrypt

Wprowadzenie do programowania w języku C — struktura programu
Wprowadzenie do programowania
w języku C
Część trzecia
Struktura programu
Autor
Roman Simiński
Kontakt
[email protected]
www.us.edu.pl/~siminski
Niniejsze opracowanie zawiera skrót treści wykładu, lektura tych materiałów nie zastąpi uważnego w nim uczestnictwa.
Opracowanie to jest chronione prawem autorskim. Wykorzystywanie jakiegokolwiek fragmentu w celach innych niż nauka własna jest nielegalne.
Dystrybuowanie tego opracowania lub jakiejkolwiek jego części oraz wykorzystywanie zarobkowe bez zgody autora jest zabronione.
Podstawy i języki programowania
Język C
Struktura programu
Struktura programu jednomodułowego
Problem
Napisać program obliczający obwód i pole
koła o promieniu podanym przez
użytkownika.
Spróbujmy to napisać z wykorzystaniem podprogramów
W języku C nie występuje podział podprogramów na procedury i funkcje. Wszystkie
podprogramy są funkcjami, istnieje jednak możliwość wykorzystywania funkcji jak
procedur, bądź deklarowania funkcji tak, by przypominały procedury.
#include <stdio.h>
#include <stdlib.h>
int main()
{
przywitanie();
Wywołanie funkcji
return EXIT_SUCCESS;
}
int przywitanie( void )
{
puts( "\nCzesc, tu funkcja przywitanie" );
return 0;
}
Copyright © Roman Simiński
Definicja funkcji
Strona : 2
Podstawy i języki programowania
Język C
Struktura programu
Struktura programu jednomodułowego — funkcje
Czy testowy program się skompiluje bez problemów?
Tak, bez błędów i ostrzeżeń — w starszych wersjach kompilatorów.
Tak, bez błędów lecz z ostrzeżeniem — w nowszych wersjach kompilatorów, oraz tych
pracujących w trybie zgodności z normą ANSI.
Nie, wystąpi błąd — w kompilatorach pracujących w trybie C++.
Dlaczego występują te rozbieżności?
Definicja funkcji przywitanie występuje po jej wywołaniu. Kompilator na etapie
wywołania jej jeszcze nie zna. Czyni w stosunku do niej założenia — że to funkcja, której
rezultatem jest wartość int. To założenie może być słusznie albo nie.
Aby uniknąć niejednoznaczności, wprowadza się prototypy funkcji.
Aby kompilator mógł kontrolować poprawność wywołania funkcji, należy to wywołanie
poprzedzić definicją lub deklaracją wywoływanej funkcji. Deklaracja przyjmuje postać
prototypu funkcji.
Definicja funkcji
int przywitanie( void )
{
puts( "\nCzesc, tu funkcja przywitanie" );
return 0;
}
Copyright © Roman Simiński
Prototyp funkcji
int przywitanie( void );
Strona : 3
Podstawy i języki programowania
Język C
Struktura programu
Struktura programu jednomodułowego — funkcje
Ogólna postać definicji funkcji:
<typ_rezultatu> nazwa_funkcji ( lista_parametrów_formalnych )
{
ciało_funkcji
}
Ogólna postać prototypu funkcji:
<typ_rezultatu> nazwa_funkcji ( lista_parametrów_formalnych );
Dwie wersje poprawnego programu przykładowego
Z prototypem:
Po „paskalowemu”:
. . .
int przywitanie( void );
int main()
{
przywitanie();
return EXIT_SUCCESS;
}
int przywitanie( void )
{
. . .
}
Copyright © Roman Simiński
. . .
int przywitanie( void )
{
. . .
}
int main()
{
przywitanie();
return EXIT_SUCCESS;
}
Strona : 4
Podstawy i języki programowania
Język C
Struktura programu
Struktura programu jednomodułowego — funkcje
Dlaczego przywitanie to funkcja, skoro nie udostępnia sensownego rezultatu?
int przywitanie( void )
{
puts( "\nCzesc, tu funkcja przywitanie" );
return 0;
}
Może lepiej tak, czyli jak zrobić z funkcji procedurę:
void przywitanie( void )
{
puts( "\nCzesc, tu funkcja przywitanie" );
}
Ale może być kłopot
int main()
{
przywitanie();
return EXIT_SUCCESS;
}
void przywitanie( void )
{
puts( "\nCzesc, tu funkcja przywitanie" );
}
Copyright © Roman Simiński
Kompilator zakłada, że funkcja
będzie miała rezultat int.
Strona : 5
Podstawy i języki programowania
Język C
Struktura programu
Struktura programu jednomodułowego — funkcje, podsumowanie
Podsumowanie informacji o prototypach
Starsze implementacje C dopuszczały wywoływanie funkcji wcześniej kompilatorowi
nieznanych.
W trakcie kompilowania wywołania nieznanej funkcji przez domniemanie
przyjmowano, że zwraca ona wartość int i nic nie wiadomo na temat jej parametrów.
Nie pozwalało to kompilatorowi kontrolować poprawności wywołania funkcji.
Aby kompilator mógł kontrolować poprawność wywołania funkcji, należy to
wywołanie poprzedzić definicją lub deklaracją wywoływanej funkcji.
Deklaracja przyjmuje postać prototypu funkcji.
Deklaracja i definicja funkcji powinna być zgodna. Jeżeli w obrębie jednego pliku
wystąpi niezgodność, kompilator zgłosi błąd kompilacji.
Copyright © Roman Simiński
Strona : 6
Podstawy i języki programowania
Język C
Struktura programu
Struktura programu jednomodułowego — funkcje, podsumowanie
Uwagi na temat definicji funkcji:
Słowo kluczowe void, będące nazwą typu, oznacza brak, nieobecność jakiejkolwiek
wartości.
Jeżeli typem rezultatu będzie typ określany słowem kluczowym void, to oznacza, iż
funkcja nie udostępnia rezultatu – staje się wtedy czymś podobnym do procedury
z języka Pascal.
Jeżeli w miejscu listy parametrów formalnych występuje słowo kluczowe void, to
oznacza, że funkcja nie posiada parametrów.
Jeżeli funkcja „zwraca wartość” w miejscu wywołania, w ciele funkcji powinna
wystąpić instrukcja return, a po niej, wyrażenie o typie zgodnym z typem rezultatu
funkcji.
Na liście parametrów formalnych, dla każdego parametru określamy jego typ.
Copyright © Roman Simiński
Strona : 7
Podstawy i języki programowania
Język C
Struktura programu
Struktura programu jednomodułowego — obliczanie obwodu i pola koła
A może by tak w końcu wrócić do programu, który mieliśmy napisać...
#include <stdio.h>
#include <stdlib.h>
#define PI 3.14
#define MAKS_DL 80
void oblicz( void )
{
float r;
char s[ MAKS_DL ];
printf("\nPodaj promien R = " );
fgets( s, MAKS_DL, stdin );
r = atof( s );
void komunikat_wstepny( void );
void oblicz( void );
float obwod_kola( float r );
float pole_kola( float r );
int main()
{
komunikat_wstepny();
oblicz();
return EXIT_SUCCESS;
}
void komunikat_wstepny( void )
{
puts("\nObliczam obwod ...");
}
Copyright © Roman Simiński
printf( "Obwod : %g\n", obwod_kola(r) );
printf( "Pole : %g\n", pole_kola(r) );
printf( "\nNacisnij Enter by ..." );
getchar();
}
float obwod_kola( float r )
{
return 2 * PI * r;
}
float pole_kola( float r )
{
return PI * r * r;
}
Strona : 8
Podstawy i języki programowania
Język C
Struktura programu
Struktura programu jednomodułowego — przykłady funkcji
Kilka prostych funkcji
void print_message( void )
{
puts( "\nObliczam pole kola" );
}
int zawsze_5( void ) /* Wiem, że to bez sensu :-\ */
{
return 5;
}
float do_kwadratu( float a )
{
return a * a;
}
float oblicz_spalanie( float paliwo, float dystans )
{
return ( paliwo * 100 ) / dystans;
}
Copyright © Roman Simiński
Strona : 9
Podstawy i języki programowania
Język C
Struktura programu
Struktura programu jednomodułowego — przykłady funkcji
Zamiast
. . .
printf("\nPodaj promien R = " );
fgets( s, MAKS_DL, stdin );
r = atof( s );
printf( "Obwod : %g\n", obwod_kola(r) );
printf( "Pole : %g\n", pole_kola(r) );
. . .
A może tak:
. . .
printf("\nPodaj promien R = " );
r = wczytaj_liczbe_f();
printf( "Obwod : %g\n", obwod_kola( r ) );
printf( "Pole : %g\n", pole_kola( r ) );
. . .
Przykład użytecznej funkcji
float wczytaj_liczbe_f( void )
{
char bufor_tekstowy[ 80 ];
fgets( bufor_tekstowy, 80, stdin );
return atof( bufor_tekstowy );
}
Copyright © Roman Simiński
Strona : 10
Podstawy i języki programowania
Język C
Struktura programu
Struktura programu jednomodułowego — przykłady funkcji
Zamiast
. . .
printf("\nPodaj promien R = " );
r = wczytaj_liczbe_f();
printf( "Obwod : %g\n", obwod_kola( r ) );
printf( "Pole : %g\n", pole_kola( r ) );
. . .
A może tak:
. . .
r = wczytaj_liczbe_f( "\nPodaj promien R = " );
printf( "Obwod : %g\n", obwod_kola( r ) );
printf( "Pole : %g\n", pole_kola( r ) );
. . .
Przykład użytecznej funkcji — wersja kolejna
float wczytaj_liczbe_f( char komunikat[] )
{
char bufor_tekstowy[ 80 ];
printf( komunikat );
fgets( bufor_tekstowy, 80, stdin );
return atof( bufor_tekstowy );
}
Copyright © Roman Simiński
Strona : 11
Podstawy i języki programowania
Język C
Struktura programu
Struktura programu jednomodułowego — przykłady funkcji
Przykład wykorzystania rezultatu do sygnalizacji błędów
float oblicz_spalanie( float paliwo, float dystans )
{
if( dystans == 0 )
return –1;
else
return fabs( ( paliwo * 100 ) / dystans );
}
. . .
spalanie = oblicz_spalanie( 40.5, 0 );
if( spalanie == -1 )
printf( "Nie dokonam obliczeń dla błędnych danych" );
else
printf( "Spalanie %4.2f l na 100 km", spalanie );
. . .
Uwaga na instrukcję return
float oblicz_spalanie( float paliwo, float dystans )
{
if( dystans == 0 )
printf( "Nie dokonam obliczeń dla zerowego dystansu" );
else
return ( paliwo * 100 ) / dystans;
}
Copyright © Roman Simiński
// return ???
Strona : 12
Podstawy i języki programowania
Język C
Struktura programu
Struktura programu jednomodułowego — uwagi
Parę pułapek związanych z definicjami funkcji
W języku C wolno czasem opuścić słowo kluczowe int, wtedy zostanie ono przyjęte
domyślnie. To, że tak robić można nie oznacza, że tak robić trzeba.
Zapisy równoważne
int fun()
{
. . .
}
fun()
{
. . .
}
Nawiasy po nazwie funkcji są konieczne, nawet gdy funkcja nie ma parametrów.
Nawiasy występują zarówno przy definicji, deklaracji jak i przy wywołaniu funkcji.
Jednak puste nawiasy wbrew pozorom nie oznaczają braku parametrów a ich
nieokreśloną liczbę! Trzy następujące po sobie kropki ... oznaczają zmienną liczbę
parametrów, jakie można przekazać funkcji.
Zapisy równoważne
int fun()
{
. . .
}
int fun( ... )
{
. . .
}
Dozwolone wywołania funkcji fun:
fun(); fun( 5 ); fun( "Ala" ); fun( 2 * a ); fun( "Ala" , "As" );
Copyright © Roman Simiński
Strona : 13
Podstawy i języki programowania
Język C
Struktura programu
Struktura programu jednomodułowego — uwagi
Funkcja printf jest przykładem funkcji o zmiennej liczbie parametrów:
printf( "Witaj w programie" );
printf( "Pole kola %g, jego obwod %g", pole_kola( r ), obwod_kola( r ) );
printf( "Spalanie %4.2f l na 100 km", ( paliwo * 100 ) / dystans );
W języku C każdy programista może pisać własne funkcje ze zmienną liczbą
parametrów, wymaga to zastosowania odpowiednich makr – zostanie to omówione
później.
Słowo kluczowe void oznacza brak, nieobecność jakiejkolwiek wartości.
void fun( void )
{
. . .
}
Copyright © Roman Simiński
Zapis oznacza, że funkcja fun nie udostępnia
żadnej wartości, oraz nie otrzymuje żadnych
parametrów.
Strona : 14
Podstawy i języki programowania
Język C
Struktura programu
Struktura programu jednomodułowego — uwagi
W języku C wszystkie funkcje są zewnętrzne
void oblicz_pole_kola( void )
{
float r;
char s[ MAKS_DL ];
printf("\nPodaj promien R = " );
gets( s );
r = atof( s );
printf( "Pole kola wynosi %g", pole_kola( r ) );
}
float pole_kola( float r )
{
return PI * r * r;
}
Procedure ObliczPoleKola;
Var
R : Real;
Function PoleKola : Real; { Funkcja nielokalna }
Const
Pi = 3.14;
Begin
PoleKola := Pi * R * R;
End;
Begin
WriteLn;
WriteLn( ”Podaj promień R: ” );
ReadLn( R );
WriteLn( ”Pole wynosi: ”, PoleKola );
End;
Copyright © Roman Simiński
Strona : 15
Podstawy i języki programowania
Język C
Struktura programu
Deklaracje i definicje zmiennych ― klasy pamięci
Zmienne klasy auto — zmienne automatyczne
void fun( float a )
{
int
i = 0;
char c = 'A';
float f;
if( i == 0 )
{
float i = 100.0;
int
k;
. . .
}
Przesłanianie identyfikatorów ― w obrębie
tego bloku instrukcji if nazwa i oznacza,
lokalną w tym bloki, zmienną typu float.
}
Zmienne klasy auto mogą być definiowane na początku każdego bloku (standard C89
języka C, w standardzie C99 i w języku C++ jest inaczej).
Zmienne klasy auto pojawiają się i znikają wraz z wejściem i wyjściem sterowania do
bloku w którym są zadeklarowane.
Zmienne deklarowane wewnątrz bloku są automatycznymi, jeżeli nie podano klasy
pamięci albo jawnie użyto specyfikatora auto.
Copyright © Roman Simiński
Strona : 16
Podstawy i języki programowania
Język C
Struktura programu
Deklaracje i definicje zmiennych ― klasy pamięci
Zmienne klasy auto — zmienne automatyczne,cd...
#include <stdio.h>
#include <stdlib.h>
void fun( float a )
{
int i = 0;
int j;
printf( "\na = %.2f | i = %d | j = %d", a, i, j );
}
int main()
{
fun( 1.0 );
fun( 2.0 );
fun( 3.0 );
return
EXIT_SUCCESS;
}
Zmienne klasy auto:
Nie zachowują swoich wartości pomiędzy swoimi kolejnymi kreacjami.
O ile nie zostaną zainicjalizowane, maja wartości przypadkowe.
Parametry formalne funkcji też są klasy auto.
Copyright © Roman Simiński
Strona : 17
Podstawy i języki programowania
Język C
Struktura programu
Deklaracje i definicje zmiennych ― klasy pamięci
Gdzie w pamięci operacyjnej lokowane są zmienne automatyczne?
Zmienna klasy auto tworzone są automatycznie, lokowane są na stosie.
Stos to element procesu, służący do przechowywania danych chwilowych.
Na stosie lokowane są zmienne auto, w tym argumenty funkcji, oraz adresy powrotu
dla wywoływanych podprogramów.
Stos ma ustalony i ograniczony rozmiar ― należy sprawdzić ustalenia rozmiaru stosu
w opcjach kompilatora (konsolidatora). Może się zdarzyć, że stos ma rozmiar rzędu
kilku kilobajtów.
Wobec powyższego, niebezpieczna może być poniższa definicja dużej tablicy:
void fun( void )
{
float tab[ 10000 ];
. . .
}
Uwaga: użycie słowa kluczowego auto dla deklaracji zmiennej, która jest domyślnie
właśnie klasy auto, jest bezcelowe:
{
{
auto int i;
}
Copyright © Roman Simiński
int i;
}
Strona : 18
Podstawy i języki programowania
Język C
Struktura programu
Deklaracje i definicje zmiennych ― klasy pamięci
Zmienne klasy static — zmienne statyczne
#include <stdio.h>
void fun_auto( void )
{
int i = 1;
printf( "\nAuto
%d", i++ );
}
void fun_static( void )
{
static int i = 1;
printf( "\nStatic %d\n", i++ );
}
Operator ++ zwiększa o 1 wartość
zmiennej przy której występuje.
Zatem, zapis:
i++
równoważny jest:
i = i + 1
int main()
{
fun_auto();
fun_static();
fun_auto();
fun_static();
fun_auto();
fun_static();
. . .
}
Copyright © Roman Simiński
Strona : 19
Podstawy i języki programowania
Język C
Struktura programu
Deklaracje i definicje zmiennych ― klasy pamięci
Zmienne klasy static — zmienne statyczne, cd...
Zmienne statyczne mogą być lokalne w bloku lub zewnętrzne dla wszystkich bloków.
Jeżeli zmienna wewnątrz bloku zostanie zadeklarowana ze specyfikatorem static, to:
✔
jest raz inicjowana wartością inicjalizatora lub nadawana jest jej wartość zerowa
odpowiednio do typu.
✔
przechowuje wartość po opuszczeniu i ponownym wejściu do bloku.
Statyczne zmienne lokalne stanowią prywatną, nieulotną pamięć danej funkcji czy
bloku.
Szkic przykładowego zastosowania zmiennej statycznej ― funkcja z limitem wywołań
void fun_z_limitem_wywolan( void )
{
static int licznik_wywolan = 0;
if( licznik_wywolan < 10 )
{
licznik_wywolan++;
/* Tutaj odpowiednie instrukcje */
}
else
puts( "Wersja demo -- limit wywolan wyczerpany" );
}
Copyright © Roman Simiński
Strona : 20
Podstawy i języki programowania
Język C
Struktura programu
Deklaracje i definicje zmiennych ― klasy pamięci
Zmienne klasy register — zmienne rejestrowe
Deklaracja zmiennej jako register jest równoważna z deklaracją auto, ale
wskazuje że deklarowany obiekt będzie intensywnie wykorzystywany, i w miarę
możliwości będzie umieszczony w rejestrze procesora.
Jeżeli nie jest możliwe umieszczenie zmiennej w rejestrze, pozostaje ona w pamięci.
Zmienne rejestrowe pozwalają zredukować zajętość pamięci i poprawić szybkość
wykonania operacji takie zmienne wykorzystujących.
Jednak większość współczesnych kompilatorów wykorzystuje optymalizację
rejestrową, zatem wiele zmiennych i tak przechowywanych jest w rejestrach, mimo
braku jawnej specyfikacji jako register.
register i;
for( i = 0; i < 10; i++ )
{
. . .
}
int very_time_critical_fun( register int i, register char c )
{
. . .
}
Copyright © Roman Simiński
Strona : 21
Podstawy i języki programowania
Język C
Struktura programu
Deklaracje i definicje zmiennych ― klasy pamięci
Zmienne klasy extern — zmienne zewnętrzne
#include <stdio.h>
#include <stdlib.h>
float dystans, paliwo;
void komunikat_wstepny( void )
{
. . .
}
int main()
{
komunikat_wstepny();
czytaj_dane();
pisz_wyniki();
return EXIT_SUCCESS;
}
void czytaj_dane( void )
{
. . .
dystans = fabs( atof( linia ) );
. . .
paliwo = fabs( atof( linia ) );
}
void pisz_wyniki( void )
{
if( dystans == 0 )
printf( "Nie policze spalania dla zerowego dystansu" );
else
printf( "Spalanie %4.2f l na 100 km", ( paliwo * 100 ) / dystans );
}
Copyright © Roman Simiński
Strona : 22
Podstawy i języki programowania
Język C
Struktura programu
Deklaracje i definicje zmiennych ― klasy pamięci
Zmienne klasy extern — zmienne zewnętrzne, cd...
Zmienne zewnętrzne deklarowane są na zewnątrz wszystkich funkcji. Zasięg
zmiennej zewnętrznej rozciąga się od miejsca deklaracji do końca tego pliku.
Zmienne zewnętrzne istnieją stale, nie pojawiają się i nie znikają, zachowują swoje
wartości i są dostępne dla wszystkich funkcji programy występujących w zakresie
danej zmiennej.
Zmienna zewnętrzna jest raz inicjowana wartością inicjalizatora lub nadawana jest
jej wartość zerowa odpowiednio do typu.
Jeżeli dla zmiennej zewnętrznej użyjemy specyfikacji static, to oznacza to
uprywatnienie (ograniczenie dostępu) w obrębie danego pliku źródłowego.
Zmienne zewnętrzne oraz ich właściwe definiowanie i deklarowanie mają istotne
znaczenie przy organizacji programów wielomodułowych.
Delikatne kwestie ― definicja a deklaracja
extern int i;
Deklaracja zmiennej jako extern nie
powoduje przydziału pamięci i może
występować w programie wiele razy.
Copyright © Roman Simiński
int i;
Definicja zmiennej, powodująca
przydzielenie pamięci.
Strona : 23
Podstawy i języki programowania
Język C
Struktura programu
Deklaracje i definicje zmiennych ― przekazywanie parametrów
W języku C parametry przekazywane są przez wartość
Każda funkcja może posiadać parametry — na etapie definicji funkcji określa się ich
liczbę, typy i nazwy.
Parametry te nazywane są parametrami formalnymi funkcji.
float pole_trojkata( float a, float h )
/* Definicja funkcji */
{
return 0.5 * a * h;
a i h to nazwy parametrów formalnych
}
funkcji
Każdą funkcje można wywołać. Na etapie wywołania funkcji określa się konkretne
wartości jakie maja przyjąć parametry formalne.
Wartości, z którymi jest wywoływana funkcja nazywane są parametrami aktualnymi
wywołania funkcji.
float podstawa = 10, wysokosc = 5, pole;
pole = pole_trojkata( podstawa, wysokosc );
pole = pole_trojkata( 20, 10 );
Copyright © Roman Simiński
podstawa i wysokosc oraz 20
i 10 to parametry aktualne
Strona : 24
Podstawy i języki programowania
Język C
Struktura programu
Deklaracje i definicje zmiennych ― przekazywanie parametrów
W języku C parametry przekazywane są przez wartość
W języku C obowiązuje zasada przekazywania parametrów przez wartość.
Zakład ona, iż na etapie wywołania funkcji wartość parametru aktualnego
kopiowana jest do odpowiedniego parametru formalnego.
Parametr formalny i aktualny są od siebie niezależne i, po przekazaniu wartości, nie
występuje pomiędzy nimi żadne powiązanie.
W języku C nie występuje jawnie przekazywanie parametrów przez zmienną.
Copyright © Roman Simiński
Strona : 25
Język C
Podstawy i języki programowania
Struktura programu
Ilustracja przekazywania parametrów przez wartość
a -- to parametr formalny funkcji inc
i -- to parametr aktualny wywołania funkcji inc
Stan pamięci
operacyjnej
i
5
a
5
Kopiowanie wartości parametru
aktualnego i do parametru formalnego a
Wywołanie funkcji inc
Wykonanie funkcji inc
Stan pamięci
operacyjnej
i
a
5
6
5
a++
Inkrementacja parametru
formalnego a
Copyright © Roman Simiński
Po wykonaniu funkcji inc
Stan pamięci
operacyjnej
i
5
a
6
Parametr a będący klasy
auto jest usuwany
Strona : 26