Środowisko programisty/Wprowadzenie do C
Skąd wziąć kompilator języka C
Do pisania programów w C potrzebne są:
- Edytor tekstów. Może być dowolny, chociaż niekoniecznie najmądrzejszym posunięciem będzie pisanie w Wordzie.
- Kompilator C.
Można zakupić jeden z wielu komercyjnych kompilatorów języka C np:
albo użyć jednego z darmowych kompilatorów. Podczas tego kursu będziemy używać gcc, którego windowsową wersją jest DJGPP.
Linux
Instalacja gcc pod Linuksem nie nastręcza zazwyczaj trudności, wystarczy: uruchomić menedżer pakietów, odnaleźć w nim gcc i zaznaczyć go do instalacji.
Windows
Pod Windows należy:
- Wejść do repozytorium pakietów, którego adres znajduje się na stronie DJGPP.
- Ściągnąć co najmniej pliki: djdev???.zip, bnu?????.zip i gcc????.zip. (???? oznaczają numer wersji, który się od czasu do czasu zmienia).
- Utworzyć katalog w którym ma zostać zainstalowane DJGPP (np. c:\djgpp).
- Rozpakować wszystkie pliki *.zip.
- Ustawić zmienną środowiskową DJGPP (np. w autoexec.bat), na nazwę pliku konfiguracyjnego (zazwyczaj C:/DJGPP/DJGPP.ENV), należy pamiętać aby w wartości zmiennej używać znaków / zamiast \.
- Do zmiennej środowiskowej PATH dodać katalog zawierający pliki wykonywalne (zazwyczaj C:\DJGPP\BIN)
Pomocny w instalacji DJGPP może być internetowy instalator
Kompilacja i uruchamianie programów
Przykładowy program
Aby uruchomić program w C trzeba go najpierw napisać. Programy w C piszemy w dowolnym edytorze tekstu i zapisujemy w pliku o zwyczajowo przyjętym rozszerzeniu .c. Przyjrzyjmy się następującemu programowi:
#include <stdio.h> int main() { printf("Raz\n"); return 0; }
Program ten wypisuje na ekran napis Raz po czym kończy swoje działanie.
Kompilacja
Spróbujmy skompilować nasz program. Załóżmy, że nazwaliśmy go (to typowe) test.c. Uruchamiamy w shellu polecenie gcc i co się dzieje:
krzysiek@krzysiek-desktop:~/Tmp$ gcc test.c krzysiek@krzysiek-desktop:~/Tmp$
Wbrew pozorom, to że system nic nie wypisał na ekran, nie oznacza niczego złego. Po prostu program się poprawnie skompilował, ale nie wiemy jak go uruchomić. Sprawdźmy, za pomocą polecenia ls, jakie inne pliki powstały w katalogu:
krzysiek@krzysiek-desktop:~/Tmp$ ls a.out test.c krzysiek@krzysiek-desktop:~/Tmp$
Kompilator gcc domyślnie tworzy plik wykonywalny o nazwie a.out, a dla nas wygodniej by było gdyby nazywał się on jakoś inaczej, co uzyskujemy za pomocą opcji -o kompilatora. Czyli:
krzysiek@krzysiek-desktop:~/Tmp$ rm a.out krzysiek@krzysiek-desktop:~/Tmp$ gcc test.c -o test krzysiek@krzysiek-desktop:~/Tmp$ ls test test.c krzysiek@krzysiek-desktop:~/Tmp$
No to teraz uruchamiamy nasz program:
krzysiek@krzysiek-desktop:~/Tmp$ test krzysiek@krzysiek-desktop:~/Tmp$
Hmm... nic się nie stało. Dlaczego? Ano dlatego, że test jest wbudowanym poleceniem shella i aby uruchomić nasz program, potrzebujemy poprzedzić jego nazwę ścieżką:
krzysiek@krzysiek-desktop:~/Tmp$ ./test Raz krzysiek@krzysiek-desktop:~/Tmp$
W ten sposób napisaliśmy i uruchomiliśmy nasz pierwszy program w C
Błędy kompilacji
Co by się stało, gdyby nasz program miał jakieś błędy? Sprawdźmy.
Najpierw błąd, który jest niegroźny. W naszym programie używamy funkcji printf, a po to by jej użyć dołączamy plik nagłówkowy stdio.h, w którym ta funkcja jest opisana. Co się stanie, jeśli nie dołączymy tego pliku?
int main() { printf("Raz\n"); return 0; }
Spróbujmy skompilować i uruchomić ten program:
krzysiek@krzysiek-desktop:~/Tmp$ rm test krzysiek@krzysiek-desktop:~/Tmp$ gcc test.c -o test test.c: In function ‘main’: test.c:3: warning: incompatible implicit declaration of built-in function ‘printf’ krzysiek@krzysiek-desktop:~/Tmp$ ./test Raz krzysiek@krzysiek-desktop:~/Tmp$
Kompilator ostrzegł nas (warning), że w linii trzeciej pliku test.c jest problem polegający na użyciu "nieznanej" funkcji printf, ale plik został skompilowany i program można uruchomić.
Weźmy teraz następujący przykład:
#include <stdio.h> int main() { printf("Raz\n"); }
Brak w nim instrukcji return, która powinna kończyć działanie funkcji main. Skompilujmy ten program:
krzysiek@krzysiek-desktop:~/Tmp$ gcc test.c -o test krzysiek@krzysiek-desktop:~/Tmp$
Kompilator nie zgłosił żadnych błędów ani ostrzeżeń, ale program ma wadę. Dzieje się tak dlatego, że ostrzeżenia zostały podzielone na kilka kategorii, z których domyślnie zgłaszane są jedynie najważniejsze. Aby wyświetlić wszystkie ostrzeżenia należy użyć opcji -Wall.
krzysiek@krzysiek-desktop:~/Tmp$ gcc -Wall test.c -o test test.c: In function ‘main’: test.c:5: warning: control reaches end of non-void function krzysiek@krzysiek-desktop:~/Tmp$
Na koniec program z błędem:
#include <stdio.h> int main() { printf("Raz\n); return 0; }
brakuje w nim znaku " po \n
krzysiek@krzysiek-desktop:~/Tmp$ gcc test.c -o test test.c: In function ‘main’: test.c:4: error: missing terminating " character test.c:5: error: syntax error before ‘return’ krzysiek@krzysiek-desktop:~/Tmp$
W tym przypadku program nie został skompilowany, więc nie można go uruchomić, a kompilator zgłosił błąd w czwartej linii (missing terminating " character).
Zmienne
Zmienne w C definiuje się podając ich nazwę poprzedzoną typem.
int a;
To jest deklaracja zmiennej a o typie int. Podstawowe typy proste w C to:
Nazwa | Znaczenie |
---|---|
char | pojedynczy znak |
int | liczba całkowita (typowo cztery bajty) |
double | liczba rzeczywista (typowo osiem bajtów) |
Deklarując zmienne można podać ich wartości początkowe, można deklarować tabele oraz wiele zmiennych w jednej instrukcji:
int b = 10, tabela[20];
Jeżeli zmienna jest zadeklarowana wewnątrz funkcji, to jest to zmienna lokalna i jest widoczna tylko wewnątrz tej funkcji, w preciwnym wypadku zmienna jest globalna.
Pętle
Do wielokrotnego wykonywania tego samego fragmentu programu w języku C można wykorzystać pętle.
while
Pętla while ma następują składnię:
while (warunek) { instrukcje }
Pętla while działa w następujący sposób:
- Najpierw jest obliczana wartość logiczna warunku, czyli sprawdzane jest czy warunek jest spełniony.
- Jeśli warunek jest spełniony, to wykonywane są instrukcje, po czym ponownie sprawdzane jest czy warunek jest spełniony.
- Dzieje się tak, aż do momentu, w którym warunek przestaje być spełniony.
Aby zrozumieć dokładnie działanie pętli while, przyjrzyjmy się następującemu kodowi:
int i; int j; i = 1; j = 0; while (i < 3) { i = i + 2; j = j + i; i = i - 1; }
- Przed wejściem do pętli, zmienne i oraz j mają odpowiednio wartości 1 i 0.
- Po pierwszym przejściu pętli i oraz j mają wartości 2 i 3.
- Po drugim przejściu pętli i oraz j mają wartości 3 i 7, a więc nie jest spełniony warunek (i < 3), i program nie wejdzie do pętli po raz kolejny.
Zwróćmy uwagę na to, że w trakcie pierwszego przejścia pętli, po instrukcji i = i + 2, wartość i wynosiła 3, czyli nie był spełniony warunek logiczny (i < 3), ale program nie wyskoczył z pętli. Dlaczego tak się stało? Ponieważ wartość wyrażenia logicznego jest wyliczana po wykonaniu wszystkich instrukcji z wnętrza pętli, a w naszej pętli znajduje się instrukcja i = i - 1, po wykonaniu której i przyjęło wartość 2 i warunek był już spełniony.
Istnieje również druga postać instrukcji while, w krórej warunek jest sprawdzany po, a nie przed wykonaniem instrukcji. Posiada ona następującą składnię:
do { instrukcje } while (warunek)
for
Pętla for ma następującą składnię:
for (inicjalizacja; warunek logiczny; akcja) { instrukcje }
Działa ona w sposób następujący:
- Wykonywana jest inicjalizacja
- Sprawdzany jest warunek logiczny, jeśli jest on niespełniony, to pętla kończy swoje działanie
- Wykonywane są instrukcje
- Wykonywana jest akcja
Przyglądnijmy się następującemu programowi:
int i; int j; j = 0; for (i = 1; i <= 10; i = i + 1) { j = j + i; }
Sumuje on liczby od 1 do 10.
- Na początku pętli for wartość i jest ustawiana na 1
- Następnie i jest dodawane do j
- Potem wartość i jest powiększana o 1
- I jest sprawdzane czy i nie jest większe do 10
- Po wyjściu z pętli j ma wartość 55 (czyli suma liczb od 1 do 10) a i ma wartość 11