230 likes | 389 Views
DEFINIOWANIE ZMIENNYCH, C.D. Pamiętamy, że istnieją „podtypy” liczb całkowitych: int, short, long, unsigned; „podtypy” zmiennoprzecinkowych: float i doube, zmienne znakowe char oraz tablice tych zmiennych Każda z tych zmiennych może być również zadeklarowana jako zmienna rejestrowa np.:
E N D
DEFINIOWANIE ZMIENNYCH, C.D. Pamiętamy, że istnieją „podtypy” liczb całkowitych: int, short, long, unsigned; „podtypy” zmiennoprzecinkowych: float i doube, zmienne znakowe char oraz tablice tych zmiennych Każda z tych zmiennych może być również zadeklarowana jako zmienna rejestrowa np.: register int n; register char c; Zmienne rejestrowe przechowywane są w procesorze, dostęp do nich jest szybki. ALE: najczęściej nie stosujemy powyższych deklaracji. Kompilator podczas optymalizacji „sam” to robi z nas. DANE STRUKTURALNE (odpowiednik rekordów) struc katalog { katalog brzechwa[50], lenin[100]; int numer; char opis[40]; float cena; int polka; } ksiazki[1000];
INICJOWANIE STRUKTURY: struct data { int dzien; int miesiac; int rok; char nazwa_miesiac[4]; } d; struc data d={4,7,2003,``Lipi”} ODWOŁANIE SIĘ DO ELEMENTU STRUKTURY: nazwa_struktury.skladowa d.dzien=4; d.miesiac=7; d.rok=2003; d.nazwa_miesiac[0]=``L’’; Struktury mogą być zagnieżdżone: struct osoba{ char imie[10]; char nazwisko[20]; struct data data_urodzenia; } baba; baba.data_urodzenia.rok=2003; itp.
DEFINIOWANIE WŁASNYCH TYPÓW ZMIENNYCH: typedef int miesiac; typedef osoba chlop; DODATKOWE PRE-DEFINIOWANE STAŁE: \b - „backspace”; \r - powrót kursora, \f - nowa strona, \\ - „backslash”; \’ - pojedynczy apostrof, DALEJ: TRUE to 1, FALSE to 0, \xxx (xxx -liczba), liczba w postaci ósemkowej, w szczególności \7 to „brzęczyk” printf (``\7 \7 \7”) - trzy razy zabrzęczy, NULL to \0 ******O DEFINIOWANIU TZW. UNII - PÓŹNIEJ *********** INSTRUKCJA INCLUDE # include “nazwa_pliku” lub #include <nazwa_pliku> ZASTOSOWANIE apostrofu lub <...> WSKAZUJE gdzie system będzie szukał pliku. Apostrof - aktualny katalog, <...> - tam, gdzie są biblioteki. W szczególności każdy program powinien zaczynać się od include <stdio.h> (porównaj: uses Crt w PASCALU)
UZUPEŁNIENIE NT. OPERATORÓW Operatory << oraz >> służą do przesuwania BITÓW w lewo lub prawo, np. x<<2 to przesunięcie x w lewo o dwie pozycje, zwolnione bity uzupełniane są zerami. Podobnie x>>2 to przesunięcie w prawo o dwie pozycje, zwolnione bity dla wielkości unsigned „dopełniane” są (na początku) zerami; natomiast w przypadku zmiennych ze znakiem uzupełniane są bitami znaku. UWAGA: OSTATNIE TO STANDARD, NIE KAŻDY KOMPILATOR TAK ROBI! zatem mnożenie liczby przez 2 to x>>1, przez 8 to x>>3, itp.. (A DZIELENIE?) Operator (negacja, nazywany także „uzupełnienie jedynkowe”) ~ zamienia 0 na 1 i odwrotnie Mówiliśmy o wyrażeniach i++ (++i), teraz przez analogie: zamiast pisać i=i+2; możemy pisać i+=2 DLA OPERATORÓW DWUARGUMENTOWYCH: +,-,*,/,<<,>>,&,^,| zamiast pisać a=a (operator) b możemy pisać a (operator) = b
WYRAŻENIA WARUNKOWE if (warunek) { ... instrukcje ...} E1 ? E2 :E3 - to „skrócone” wyrażenie warunkowe else {instrukcje...} Chcemy by zmienna z była największą liczbą z liczb a i b if (a>b) z=a; z=(a>b)? a:b; else z=b; Wypisać chcemy N elementów tablicy po 10 w każdym wierszu oddzielając każdą kolumnę odstępem ( ‘ ‘) oraz kończąc każdy wiersz (wraz z ostatnim) znakiem nowego wiersza (‘\n’) for (i=0; i<N; i++) printf(``%6d%c”, a[i], (i%10==9 || i=N-1)? ‘\n’:’ ‘);
W instrukcjach warunkowych i logicznych często wygodniej jest stosować notację binarną, ósemkową lub szestnastkową. Należy samodzielnie przećwiczyć te notacje. Wiemy (zapewne) jak inter- pretować zapisy binarne, np. A w jaki sposób odwrotnie, z liczby dziesiętnej otrzymać liczbę w postaci binarnej? Mamy np. liczbę dziesiętną 501, jaka jest jej reprezen- tacja binarna? Możemy to rozwiązać np. w taki sposób: 1) jeśli liczba jest nieparzysta, to ostatni (najniższy) bit jest na pewno 1, w przeciwnym wypadku - 0 2) szukamy największej potęgi 2, takiej, że 2n <=501 (od danej liczby); w naszym przypadku to 8, bo 28 = 256 a 29=512. Zatem na pozycji n+1=9 mamy 1 3) odejmujemy od liczby 2n, u nas 501-256=245 4) powtarzamy 2) dla różnicy - 245, mamy 27= 128, 245-128=117. Zatem na pozycji n+1=7+1=8 jest jedynka, powtarzamy 2) 26=64, 117-64=53, pozycja 6+1 - to jedynka, 25=32, 53-32=21, pozycja 5+1- jedynka; 24=16, 21-16=5, pozycja 4+1- jedynka; 22=4, 5-4=1, pozycja 2+1 - jedynka, 20=1, pozycja pierwsza - jedynka. Pozostałe pozycje to zera. Zapiszmy wynik: 111110101 to binarnie 501
Aby zapisywać liczby w postaci ósemkowej najwygodniej najpierw określić ich postać binarną, a później posługując się relacją binarna- ósemkowa: binarna ósemkowa 000 0 001 1 010 2 011 3 100 4 101 5 110 6 111 7 „przepisać” liczbę binarną jako ósemkową. Np. mamy: 10000001 zapisujemy „ w grupach to trzy” 10 000 001 binarna 2 0 1 ósemkowa : 201 SAMODZIELNIE: szestnastkowe, wówczas grupujemy po 4 bity (po bajcie). Cyfry szersnastkowe to 0,1,2,3,4,5,6,7,8,9,A,B,C,D,E,F Oczywiście, możemy liczbę dziesiętną zamienic na ósemkowa (16-tkową) w ten sam sposób jak na binarną - ale podstawa potęg będzie 8 (lub 16)
Program „musi wiedzieć” czy nadając wartość danej zmiennej,liczba zapisana jest dziesiętnie, ósemkowo czy szestnastkowo i=129 - dziesiętnie i=0201 - ósemkowo, wyróżnik to ZERO NA POCZĄTKU i=0x81 (lub 0X81) - szestnastkowo, wróżnik to 0x lub 0X (ZERO x; ZERO X) ZADNIE 6 Stosując przedstawiony algorytm napisać program przekształcający liczbę dziesiętną w binarną && (ósemkową || szetsnastkową) Binarne operatory: koniunkcji (&), alternatywy (I) często służą do„zasłaniania” bitów liczby (por. przykład na przekształcanie małych literw duże litery). Jeśli chcemy wyzerować wszystkie bity, prócz okrslonych to wystarczy wykonać operację: nowa_liczba=stara_liczba& maska Np. chcemy wyzerować wszystkie bity, prócz bitów 1,2,3 to nasza maska powinna mieć postać binarną ..000 111, liczba ósemkowa to maska=07, operacja liczba&maska wyzeruje wszystkie bity, poza ostatnimi trzema, które pozostawi niezmienione. OERATOR ~TO TZW. „UZUPEŁNIENIE JEDYNKOWE (zmienia 0 na jeden i odwrotnie. Liczba ~maska (~to ) zastosowana w wyrażeniu nowa_liczba=stara_liczba&(~maska) wyzeruje trzy ostatnie bity, pozostałych nie zmieni! (ROZWAŻ CO SIĘ STANIE jeśli liczba|maska oraz liczba|(~maska).
ZADANIE 7. napisz program, któren będzie a) „zasłaniać” b) zerować określoną liczbę bitów (dana wprowadzana do programu w postaci ósemkowej) Czytaną liczbę oraz wynik wyprowadź na ekran w postaci a) dziesiętnej b) ósemkowej c) binarnej TEMAT DO SAMODZIELNEGO OPRACOWANIA: priorytety obliczeń ( JAK W PASCALU!). Pamiętaj: jeśli nie jesteś pewien, STOSUJ NAWIASY!! a/b*c to a dzielone przez b i WYNIK dzielenia mnożony przez c, jeśli chciałeś, aby a było dzielone przez iloczyn b i c, to powinieneś napisać a/b/c, albo, Z NAWIASAMI: a/(b*c), itd.
STEROWANIE 1) Konstrukcja if-else (if-else-if-else-.....) Przykład: Dana jest tablica n liczb int uporządkowana rosnąco oraz liczba int x. Jeśli wartośc x wystepuje w tablicy, to funkcja zwróci jej pozycję (z przedziału 0,..,n-1), w przeciwnym wypadku zwróci -1 szukaj(x,v,n) /* szukaj x wśród v[0],...,v[n-1] */ int x,v[],n; { int low,high,mid; return(-1); low=0; } high=n-1; while(low<=high) { mid=(low+high)/2; if(x<v[mid]) high=mid-1; else if(x>v[mid]) low=mid+1; else /* znaleziono x */ return(mid); }
2) Instrukcja wielokrotnego wyboru - switch (analog instrukcji w PASCALU case). Jeśli zmienna c może przybierać wartości a,b,c,d lub inne (tzn. poza a,b,c,d) i w zależności od jej wartości wykonywane są instrukcje ia,ib,ic,id, inne, to struktura taka ma postać: switch( c) { case a: {ia}; case b: {ib}; case c: {ic}; case d: {id}; default: {inne}; break; } Instrukcja break służy do opuszczenia danej istrukcji ZADANIE 8: przy użyciu instrukcji switch napisz program zliczający ile razy w danych wejściowych pojawiły się cyfry 0,1,2,3,4,5,6,7,8,9 oraz pozostałe znaki. Czytaj dane wejściowe przy pomocy getchar(). Staraj się napisać program przy pomocy jak najmniejszej liczby instrukcji.
3) Pętle for i while while(wyrażenie) {instrukcje} Instrukcja while wykonywana jeśli wyrażenie !=0 („prawda”; pamiętaj: fałsz to 0!) W instrukcji while NAJPIERW obliczane jest wyrażenie, jeśli 0 to instrukcja NIE JEST WYKONANA for(wyr1; wyr2; wyr3} {instrukcje} jest równoważne instrukcjom wyr1; while(wyr2) {{instrukcje}; wyr3; } „Nietypowa” pętla: for(;;) {instrukcje} jest to przykład pętli NIESKOŃCZONEJ; zakończyć ją może umieszczenie w {instrukcje} „wyjscia” (return, break, goto)
Zastosowanie operatora , (przecinek) w instrukcji for Para wyrażeń oddzielonych przecinkiem jest obliczana od lewej do prawej. w instrukcji for można umieścić kilka takich wyrażeń, np. do sterowania kilkoma indeksami char s[]; /* odwracamy ciag znakow s */ for(i=0;j=strlen(s)-1; i<j;i++, j--) /* funkcja standardowa strlen(s) */ {c=s[i]; /* oblicza slugosc lancucha s */ s[i]=s[j]; s[j]=c] } ZADANIE 9 Napisz program, który przepisuje łańcuch s1 do s2 a nastepnie w łańcuchu s1 podwaja każdy znak, np.. jeśli s1 to asadlx, to otrzymać mamy aassaaddllxx
4) Pętla do-while (PASCAL: repeat-until) W poprzednich pętlach (while, for) warunek sprawdzany był na początku w pętli do-while sprawdzany jest na końcu. Pętla ta ma postać: do {instrukcje}; while(wyrażenie) Pętla jest zatrzymywana, gdy wyrażenie stanie się FAŁSZYWE (wartość 0) 5) Instrukcje break i continue instrukcja break powoduje wyjście z bloku {..} w pętli instrukcja continue powoduje przerwanie w danym miejscu i ponowne wykonanie następnego kroku pętli „od początku”, tzn. powoduje natychmiastowe wykonanie sprawdzenia warunku pętli. 6) instrukcja goto i etykiety. Jak w PASCALU. Różnica to brak deklaracji etykiet (w PASCALU: label), tzn. deklaracją jest sama etykieta goto ala; .......... ala: instrukcja; /*to jest DEKLARACJA */ Skok/etykieta obowiązują wewnątrz danej funkcji, użycie goto z „wyjściem” poza funkcje jest błędem
UZUPEŁNIENIE FUNKCJI Wiemy, że funkcje mają postać: nazwa(lista_argumentów) deklaracje_argumetów { deklaracje zmiennych lokalnych oraz instrukcje return(wyrażenie) } wszystkie elementy funkcji mogą być pominięte. W szczególności funkcja dummy() {} jest ATRAPĄ funkcji (stosowaną np.. w celu „zarezerwowania miejsca”. Nic ona nie robi. Komunikacja miedzy main a funkcjami oraz pomiędzy kolejnymi funkcjami odbywa się poprzez argumenty i wartości zwracane pod nazwą funkcji. Przekazywanie argumentów odbywa się przez WARTOŚĆ. Nadawanie wartości nazwie funkcji odbywa się instrukcją return(wyrażenie). Instrukcja return NIE MUSI zawierać wyrażenia, wówczas pod nazwą funkcji „nie ma nic” (tzn. mogą być „śmieci”) Wszystko wyżej to podsumowanie naszych wiadomości
Nowe wiadomości: Program główny i funkcje niekoniecznie muszą być w jednym pliku. Jeśli program główny jest w pliku main.c, a funkcje w plikach fun1.c, fun2.c to możemy kompilować te pliki oddzielnie i łączyć je w jeden moduł wykonywalny: gcc main.c fun1.c fun2.c ..... lub też: gcc main.c fun1.o fun2.o ..... UWAGA: NAĆWICZENIACH PRZEROBIĆ systemową instrukcje make Jeśli funkcje zwracają wartości niecałkowite to jej nagłówek ma postać: typ_funkcji nazwa_funkcji(argumenty), np. double fun2(x) double x {.... return(...) } W PROGRAMIE GŁÓWNYM musimy „potwierdzić” typ funkcji, tzn. musi być deklaracja: double fun2(); Ważna jest ZGODNOŚĆ typów w instrukcji return(...) i typu funkcji. Gdyby funkcja nie została zadeklarowana w programie głównym to wówczas kompilator założyłby, że zwraca ona wartość int! (podobnie jest w FORTRANIE)
ZADANIE 10: Oto przykład funkcji, która przekształca tekst na liczbę zmiennoprzecinkową : double tekst_na_float(s) char s[]; { double val,power; int i,sign; for(i=0;s[i]==‘ ‘||s[i]==‘\n’||s[i]==‘\t’;i++) ; /* trick, tekst może zaczynac się „bialymi” znakami, pomin je */ /* srednik wyzej jest konieczny, petla ma nic nie robic tylko pomijac znaki */ sign=1; if(s[i]==‘+’||s[i]==‘-’) /*ustalamy znak liczby*/ sign=(s[i++]==‘+’)? 1:-1; /*PYTANIE: DLACZEGO i++ a nie ++i ? */ for(val=0, s[i]>=‘0’&& s[i]<=‘9’; i++) val=10*val+s[i]-’0’; /*PYTANIE: Dlaczego -’0’ ? */ if(s[i]==‘.’) i++; for (power=1;s[i]>=‘0’&&s[i]<=‘9’;i++) { val=10*val+s[i]-’0’; power*=p10; return(sign*val/power); }
Wytłumacz dokładnie kolejne instrukcje programu. Napisz program główny, który wywołuje tą funkcję. Program główny ma czytać dane oraz wypisywać wynik
Posługiwanie się funkcjami ze zmienną liczbą argumentów nie jest na ogół bezpieczne, napisanie takiej funkcji wymaga specjalnych procedur (przykładem funkcji o zmiennej liczbie argumentów jest printf) Mówiliśmy, że argumenty przekazywane są przez wartość, tzn. ich elementy są KOPIOWANE i „lokalnie” zmieniane w funkcji. ALE: Jeśli argumentem funkcji jest nazwa tablicy, to przekazywaną wartością jest POŁOŻENIE (adsres) początku tej tablicy i jej elementy NIE SĄ KOPIOWANE. Tak więc funkcja MOŻE zmienić elementy tablicy. TABLICE SĄ PRZEKAZYWANE PRZEZ REFERENCJĘ (ADRESY)!! W dalszej części mówić będziemy o wskaźnikach, które umożliwią funkcjom dostęp do danych innych niż tablice w funkcjach wywołujących
ZMIENNE STATYCZNE Mówiliśmy, że zmienne lokalne w danej funkcji „pojawiają się” w momencie jej wywołania i „znikają” po wyjściu z funkcji. Przy ponownym wywołaniu funkcji program „nie pamięta” ich wartości obliczonych w poprzednim wywołaniu - chyba, że zadeklarujemy je w specjalny sposób. Tego typu zmienne deklarujemy jako STATYCZNE static char[100]; static double x; itp. Zmienne statyczne „znane są” TYLKO wewnątrz funkcji, w której je zadeklarowano, żadna inna funkcja nie ma do nich dostępu. Są one rodzajem „pamięci wewnętrznej” funkcji, są zmiennymi „prywatnymi” danej funkcji. NADAWANIE WARTOŚCI POCZĄTKOWYCH Możemy to robić w deklaracjach, np. int i=0; zamiast int i; ...,i=0; Możemy inicjować tablice, np. int x[5]={0,0,0,0,0} (UWAGA: w standardzie c wszystkie zmienne powinny być inicjowane zerami; jednak należy to sprawdzić dla danego kompilatora). Tablice znakowe możemy inicjować w rozmaity sposób char wyraz[]=“ala”; albo char wyraz[]={‘a’,’l’,’a’,’\0’}
REKURENCJA Podobnie jak PASCAL, c pozwala na rekurencję, tzn. dana funkcja może wywoływać samą siebie. W PASCALU pisaliśmy funkcję silnia(n), obliczjącą n!. ZADANIE 11 Napisz funkcję obliczającą n! a) bez rekurencji b) z rekurencją wskazówka: w PASCALU rekurencja miała postać: function silnia(n:integer): extended; begin if n=0 then silnia:=1 else silnia:=n*silnia(n-1) end;
MAKRA (MAKRODEFINICJE) # define tak 1 # define begin { wówczas możemy pisac: if (i>0) then # define end ;} begin ....... end; # define then INNE PRZYKŁADY #define max(a,b) ((a)>(b)?(a):(b)) UWAGA NAWIASY - aby a i b mogły być wyrażeniami #define square(x) x*x FUNKCJE MATEMATYCZNE #include <math.h> NAZWY ANALOGICZNE JAK W PASCALU (Prawie) ZADANIE 12 Napisz program tablicujący funkcje |(a+sin(x))/cos(x)| w przedziale [min,max] z krokiem delta. Wszystkie dane czytane z klawiatury. Uważaj na komplikacje związane z dziedziną funkcji.