6. Preprocesor po raz pierwszy
W poprzednim punkcie obiecałem, że napiszemy teraz nasz pierwszy program wyświetlający tekst na ekranie. Zastanówmy się co nam będzie potrzebne do realizacji tego zadania ? Ponieważ program w języku C zbudowany jest z funkcji to potrzebujemy oczywiście odpowiedniej funkcji, która zadanie to wykona. Funkcja taka nazywa się printf . Jak każe tradycja, pierwszy program powinien wyświetlać napis „Hello world !”, nasz nie będzie wyjątkiem:
void main(void)
{
printf(„Hello world !”);
}
Spróbuj zapisać ten program pod nazwą hello.c i skompilować. Zapewne kompilator zgłosił znany już Ci błąd. Zapewne już się domyślasz dlaczego. Występuje tu taka sama sytuacja jak w naszym ostatnim programie. Kompilator nie wie co symbolizuje nazwa printf . Czego brakuje ? Oczywiście prototypu tej funkcji ! Zanim jednak przejdziesz do wyszukania jej prototypu i wpisania go na początku programu przedstawię Ci dyrektywę #include . Pomyśl, funkcji podobnych do printf są setki, jeśli chciałbyś użyć ich w swoim programie musiałbyś wcześniej wpisać prototyp każdej z nich – jaka to strata czasu ! Dlatego producent Twojego kompilatora zrobił to za Ciebie – wpisał wszystkie prototypy funkcji do plików z rozszerzeniem .h (od header – nagłówek), które dostarczył razem z kompilatorem. Jednak nie bój się, nie musisz także pracowicie używać kombinacji „kopiuj i wklej” Twojego edytora – standard języka C udostępnia wspomnianą już wcześniej dyrektywę #include , która pozwala zautomatyzować tą operację. Jej użycie wygląda tak, że na początku programu należy wpisać:
#include <stdio.h>
W nawiasach należy podać nazwę pliku, który kompilator ma sobie dołączyć podczas kompilacji programu. W naszym przypadku jest to stdio.h (skrót od standard input/output – standardowe wejście/wyjście), w którym to właśnie pliku znajduje się prototyp funkcji „printf”. Zauważ także, że linijka ta nie jest zakończona średnikiem. Dodaj teraz podaną linię do programu, skompiluj go i uruchom. Pełen sukces, na ekranie pojawił się napis !
Na zakończenie omawiania tej dyrektywy mam dla Ciebie jeszcze jedną uwagę odnośnie fizycznej lokalizacji pliku nagłówkowego. Skąd kompilator ma wiedzieć, na którym dysku i w którym katalogu znajduje się ten plik ? Otóż każdy kompilator ma z góry określony katalog, w którym się pliki nagłówkowe znajdują. Na ogół jest pliki te znajdują się w katalogu o nazwie include , który z kolei znajduje się w głównym katalogu kompilatora. Jeśli nazwę pliku zawrzesz, tak jak jest przedstawione powyżej, wewnątrz nawiasów to właśnie w tym katalogu kompilator będzie poszukiwał podanego pliku. Jeśli go nie znajdzie to wyświetli błąd. Istnieje także inny sposób użycia tej dyrektywy, a mianowicie nazwę pliku podaje się wewnątrz cudzysłowia, czyli np. tak:
#include „mojplik.h”
Jeśli nazwę pliku określisz w ten sposób to kompilator będzie tego pliku szukał w aktualnie wybranym katalogu.
W punkcie tym omówimy jeszcze jedno z zastosowań innej dyrektywy, a mianowicie „#define”. Pozostałe dyrektywy zostaną omówione w dalszej części kursu (w punkcie „Preprocesor po raz drugi”).
Dzięki dyrektywie #define można zastąpić często używany ciąg znaków za pomocą identyfikatora. Ponieważ dość ciężko jest wytłumaczyć sens takiego postępowania, napiszemy program, który to zilustruje.
Załóżmy, że chcesz napisać program liczący pole powierzchni oraz obwód koła o podanym promieniu. Do obu tych operacji będziesz potrzebował liczby PI . Czyli dwa razy użyjesz w swoim programie sekwencji znaków 3.1415. Będzie to wyglądało tak:
#include <stdio.h>
float ObliczPole(float promien);
float ObliczObwod(float promien);
void main(void)
{
float pole, obwod;
pole = ObliczPole(5);
obwod = ObliczObwod(5);
}
float ObliczPole(float promien)
{
// wzór na pole to PI*R^2
return (3.1415 * promien * promien);
}
float ObliczObwod(float promien)
{
// wzór na obwód to 2*PI*R
return (2 * 3.1415 * promien);
}
Pomyśl teraz, czy nie byłoby wygodniej, gdybyś zamiast każdorazowego wpisywania wartości odpowiadającej liczbie PI , mógł wpisać po prostu PI ? Jeśli myślisz, że to żaden problem to mam dla Ciebie jeszcze jedno pytanie. A co byłoby, gdybyś nagle zapragnął zwiększyć precyzję obliczeń i do określenia liczby PI zamiast czterech, chciałbyś zastosować pięć liczb po przecinku ? Musiałbyś w takim przypadku pracowicie przeszukiwać kod programu i zamienić wszelkie wystąpienia ciągu „3.1415” na „3.14159”. Przy dużym programie o pomyłkę nietrudno. Wszystkie te problemy można rozwiązać właśnie przy pomocy dyrektywy #define . Nasz program z zastosowaniem tej dyrektywy wyglądałby następująco:
#include <stdio.h>
#define PI 3.1415
float ObliczPole(float promien);
float ObliczObwod(float promien);
void main(void)
{
float pole, obwod;
pole = ObliczPole(5);
obwod = ObliczObwod(5);
}
float ObliczPole(float promien)
{
// wzór na pole to PI*R^2
return (PI * promien * promien);
}
float ObliczObwod(float promien)
{
// wzór na obwód to 2*PI*R
return (2 * PI * promien);
}
W trzeciej linijce mamy deklarację naszego symbolu o nazwie PI (zauważ, że wyrażenie to nie jest zakończone średnikiem). Teraz kompilator każde wystąpienie takiego tekstu w kodzie programu zastąpi tekstem „3.1415”. Jeśli teraz chcielibyśmy zwiększyć precyzję obliczeń, wystarczy, że zmienimy tylko tą właśnie linijkę, a cały program będzie już używał tej nowej wartości. Prawda, że wygodne ?