[ Pobierz całość w formacie PDF ]

4
Zresztą większość funkcji bibliotecznych jast napisana w C
III Formatowane wejście/wyjście 33
czane są wartości kolejnych argumentów. Klasyczna funkcja, wiedząc ile
i jakiego typu argumentów posiada, może odpowiednio zinterpretować zawar-
tość stosu. Proszę zwrócić uwagę, jak ważna jest znajomość liczby i typów ar-
gumentów. Bez tej informacji funkcja nie jest w stanie w żaden sposób
stwierdzić, które wartości na stosie są jej argumentami i co one oznaczają. Przy-
kładowo niemożliwe jest stwierdzenie, czy dwa kolejne bajty o wartościach 65
i 0 reprezentują znak 'A', czy też może są fragmentem jakiejś liczby zmienno-
przecinkowej. Dlatego też dla "normalnej" funkcji należy zadeklarować liczbę
i typy jej parametrów. Kompilator sprawdza, czy liczba argumentów
w wywołaniu funkcji jest zgodna z definicją funkcji a także przeprowadza od-
powiednie konwersje argumentów na podstawie typów parametrów formalnych.
Rozważmy następujący przykład:
void fun(float f)
{ /*...*/ }
/*...*/
int I=1;
fun(I);
W wywołaniu funkcji fun jako argument podana jest zmienna typu int, która jest
zapisana w dwóch bajtach pamięci (dla uproszczenia zakładam, że typ int jest re-
prezentowany dwoma bajtami, choć może oczywiście być inaczej). Kompilator na
podstawie definicji funkcji fun "wie" jednak, że "spodziewa" się ona wartości ty-
pu float. Gdyby wygenerowany kod umieścił na stosie tylko dwa bajty (1 i 0), to
funkcja fun, odczytując liczbę zmiennoprzecinkową, otrzymałaby wartość przy-
padkową (po pierwsze odczytałaby więcej bajtów niż zostało umieszczone na sto-
sie, a po wtóre liczby typu float mają inną reprezentację wewnętrzną niż liczby
typu int). W rzeczywistości kompilator, wiedząc o tym, że argument funkcji ma
być typu float, wygeneruje kod dokonujący konwersji wartości zmiennej I na po-
stać zmiennoprzecinkową i umieszczający na stosie odpowiednią ilość bajtów. Jest
to właśnie automatyczna konwersja argumentów do typu odpowiedniego parame-
tru formalnego.
Kompilator języka C wykonuje jeszcze jedną automatyczną konwersję. Zmienne
typu char które są jednobajtowe i tyle miejsca zajmują normalnie w pamięci, są
automatycznie konwertowane na wartości typu int. Konwersja ta wykonywana
jest także wtedy, gdy funkcja ma zadeklarowany parametr formalny typu char.
Efekt ten ilustruje następujący program:
#include
void fun(char a,char b)
{
printf("%d\n",&b-&a);
}
char x,y;
void main()
34 Wgłąb języka C
{
printf("%d\n",&y-&x);
fun(x,y);
}
W wyniku działania tego programu wypisane zostaną różnice adresów dwóch
sąsiednich zmiennych globalnych typu char i dwóch argumentów typu char.
Ponieważ globalne zmienne znakowe zajmują jeden bajt, w pierwszym wywołaniu
funkcji printf wypisana zostanie wartość 1. W drugim wywołaniu funkcji printf,
z wnętrza funkcji fun, wypisana zostanie jednak wartość 2, bo argumenty typu
char zajmują dwa bajty - tyle, ile wartości typu int.
Fakt, że argumenty typu char zajmują dwa bajty a nie jeden, jak zmienne typu
char, nie ma zwykle dla programisty znaczenia. Należy jednak o nim pamiętać,
ponieważ czasami może być powodem bardzo trudnych do wykrycia błędów. Jako
ostrzeżenie chciałbym przytoczyć błąd jaki popełnili programiści firmy Microsoft.
Błąd ten znalazł się w standardowym pliku "stdarg.h" dołączonym do kompila-
tora Microsoft C 5.1. Plik "stdarg.h", opisany dokładniej w dalszej części tego
rozdziału, zawiera makrodefinicje udostępniające argumenty funkcji o zmiennej
liczbie argumentów. W kompilatorze Microsoft makrodefinicje te działają niepo-
prawnie, jeżeli ostatni obowiązkowy argument jest typu char. Błąd ten spowodo-
wany jest właśnie nieuwzględnieniem faktu, że argumenty typu char zajmują dwa
bajty (podczas gdy operator sizeof dla takiego argumentu da wartość 1). Zachę-
cam Czytelników mających dostęp do tego kompilatora, do przestudiowania za-
wartości pliku "stdarg.h" i przetestowania błędnego działania makrodefinicji.
W niektórych kompilatorach, np. w wymienionym wcześniej Microsoft C 5.1,
nie tylko argumenty znakowe ale także lokalne zmienne typu char zajmują dwa
bajty.
Podobnie jak konwersje typu char na int, niektóre kompilatory mogą przepro-
wadzać konwersję argumentu typu float na double. Konwersja ta nie jest bez-
względnie przestrzegana, tak więc różne kompilatory mogą się różnie
zachowywać. Obowiązkowo jest ona jednak wykonywana na argumentach funkcji
o zmiennej liczbie argumentów. Gdyby nie była ona przeprowadzana, nie mogłaby [ Pobierz całość w formacie PDF ]

  • zanotowane.pl
  • doc.pisz.pl
  • pdf.pisz.pl
  • supermarket.pev.pl
  •