Laboratorium Podstaw Informatyki Kierunek Elektrotechnika Ćwiczenie 1 Podstawy Wprowadzenie do programowania w języku C Kraków 2010
Twój pierwszy program w C Program w języku C, jak i w wielu innych językach składa się ze zmiennych, stałych oraz procedur. Zmienne służą do przechowywania danych na, których program operuje przy pomocy procedur, które w języku C nazywane są funkcjami. Oczywiście w jakiś sposób musimy zaznaczyć gdzie program się rozpoczyna, a gdzie kończy. W języku C program zawsze rozpoczyna się od głównej funkcji zwanej main. Oprócz funkcji zdefiniowanych przez programistę, program wykorzystuje funkcje biblioteczne. Funkcje biblioteczne to takie, które ktoś kiedyś napisał, skompilował, udokumentował i udostępnił innym do użytkowania. Najczęściej programiści korzystają z funkcji bibliotecznych zapewniających komunikację z systemem operacyjnym i obsługującym wejście/wyjście (klawiatura, monitor, dyski, drukarka). Aby móc korzystać z takich funkcji bibliotecznych trzeba o tym powiadomić kompilator. Robi się to poprzez włączenie odpowiedniego pliku nagłówkowego przez #include <nazwa pliku nagłówkowego>, plik ten zawiera deklaracje funkcji, zmiennych i typów danych. Najczęściej włączanym plikiem nagłówkowym jest stdio.h, jak się łatwo domyślić zawiera on deklaracje standardowych (std) funkcji do obsługi wejścia/wyjścia (io). Tak więc jesteśmy już gotowi do napisania prostego programu. #include <stdio.h> main( ) /* nazwa funkcji, za którą są nawiasy w których określone są parametry przekazywane do funkcji */ { /* nawias otwierający */ /* to jest komentarz - kompilator go ignoruje, można go pominąć przy przepisywaniu */ printf("ahoj, przygodo\n"); /* to jest wywołanie funkcje printf z jednym tekstowym argumentem */ } /* to już koniec funkcji main i koniec programu */ Program ten zawiera tylko jedną funkcję (main), wypisuje na ekranie tekst: ahoj, przygodo i powoduje przejście kursora do nowej linii. W nawiasach klamrowych { i } umieszcza się instrukcje wykonywane przez daną funkcję. Program ten wywołuje bardzo często używaną funkcję z biblioteki stdio tj. printf. Argumentem tej funkcji, w naszym przypadku, jest stała tekstowa ahoj, przygodo\n. Stała tekstowa zawsze jest zawarta pomiędzy dwoma znakami ". Sekwencja \n jest znakiem nowej linii, to ona powoduje, że kursor przechodzi do nowej linii. Zadanie 1: Spróbuj coś zmienić, np.: dopisz nowe wywołanie funkcji printf, zmień stała tekstową (tekst, który ma być wyświetlony). Zrób eksperyment z innymi znakami specjalnymi: \t - znak tabulacji, \0 - null (znak oznaczający koniec tekstu - aby zobaczyć jego działanie wstaw go w środek tekstu), \b - backspace (znak cofnięcia), \\ - znak \, \" - znak ". Typy i rozmiary danych Tak jak w matematyce są liczby całkowite, naturalne, rzeczywiste i zespolone tak w języku C są różne typy danych. Teraz poznamy typy podstawowe. Aby program mógł działać poprawnie i efektywnie musi wiedzieć jakiego typu są dane na których wykonuje operacje. W języku C mamy do dyspozycji takie podstawowe typy danych jak:
char jeden bajt, mieści jeden znak ze zbioru znaków. Ponieważ język C dostępny jest na różne maszyny, to bezpiecznym założeniem jest, że ten typ może poprawnie reprezentować liczby z zakresu 0..127; int typ całkowity, na platformach 32 bitowych zakres liczb reprezentowanych przez ten typ to -2^31 do +2^31-1; float typ zmiennoprzecinkowy pojedynczej precyzji, służy do przechowywania liczb rzeczywistych; double typ zmiennoprzecinkowy podwójnej precyzji, służy do przechowywania liczb rzeczywistych, ale z większą precyzją. Dodatkowo dostępne kwalifikatory do stosowania razem z typem int: short int któtki całkowity long int długi całkowity unsigned int całkowity bez znaku (tylko dodatnie) Słowo int może być opuszczone, a więc równoważny jest zapis: short któtki całkowity long długi całkowity unsigned całkowity bez znaku (tylko dodatnie) Oto co gwarantuje się w sprawie rozmiarów typów podstawowych: 1=sizeof(char)<=sizeof(short)<=sizeof(int)<=sizeof(long) sizeof(float)<=sizeof(double) Ponadto gwarantuje się, że typ char zajmuje co najmniej 8 bitów, short co najmniej 16, a long co najmniej 32. Jeśli chcesz się dowiedzieć jaki jest rozmiar poszczególnych typów (tj. ile bajtów pamięci zajmuje zmienna danego typu) na twojej maszynie, pod kompilatorem, którego używasz to skorzystaj z operatora sizeof. Jako argument operatora sizeof można podać nazwę interesującego nas typu danych (również typy złożone jak tablice i struktury), lub nazwę zmiennej danego typu. Rozmiar ten jest obliczany na etapie kompilacji, a wartość zwracana jest typu int (zawsze). Przykładowe użycie to: 1) dla danego typu: sizeof(int); /* ile miejsca zajmuje w pamięci obiekt typu int */ sizeof(long int); /* ile miejsca zajmuje w pamięci obiekt typu long int */ 2) dla zmiennej danego typu: float x; /*definicja zmiennej x typu float */ int a,b; /*definicja dwóch zmiennych a i b typu int */ sizeof(x); /*ile miejsca zajmuje w pamięci zmienna x*/ sizeof(a); /*ile miejsca zajmuje w pamięci zmienna a*/ W języku C wszystkie zmienne muszą być zadeklarowane (ta operacja informuje kompilator, że oto obiekt o nazwie podanej nazwie będzie przechowywał wartości podanego typu, np. w powyższym przykładzie - obiekt x będzie przechowywał wartości typu float) przed wcześniejszym użyciem. Deklaracja składa się z typu i listy zmiennych tego typu. W naszym przypadku deklaracjami są: float x; int a, b; Deklaracje, te są także definicjami - definicja powoduje przydział pamięci dla definiowanej zmiennej. Mówią one że x jest zmienną typu float (rzeczywista), oraz a i b są typu int (całkowite). Każda deklaracja, definicja i instrukcja musi być zakończona średnikiem.
Zadanie 2: Napisz program, który wypisze informacje o rozmiarze wszystkich znanych Ci typów danych. Zrealizuj to na dwa sposoby: poprzez użycie operatora sizeof z argumentem będącym typem danych, oraz z argumentem będącym zmienną danego typu. UWAGA: Do rozwiązania powyższego zadania potrzebne będzie nam rozszerzenie wiadomości o funkcji printf. Deklaracja funkcji (zawarta w zbiorze stdio.h) jest następująca: int printf(char *format [, argument,...]); Słówko int na początku deklaracji mówi, że funkcja printf zwraca wartość typu int, która równa jest liczbie wyświetlonych znaków, które funkcja wyświetliła. Dalej widać, że do funkcji printf można przekazać kilka argumentów oddzielonych przecinkami. Pierwszy argument funkcji printf nazywa się format - informuje nas o tym zapis: char *format. Do szczegółów tego zapisu wrócimy w kolejnych instrukcjach, a póki co przyjmijmy, że ten argument to nic innego jak tekst wzięty w cudzysłów, np.: "zmienna a = %d". Ten tekst jest wyświetlany, ale i analizowany ponieważ mogą w nim wystąpić specjalne sekwencje znaków sterujących. Te sekwencje poprzedzone są znakiem % i służą do wskazania miejsca, gdzie ma być wyświetlona wartość kolejnego argumentu (tzn. drugiego, trzeciego, itd), określa format, w jakim ta wartość będzie wypisana, a także jej typ. Najlepiej zobaczmy to na przykładzie. Wypiszemy wartość zmiennej x, która jest typu float oraz zmiennej b, która jest typu int. Dodatkowo wypiszemy tekst: zmienna x = <wartość x>,zmienna = <wartość b>. printf("zmienna x = %f, zmienna b = %d", x, b); Jak widać, tekst: "zmienna x = %f, zmienna b = %d" zawiera dwie sekwencje sterujące %f i %d. Informują one funkcję printf, że drugi jej argument (bo pierwszym był wspomniany tekst) jest typu float, a trzeci jest typu int. Wszelkie próby oszukania funkcji printf np. próba wypisania zmiennej typu float jako int mogą zakończyć się katastrofą.
Instrukcje Do wykonywania operacji na danych w jezyku C służą instrukcje. Teraz poznamy niektóre z nich. Instrukcja złożona: Instrukcja złożona jest ciągiem instrukcji prostych zamkniętych w nawiasy klamrowe { i }. Instrukcja warunkowa Instrukcja warunkowa ma postać: lub if( wyrażenie ) /*jeśli wyrażenie jest prawdziwe, tzn. ma wartość niezerową */ instrukcja /*to wykonaj instrukcje. */ if( wyrażenie ) /*jeśli wyrażenie jest prawdziwe, tzn. ma wartość niezerową */ instrukcja1 /*to wykonaj instrukcja1 */ else instrukcja2 /* w przeciwnym wypadku wykonaj instrukcja2 */ Przykład: #include <stdio.h> main() { int i = 101; } if(i > 100) { /*nawias otwierający instrukcji złożonej */ printf("wartość i większa od 100"); printf("\n ); } /* nawias zamykający instrukcji złożonej */ else { /* nawias otwierający instrukcji złożonej */ printf("wartość i mniejsza lub równa 100"); printf("\n ); } /* nawias zamykający instrukcji złożonej */ Zadanie 3: zmodyfikować powyższy program tak, aby pozwalał na sprawdzeni podzielności liczby przez 2. Do sprawdzenia podzielności należy zastosować dwuargumentowy operator modulo: %. Operator ten podaje resztę z dzielenia pierwszego argumentu przez drugi. Np. 7%2 daje w wyniku 1, a 10%5 zero. Instrukcja for Instrukcja for służy do organizacji pętli i ma postać for( wyrażenie1 ; wyrażenie2 ; wyrażenie3 ) instrukcja Pierwsze wyrażenie określa stan początkowy pętli, tzn. zazwyczaj służy do inicjalizacji (tj. nadawania wartości początkowej; np. x=10) zmiennej sterującej pętli. Jest ono obliczane na samym początku i tylko jeden raz. Drugie wyrażenie jest warunkiem (np. x<10), którego wartość sprawdza się przed każdym obrotem pętli - gdy jego wartość będzie zero, wówczas wykonywanie pętli będzie przerwane. Trzecie wyrażenie zwykle określa zmianę stanu pętli i jest
wykonywane po każdym obrocie pętli, np. zwiększa wartość zmiennej sterującej o jeden (x=x+1). Dowolne wyrażenie z tych trzech (a nawet wszystkie) można opuścić. Pominięcie warunku wyrażenie2 jest równoważne z zastąpieniem go stałą różną od zera (liczba różna od zera ma wartość logiczną TRUE (prawda), liczba zero ma wartość logiczną FALSE (nie prawda)). Przykład: wypiszemy dziesięć liczb z pewnym krokiem. #include <stdio.h> main() { int i; /* definicja zmiennej i */ for(i = 10; i < 30; i = i + 2) { /* znów przykład instrukcji złożonej, choć można by się bez niej obejść */ printf("liczba %d", i);/* instrukcja powtarzana w pętli */ printf("\n"); /* druga instrukcja powtarzana w pętli */ } } Zadanie 4: na podstawie programu przykładowego (dla pętli for) napisać program, który będzie wypisywał kolejne liczby (nie ujemne) w kolejnych liniach, ale tak aby przed każdą liczbą występowało tyle spacji ile wynosi wypisana liczba. (przykładowo przed liczbą zero - zero spacji, przez jedynką jedna spacja itp.), zobacz przykład: 0 1 2 3 Instrukcja while Druga instrukcja służąca do organizacji pętli ma postać: while( wyrażenie ) instrukcja Wykonanie instrukcji powtarza się tak długo, jak długo wartość wyrażenia wyrażenie jest różna od zera. Sprawdzenia tego dokonuje się przed każdym wykonaniem instrukcji instrukcja. Zadanie 5: Rozwiązać zadanie nr. 4 z użyciem wyłącznie pętli while. Instrukcja do Trzecia instrukcja służąca do organizacji pętli ma postać: do instrukcja while( wyrażenie ) ; Wykonanie podinstrukcji instrukcja powtarza się tak długo, aż wartość wyrażenia wyrażenie stanie się zerem. Sprawdzenia tego dokonuje się po każdym wykonaniu podinstrukcji. Zadanie 6: Rozwiązać zadanie nr. 4 z użyciem wyłącznie pętli do while. Kody ASCII
Komputer operuje na liczbach, które w zależności od sytuacji są różnie interpretowane - jako liczba całkowita, rzeczywista, odcień koloru, znak pisarski, itp. W tej chwili interesuje nas kod ASCII, który określa jaka liczba (kod) odpowiada jakiemu znakowi pisarskiemu. Funkcja printf może wypisywać nie tylko wartości numeryczne wyrażeń, ale również znakowe. Do tego celu służy sekwencja sterująca %c. Przykładowo: printf("litera:%c %c %c ", 65, 66, A ); Ponieważ znak pisarski w pamięci jest przechowywany i traktowany jak liczba typu całkowitego ale mająca 8 bitów, to możliwe są operacje arytmetyczne, np. dodawanie i wypisanie wartości takiej operacji znakowo: printf("litera%c", A +1); Możliwa jest też sytuacja odwrotna. Mając znak można wypisać wartość kodu, czyli liczbę całkowitą jak odpowiada temu znakowi np.: printf("znak %c ma kod %d", A, A ); Zadanie 7: Napisz program, który wypisze fragment tablicy ASCII, czyli wartości kodów znaków od a do z, od A do Z oraz od 0 do 9.