Wyklad ANSI C - Wydział Elektrotechniki i Automatyki
Transkrypt
Wyklad ANSI C - Wydział Elektrotechniki i Automatyki
Katedra Elektrotechniki Teoretycznej i Informatyki sem. III i IV Dr inż. M. Czyżak Literatura: 1. A.R. Neubauer, Języki C i C++, Twój pierwszy program, Help, W-wa, 1995. 2. C. Sexton, Język C to proste, RM, W-wa, 2001. 3. G. Perry, Język C w przykładach, Que, W-wa, 2000. 4. Brian W. Kernighan, Dennis M. Ritchie, Język ANSI C, WNT, Warszawa, 1994. 5. Andrzej Zalewski, Programowanie w językach C i C++ z wykorzystaniem pakietu Borland C++, Nakom, Poznań, 1999. Język ANSI C - historia 1972 – definicja języka C– Dennis M. Ritchie Pierwowzór – beztypowy język B ( Ken Thompson (1970)) będący adaptacją języka BCPL( Basic Combined Programming Language, Martin Richards(1967) ) dla PDP-7. 1974- opis języka C – D.M. Ritchie, B.W. Kernighan, The C programming language, Prentice-Hall. Standard K&R. 1988- unowocześniony standard języka C- język ANSI C. Opis standardu : American National Standard for Information SystemsProgramming Language C, X3.159-1989). Później standard ISO 9899:1990. Język ANSI C - wstęp Zastosowanie: początkowo systemy operacyjne, oprogramowanie narzędziowe ( kompilatory, edytory) i oprogramowanie sieciowe, później oprogramowanie użytkowe. Kompilatory dostępne w praktycznie każdym systemie operacyjnym. Język C jest podstawowym językiem tworzenia oprogramowania dla procesorów sygnałowych i sterujących różnego typu urządzeniami (przykład- kompilator Keil C dla 8051). Język ANSI C - wstęp Cechy języka C cz I: Typy: - typy podstawowe : typy znakowe, całkowite i rzeczywiste - typy pochodne : wskaźnikowe, tablice, struktury, unie i inne. Wyrażenia : budowane z operatorów i ich argumentów Wskaźniki : operacje na adresach niezależnie od maszyny Język ANSI C - wstęp Cechy języka C cz. II: Konstrukcje sterujące: - grupowanie instrukcji (instrukcja złożona) - podejmowanie decyzji ( if, if-else, switch) - realizacja powtarzania ze sprawdzaniem warunku na początku ( for, while), na końcu ( do-while) - przerwanie (break) i kontynuacja pętli (continue) Język ANSI C - wstęp Cechy języka C cz III: Ogólna budowa programu - program składa z pewnych jednostek programowych ( fragmentów kodu ) zwanych funkcjami z dobrze określonymi mechanizmami komunikacji z otoczeniem; - program w C posiada tzw. płaską strukturę tzn. funkcje są niezależne i nie można definiować jednych funkcji w drugich. Język ANSI C - wstęp Cechy języka C cz IV: Funkcje: - mogą zwracać wartości typów podstawowych, struktury, unie i wskaźniki; - zmienne w funkcjach mogą być zmiennymi lokalnymi (automatyczne i statyczne) lub zewnętrznymi; - mogą być wywoływane rekurencyjnie; - mogą być rozmieszczone w jednym lub większej liczbie plików. Język ANSI C - wstęp Przykład 1. Prosty program w języku C- wersja 1 #include <stdio.h> main() { printf(”Pierwszy program w języku C”); } Język ANSI C - wstęp Przykład 2. Prosty program w języku C- wersja 2 #include <stdio.h> #include <conio.h> main() { printf(”Pierwszy program w języku C”); getch(); } Język ANSI C - wstęp Przykład 3. Prosty program w języku C- wersja 3 /* program ten jest uzupełniony o wybrane elementy*/ #include <stdio.h> #include <conio.h> main() { clrscr(); printf(”Pierwszy program w języku C”); printf(”\n napisany przez J. Kowalskiego”); getch(); return 0; } Język ANSI C - przykład Przykład 4. Prosty program z użyciem zmiennych #include <stdio.h> #include <conio.h> main() { int a; int b; int c; a=1; b=2; c=a+b; printf(”\n Suma a+b =%d”,c); getch(); return 0; } Język ANSI C – pojęcie zmiennej Zmienna w języku C Zmienna w języku C z punktu widzenia programisty oznacza pewien obszar pamięci komputera, który może być używany przez programistę w określony sposób. Np. dla zmiennej a, rozmiar tego obszaru określa typ int ( skrót od integer-całkowity). Typ ten oznacza, że w zmiennych tego typu można przechowywać wartości całkowite ( dla kompilatora Borland Builder C++ typ int zajmuje 32 bity i można przechowywać wartości z zakresu [−231 , 231 − 1]. Liczby całkowite są przechowywane w kodzie U2. Język ANSI C – operatory Operatory w jezyku C Operatory to znaki lub kombinacje znaków symbolizujące pewne operacje w programie np. =, +, -. Operatory służą wraz i innymi elementami do tworzenia tzw. wyrażeń. Wyrażenie jest pewną kombinacją operatorów i innych elementów takich jak zmienne i stałe. Przykładami wyrażeń mogą być: -a - jest to wyrażenie skladające się z operatora minus (-) i argumentu ( zmiennej a ) -b+c – wyrażenie to składa się z operatora plus(+) i dwóch argumentów (zmienne a i b) Język ANSI C – operatory Operator minus(-) w pierwszym wyrażeniu jest operatorem jednoargumentowym, a operator plus(+) w drugim wyrażeniu jest operatorem dwuargumentowym. Ogólnie w języku C operator jednoargumentowy może być użyty w sposób następujący: op argument lub argument op, a operator dwuargumentowy argument1 op argument 2 Język ANSI C – operatory Operatory w C można podzielić na następujące grupy: -operatory arytmetyczne ( np. +,-,*,/, %, ++, --) -operatory relacyjne (<,<=, ==, >, >=, !=) -operatory logiczne (&&, ||, !) -operatory bitowe ( ~, &, |, ^) -operator funkcyjny () -operator indeksowania [ ] - operatory adresowy ( &) i dereferencji (*) Język ANSI C – operatory -operator sizeof -operator warunkowy(?) -operatory przypisania ( np. =) -operator rzutowania (typ) -operator przecinkowy -operatory bezpośredniego( . ) i pośredniego odwołania do pola struktury(->) Język ANSI C – typy całkowite Zakresy i rozmiary typów całkowitych Typ unsigned char 8 char short int unsigned short int int unsigned int long int unsigned long int Rozmiar(bity) 8 16 16 32 32 32 32 Zakres liczbowy 0 do 255 -128 do 127 -32768 do 32767 0 do 65536 -2147483648 do 2147483647 0 do 4294967295 zakres int zakres unsigned int ( typ wyliczeniowy enum zostanie omówiony w dalszej części) unsigned oznacza typ bez znaku (kod NKB), pozostałe są przechowywane w kodzie U2. Język ANSI C – operatory Operatory arytmetyki na liczbach całkowitych (ang. integer arithmetic) - operatory jednoargumentowe ‘+’ i ‘ –’ - operator dodawania ‘ + ’ - operator odejmowania ‘-’ - operator mnożenia ‘*’ - operator dzielenia całkowitego ‘/’ - operator obliczania reszty z dzielenia całkowitego Operatory te są stosowane, gdy argument lub argumenty operatora są typu całkowitego. Język ANSI C – operatory /*Przykład 5. Program ilustrujący użycie operatorów arytmetyki całkowitej dla liczb ze znakiem*> #include <stdio.h> #include <conio.h> main() { int a,b,c; printf(”Podaj a:”); scanf(”%d”, &a); printf(”Podaj b:”); scanf(”%d”, &b); c=a+b; printf(”\n Suma %d + %d =%d”, a,b,c); c=a-b; printf(”\n Roznica %d - %d =%d”, a,b,c); c=a*b; printf(”\n Iloczyn %d * %d =%d”, a,b,c); c=a/b; printf(”\n Iloraz calkowity %d przez %d =%d”, a,b,c); c=a%b; printf(”\n Reszta z dzielenia całkowitego %d przez %d =%d”, a,b,c); getch(); return 0; } Język ANSI C – typy całkowite Uwaga: ani argumenty ani też rezultaty poszczególnych działań nie mogą przekraczać zakresów liczbowych dla poszczególnych typów podanych w tabeli. Przykładowo, mnożenie liczb 65000*40000 da liczbę ujemną. Reprezentacja tego iloczynu jest 32-bitowa, stąd najwyższy jej bit wchodzi na pozycję znaku, co powoduje, że otrzymany ciąg bitów jest traktowany jako liczba ujemna w kodzie U2. Język ANSI C – typy całkowite /*Przykład 6. Program ilustrujący użycie operatorów arytmetyki całkowitej dla liczb bez znaku*/ #include <stdio.h> #include <conio.h> main() { unsigned int a,b,c; printf(”Podaj a:”); scanf(”%u”, &a); printf(”Podaj b:”); scanf(”%u”, &b); c=a+b; printf(”\n Suma %u + %u =%u”, a,b,c); c=a-b; printf(”\n Roznica %u - %u =%u”, a,b,c); c=a*b; printf(”\n Iloczyn %u * %u =%u”, a,b,c); c=a/b; printf(”\n Iloraz calkowity %u przez %u =%u”, a,b,c); c=a%b; printf(”\n Reszta z dzielenia całkowitego %u przez %u =%u”, a,b,c); getch(); return 0;} Język ANSI C – typy całkowite Przykład 7. Program ilustrujący użycie operatorów arytmetyki całkowitej do obliczania wyrażenia #include <stdio.h> #include <conio.h> #include <math.h> main() { int k,l, licznik, mianownik, wynik; printf(”Podaj k:”); scanf(”%d”, &k); printf(”\n Podaj l:”); scanf(”%d”, &k); licznik=k*k*k+ k%5 +abs(l); mianownik=k+ k/(k+l); wynik=licznik/mianownik; printf(”\n Wartosc wyrazenia=%d”, wynik); getch(); return 0; } k3 + k 5 + l y= k k+ k +l Język ANSI C – operatory cz. V Wartości logiczne w języku C -Wartość logiczna prawda jest reprezentowana przez każdą liczbę różną od zera, np. 1,2, -5. -Wartość logiczna fałsz jest reprezentowana przez 0. Język ANSI C – operatory cz. VI Operatory relacji w języku C Operatory te to: ‘<‘ ‘ < =‘ ‘ >‘ ‘ >= ‘ ‘==‘ ‘!=‘ - mniejsze - mniejszy lub równe - większe - większe lub równe - równe - rózne Język ANSI C – operatory cz. IV Wyrażenie relacyjne Wyrażenie relacyjne to wyrażenie składające się z operandów połączonych pewnym operatorem relacji op1 operator_relacyjny op2, np. a>b, a!=b, a==b. Wartością wyrażenia relacyjnego jest 1, jeżeli wyrażenie jest prawdziwe albo też 0, gdy wyrażenie jest fałszywe. Język ANSI C -operatory Przykład. Prosty program ilustrujący zastosowanie operatorów relacyjnych #include <stdio.h> #include <conio.h.> int main(int argc, char * argv{}) { int a,b,c; printf(” Podaj a=:”); scanf (”%d”, &a); printf(” Podaj b=:”); scanf (”%d”, &b); c=a<b; printf (”\n Wartosc wyrazenia %d < %d=%d”,a,b,c); c=a<=b; printf (”\n Wartosc wyrazenia %d <= %d=%d”,a,b,c); c=a==b; prinf (”\n Wartosc wyrazenia %d ==%d=%d”,a,b,c); c=a>b; printf (”\n Wartosc wyrazenia %d > %d=%d”,a,b,c); c=a>=b; printf (”\n Wartosc wyrazenia %d > =%d=%d”,a,b,c); c=a!=b; printf (”\n Wartosc wyrazenia %d != %d=%d”,a,b,c); getch(); return 0; } Przykład. Program uzupełniony o możliwość wielokrotnego wykonania ( nieskończona pętla while) #include <stdio.h> #include <conio.h.> int main(int argc, char * argv{}) { int a,b,c; while (1) // początek pętli while { printf(” Podaj a=:”); scanf (”%d”, &a); printf(” Podaj b=:”); scanf (”%d”, &b); c=a<b; printf (”\n Wartosc wyrazenia %d < %d=%d”,a,b,c); c=a<=b; printf (”\n Wartosc wyrazenia %d <= %d=%d”,a,b,c); c=a==b; prinf (”\n Wartosc wyrazenia %d ==%d=%d”,a,b,c); c=a>b; printf (”\n Wartosc wyrazenia %d > %d=%d”,a,b,c); c=a>=b; printf (”\n Wartosc wyrazenia %d > =%d=%d”,a,b,c); c=a!=b; printf (”\n Wartosc wyrazenia %d != %d=%d”,a,b,c); getch(); } // koniec pętli do while return 0; } Przykład. Program uzupełniony o możliwość wielokrotnego wykonania ( pętla while z określoną liczbą wykonań ) #include <stdio.h> #include <conio.h.> int main(int argc, char * argv{}) { int a,b,c, n, licznik=0; printf(” Podaj liczbe wykonan petli”); scanf(”%d”,&n); while (licznik <n) { printf(” Podaj a=:”); scanf (”%d”, &a); printf(” Podaj b=:”); scanf (”%d”, &b); c=a<b; printf (”\n Wartosc wyrazenia %d < %d=%d”,a,b,c); c=a<=b; printf (”\n Wartosc wyrazenia %d <= %d=%d”,a,b,c); c=a==b; prinf (”\n Wartosc wyrazenia %d ==%d=%d”,a,b,c); c=a>b; printf (”\n Wartosc wyrazenia %d > %d=%d”,a,b,c); c=a>=b; printf (”\n Wartosc wyrazenia %d > =%d=%d”,a,b,c); c=a!=b; printf (”\n Wartosc wyrazenia %d != %d=%d”,a,b,c); licznik=licznik+1; getch(); } return 0; } Język ANSI C – operatory cz. VII Operatory logiczne w języku C Operatory te to: ‘ ! ‘ - operator negacji ‘&&‘ - operator koniunkcji( iloczynu logicznego) ‘||‘ - operator alternatywy (sumy logicznej) Język ANSI C – operatory Tabela wartości dla operatorów logicznych x y x&&y x||y !x 0 (fałsz) 0 (fałsz) 0 0 1 0 (fałsz) 1 (prawda) 0 1 1 1 (prawda) 0 (fałsz) 0 1 0 1 (prawda) 1 (prawda) 1 1 0 Przykład. Program ilustrujący najprostsze użycie operatorów logicznych. #include <stdio.h> #include <conio.h.> int main(int argc, char * argv{}) { int a,b,c, n, licznik=0; printf(” Podaj liczbe wykonan petli”); scanf(”%d”,&n); while (licznik <n) { printf(” Podaj a=:”); scanf (”%d”, &a); printf(” Podaj b=:”); scanf (”%d”, &b); c=a&&b; printf (”\n Wartosc wyrazenia %d &&%d=%d”,a,b,c); c=a||b; printf (”\n Wartosc wyrazenia %d ||%d=%d”,a,b,c); c=!a; prinf (”\n Wartosc wyrazenia !%d=%d”,a,c); licznik=licznik+1; getch(); } return 0; } Przykład. Wyrażenia logiczne. Dla obliczenia wartości w poniższych wyrażeniach przyjęto x=1, y=4, z=1. Wyrażenie Wartość wyrażenia x<=1 && y==3 0 x<=1||y==3 1 !(x>1) 1 !x>1 0 !(x<=1||y==3) 0 x>=1&&y==3||z<14 1 Język ANSI C -operatory Priorytety operatorów cz. I O kolejności stosowania operatorów w wyrażeniu decyduje ich priorytet ( pierwszeństwo). Priorytet operatora decyduje o tym czy będzie on użyty przed innymi operatorami czy też po nich. Przykładowo, w wyrażeniu a+b*c stosowany jest najpierw operator * , a dopiero później operator +. Podobnie w wyrażeniu a>b && c<d || e>f stosowane są najpierw operatory relacyjne, później operator && i następnie operator ||. Język ANSI C Hierarchia operatorów ( dotychczas stosowanych) Poziom 1 (najwyższy) ‘ –’ , ‘+’ (jednoargumentowy), ‘!’ Poziom 2 ‘ *’, ‘/’, ‘%’ Poziom 3 ‘+’, ‘-’ Poziom 4 ‘<‘, ‘<=‘, ‘>’,’>=‘, Poziom 5 ‘==‘, ‘!=‘ Poziom 6 ‘&&’ Poziom 7 ‘ ||’ Poziom 8 ‘=‘ Język ANSI C – identyfikator Identyfikatory Jeżeli piszemy program musimy wybrać nazwy dla zmiennych, funkcji i innych obiektów. - Identyfikator(nazwa) jest sekwencją liter i cyfr o dowolnej długości (niektóre kompilatory wprowadzają tu pewne ograniczenia). - Pierwszy znak identyfikatora musi być literą, przy czym znak podkreślenia '_' jest traktowany jako litera. - Litery duże i małe są rozróżniane. Język ANSI C – identyfikatory - Służą one do zapisu konstrukcji, jakie są dopuszczalne w języku C. - Identyfikator nie może być słowem kluczowym(niektóre identyfikatory zostały zastrzeżone przez twórców języka). Budowa identyfikatora Identyfikator w C musi spełniać następujące wymagania: a) zaczynać od litery (A do Z, a do z) lub też od znaku podkreślenia _ ; b) składać z liter (A do Z, a do z), cyfr lub też znaków podkreślenia _ ; c) nie może być słowem kluczowym (słowo kluczowe to takie słowo jak for czy while ). Język ANSI C – identyfikatory Przykład. Legalne (dopuszczalne) identyfikatory ilosc ilosc_calkowita _suma kolumna3 ILOSC Z wyjątkiem identyfikatorów zewnętrznych (nazw zewnętrznych), pierwsze 31 znaków każdego identyfikatora to tzw. znaki znaczące. Jeżeli dwa identyfikatory różnią się od 32 znaku, mogą być uznane za ten sam identyfikator. Ze względu na to , że rozróżniane są litery małe i wielkie identyfikatory ilosc i ILOSC są różnymi identyfikatorami. Język ANSI C – identyfikatory Przykład. Identyfikatory nielegalne (niepoprawne). ilosc$ (niedopuszczalny znak $) 2_suma (rozpoczyna się cyfrą) long (słowo kluczowe) druga suma (w identyfikatorze nie może być spacji) ILOSC-CALKOWITA (niedopuszczalny znak -) Język ANSI C – komentarze Komentarze - Komentarze są ciągami znaków ujętych w znaki /* */. - Komentarze są ignorowane przez kompilator. Przykład. /* Komentarz w języku C */ - Komentarze takie mogą obejmować wiele wierszy. - W komentarzach można używać polskie znaki. - Jeśli stosuje się kompilator C++, można też stosować komentarz jednowierszowy rozpoczynający się od znaków //. Język ANSI C W języku ANSI C istnieją następujące słowa kluczowe: auto break case char const continue default do double else enum extern float for goto if int long register return short signed sizeof static struct switch typedef union unsigned void volatile while Język ANSI C – typ znakowy Typ znakowy -W językach programowania istnieje konieczność reprezentowania, oprócz liczb, także znaków, np. liter. -Znaki są reprezentowane wewnętrznie w postaci pewnych całkowitych nieujemnych liczb zwanych kodami znaków. -Sposób przyporządkowania poszczególnym znakom odpowiednich liczb zwany jest kodem. Przykłady kodów to np. ASCII, EBDIC, Unicode, 8859-2. Język ANSI C – typ znakowy Kod ASCII - Kod ASCII (ang. American Standard Code for Information Interchange) jest standardowym sposobem kodowania znaków. - Standardowy oznacza, że jest powszechnie przyjęty przez wytwórców różnych urządzeń, aby mogły się ze sobą komunikować. - Kodowanie oznacza, że każdemu znakowi odpowiada w tym kodzie pewna liczba będąca jej tzw. kodem ASCII. Język ANSI C – typ znakowy Kod ASCII - W kodzie ASCII znaki o kodach z zakresu [0,127] należą do tzw. standardowego zestawu znaków. Kodom z tego zakresu odpowiadają te same znaki we wszystkich urządzeniach wykorzystujących kod ASCII. Ogólna budowa kodu ASCII - Kody z zakresu [0,31] reprezentują tzw. znaki sterujące np. kod 8 znak BS (ang. backspace), kod 10 znak LF (ang. Line Feed- nowa linia), kod 13 znak CR (ang. Carriage Return- powrót karetki, terminologia pochodzi z maszyn do pisania). Cyfry są reprezentowane przez kody z przedziału [48,57], duże litery z [65,90], a małe litery [97,122]. ( kody pozostałych znaków w dostarczonym materiale). Język ANSI C – typ znakowy Typ znakowy - W języku C istnieje specjalny typ char o zakresie [-128,127] przeznaczony do operacji na znakach. Zmienne i stałe tego typu zajmują 1 bajt. Zmienne są definiowane w sposób następujący: char ch; - W języku C istnieje szereg funkcji istnieje umożliwiających wykonywanie operacji na znakach takich jak wprowadzanie znaków z klawiatury, wyprowadzanie znaków na ekran i klasyfikacja znaków. Język ANSI C – typ znakowy Wejście znakowe(1) - Znaki mogą być wczytywane z klawiatury przy zastosowaniu funkcji pochodzących ze standardowej biblioteki języka C lub też funkcji dostarczanych przez producentów kompilatorów. 1. Funkcja scanf. Przy wczytywaniu znaków konieczne jest zastosowanie deskryptora format %c. printf(” Podaj znak :”); scanf (”%c”,&ch); 2. Funkcja getchar(). printf(” Podaj znak :”); ch=getchar(); Język ANSI C – typ znakowy Wejście znakowe (2) - Funkcja getch(); printf(” Podaj znak :”); ch=getch(); Wczytywanie znaku przy użyciu scanf i getchar wymaga Enter po wprowadzeniu znaku. Język ANSI C – typ znakowy Wyjście znakowe - Wyprowadzenie znaku na ekran może być zrealizowane w sposób następujący printf(” Znak=%c”, ch); lub putchar(ch); lub putch (ch); Język ANSI C – typ znakowy Przykład. Drukowanie wybranych znaków i ich kodów ASCII. #include <stdio.h> #include <conio.h> main() { int i ; i=32; clrscr(); while (i<256) { printf(”\n \n Znak =%c kod dec=%3d hex =%x oct=%o”, i , i, i, i ); getch(); i++; } return 0; } Język ANSI C – typ znakowy Stałe znakowe Stała znakowa – ciąg złożony z jednego lub wiekszej liczby znaków ujęty w pojedyncze apostrofy np. ‘x’. Stałe jednoznakowe są typu char, a wieloznakowe typu int. Stałe znakowe mogą być używane w operacjach arytmetycznych. W operacjach stosowany jest kod ASCII liczby np.. int cyfra; cyfra= ‘5’-’0’; Faktycznie jest to odejmowanie 53-48, czyli w zmiennej cyfra będzie wartość 5. Język ANSI C – typ znakowy Stałe znakowe Stałe znakowe mogą być zapisywane również przy zastosowaniu kodów heksalnych i oktalnych, np. stałą znakową ‘A’ można zapisać także jako ‘\x41’ albo też ‘\101’ . Mechanizm ten ma szczególne znaczenie jeżeli chce się drukować znaki, które nie występują na klawiaturze. Język ANSI C – typ znakowy Stałe znakowe Przykładowo, napis ” Róża bez kolców” można wydrukować w sposób następujący: printf("\n R\242\276a bez kolc\242w"); //wersja oktalna printf("\n R\xa2\xbe a bez kolc\xa2w");// wersja heksalna W powyższych instrukcjach zastosowano kody oktalne i heksalne liter ó i ż. 242 i a2 są kodami ó, a 276 i be litery ż. Język ANSI C – typ znakowy Sekwencje ucieczki ( ang. escape sequences) Znaki, które nie mają reprezentacji graficznej na ekranie monitora lub też mają specjalne znaczenia w łańcuchach znaków, mogą być wygenerowane przy użyciu tzw. sekwencji ucieczki złożonych z ukośnika \ i odpowiedniego znaku. Język ANSI C – typ znakowy Tabela sekwencji ucieczki cz. I Nazwa sekwencji Symbol Zapis znakowy Wartość liczbowa Nowy wiersz NL(LF) \n 10 (dec) Tabulacja pozioma HT \t 9(dec) Tabulacja pionowa VT \v 11(dec) Kasowanie znaku (backspace) BS \b 8(dec) Powrót karetki CR \r 13(dec) Język ANSI C – typ znakowy Tabela sekwencji ucieczki cz. II Nazwa sekwencji Symbol Zapis znakowy Wartość liczbowa Nowa strona ( form feed) FF \f 12 (dec) Dzwonek (alert) BEL \a 7(dec) Lewy ukośnik \ \\ 92(dec) Znak zapytania ? \? 63(dec) Pojedynczy apostrof ‘ \’ 39(dec) Podwójny apostrof ” \” 34(dec) Znak zerowy NUL \0 0 Język ANSI C – typy rzeczywiste Zakresy i rozmiary typów rzeczywistych Typ Rozmiar(bity) Zakres liczbowy Liczba cyfr znaczących float 32 3.4 ⋅10 −38 do 3.4 ⋅1038 6-7 double 64 0.7 ⋅10 −308 do 3.4 ⋅10308 15-16 long double 80 3.4 ⋅10 −4932 do 0.1 ⋅10 4932 19-20 Język ANSI C – typy rzeczywiste Przykład. Program ilustrujący wczytywanie i drukowanie zmiennych rzeczywistych typu float, double i long double. #include <stdio.h> #include <conio.h> main() { float x; double y; long double z; printf(”Podaj x:”); scanf(”%f”, &x); // %f jest deskryptorem formatu stosowanym przy wczytywaniu // i drukowaniu zmiennych typu float printf(”\n Podaj y:”); scanf(”%lf”, &y);// %lf deskryptor formatu dla zmiennych typu double printf(”\nPodaj z:”); scanf(”%Lf”, &z);// %Lf deskryptor formatu dla zmiennych typu long double printf(”\n x=%f”, x); printf(”\n y=%f”, y); printf(”\n z=%Lf”, z); getch(); return 0; } Język ANSI C – typy rzeczywiste /*Przykład. Program ilustrujący użycie operatorów arytmetyki rzeczywistej*/ #include <stdio.h> #include <conio.h> main() { double a,b,c; printf(”Podaj a:”); scanf(”%lf”, &a); printf(”Podaj b:”); scanf(”%lf”, &b); c=a+b; printf(”\n Suma %f + %f =%f”, a,b,c); c=a-b; printf(”\n Roznica %f - %f =%10.2f”, a,b,c); c=a*b; printf(”\n Iloczyn %f * %f =%.4f”, a,b,c); c=a/b; printf(”\n Iloraz %e przez %e =%f”, a,b,c); getch(); return 0;} Język ANSI C – typy rzeczywiste Formatowanie wydruku liczb rzeczywistych Formatowanie wydruku to kształtowanie zewnętrznej postaci liczb (na ekranie monitora lub na drukarce). Zakres takiego kształtowania zależy od formatu liczby rzeczywistej czyli sposobu jej przechowywania w pamięci komputera. Powszechnie przyjmuje się tzw. formaty standardowe zgodne z pewną normą. Normą tą jest tzw. standard IEEE 754. Standard ten określa cztery podstawowe formaty binarne liczb rzeczywistych: - zwykły o o pojedynczej precyzji (SINGLE) - rozszerzony o pojedynczej precyzji (SINGLE EXTENDED) - zwykły o podwójnej precyzji (DOUBLE) - rozszerzony podwójnej precyzji (DOUBLE EXTENDED) s ........................... E- wykładnik w e-bitowym kodzie z obciążeniem +N ............... f- ułamek ( m-bitowy kod modułu mantysy bez bitu ukrytego) Język ANSI C – typy rzeczywiste W standardzie IEEE 754 wymaga się, aby reprezentacje liczb były znormalizowane( nie dotyczy to liczb bardzo bliskich 0). Moduł mantysy ma być liczbąM z przedziału 1≤ M < 2 i obowiązuje konwencja ukrytej jedynki, co oznacza, że najstarszy bit mantysy jest równy 1, jednak nie jest pamiętany. Każdy moduł ma postać 1.bbb.....b. Wartość liczby znormalizowanej można wyrazić następującą zależnością: F = (−1) ⋅ (1+ f ) ⋅ 2 s E Język ANSI C – typy rzeczywiste Wybrane formaty IEEE 754 Parametr Symbol SINGLE DOUBLE DOUBLE EXTENDED Rozmiar formatu n 32 64 >=79 Rozmiar mantysy m 23(+1) 52(+1) >=64 Rozmiar wykładnika e 8 11 >=15 Obciążenie wykladnika N 127 1023 >=16383 Zakres wykładnika E [-126,127] [-1022,1023] [-(N-1),N] Dokładność ulp 2 −23 ≈ 10 −7 2 −52 ≈ 10 −15 2 −m +1 Zakres formatu R 2128 ≈ 1038 21024 ≈ 10308 216384 ≈ 10 4932 Język ANSI C – typy rzeczywiste Maksymalna ilość znaczących cyfr dziesiętnych, które można uzyskać w wydruku wynika z wielkości liczb dziesiętnych, która z kolei wynika z przeliczenia maksymalnych wartości mantysy na postać dziesiętną. Dla typu SINGLE ( w języku C odpowiada mu typ float) mantysa może reprezentować liczbę dziesiętną o wartości 2 ≈ 10 23 7 co umożliwia uzyskanie 6-7 cyfr dziesiętnych. Podobnie dla typu double mamy co daje 15-16 cyfr dziesiętnych. 252 ≈ 1015 Język ANSI C – typy rzeczywiste Dla typu long double mamy 264 ≈ 1019 czyli 19-20 cyfr dziesiętnych. Podstawowe formaty wydruku liczb rzeczywistych w języku C to - format stałoprzecinkowy - m.dd..ddd - format zmiennoprzecinkowy ( notacja inżynierska) m.d....d e xx± lub m.d....d e ± xxxx ( zamiast e może też być stosowane E). Język ANSI C – typy rzeczywiste Wybrane zasady tworzenia opisu formatu w łańcuchu formatującym (pełny opis w B.W. Kernighan, D.M. Ritchie, Język ANSI C, Dodatek B, Pkt. B1.2 Formatowanie wyjście) Przykład. double x=12.348; printf(”\n x=%10.2lf”, x); Opis formatu rozpoczyna się znakiem %, liczba 10 oznacza minimalną szerokość pola wydruku, liczba 2 ilość drukowanych miejsc po kropce dziesiętnej, a lf oznacza wydruk wartości typu double. W ogólnym przypadku pomiędzy znakiem % a znakiem przekształcenia mogą wystąpić - modyfikatory( umieszczane w dowolnej kolejności), które wpływają na postać wydruku: Język ANSI C – typy rzeczywiste Modyfikatory te to: -znak minus (-) powodujący dosunięcie wydruku do lewego krańca pola - znak plus (+) wypisanie liczby zawsze ze znakiem - odstęp poprzedzenie wyniku znakiem odstępu, jeżeli pierwszym drukowanym znakiem nie jest plus lub minus -znak zero (0) uzupełnienie liczby wiodącymi zerami do pełnego rozmiaru pola Następnym znakiem jest liczba określająca minimalny rozmiar pola. Przekształcony argument zostanie wydrukowany w polu o ilości znaków nie mniejszej niż ta liczba. Jeżeli argument wymaga szerszego pola, to pole wydruku jest rozszerzane. Jeżeli argument jest krótszy, dopełniany jest spacjami z lewej strony (ew. w zależności od modyfikatora z prawej lub zerami). Kolejnym znakiem jest kropka, której następuje liczba oznaczająca precyzję czyli ilość cyfr po kropce dziesiętnej w specyfikacjach f, e i E. Język ANSI C – typy rzeczywiste Uwagi dodatkowe o formatach f, e i E Dla formatu f domyślną precyzją jest 6, przy precyzji zero opuszcza się kropkę dziesiętną. Dla ± formatu m.dddddde xx lub formatu m.ddddddE xx domyślną precyzją ± ± jest 6 i przy precyzji zero również opuszcza się kropkę dziesiętną. Przykład. Formaty z zastosowaniem modyfikatorów. x=25.3485; printf(”\n x=% 020.4f”,x);// uzupełnienie zerami printf(”\n x=% +20.4f”,x);// drukowanie znaku liczby printf(”\n x=% -20.4f”,x);//dosunięcie do lewego krańca pola Otrzymane wydruki: x= 000000000000025.3485 x= x= 25.3485 +25.3485 Język ANSI C – typy rzeczywiste Arytmetyczne funkcje standardowe ANSI C Standard ANSI/ISO C definiuje zawartość i postać standardowej biblioteki języka ANSI C. Standard określa zestaw funkcji, w który muszą być wyposażone kompilatory zgodne ze standardem. Kompilatory mogą też zawierać funkcje, które nie są określone w standardzie. Każda funkcja zdefiniowana w standardowej bibliotece języka C ma odpowiadający jej nagłówek, nagłówki te są zawarte w plikach nagłówkowych włączanych dyrektywą #include. Standard C89 zawiera następujące 22 standardowe funkcje arytmetyczne: acos cos fmod modf tan asin cosh frexp pow tanh atan exp ldexp sin atan2 fabs log sinh ceil floor log10 sqrt Język ANSI C – typy rzeczywiste acos double acos (double arg); long double acosl (long double arg); Funkcje te obliczają arcus cosinus arg. Argument musi należeć do przedziału [-1,1]. Jeżeli argument nie należy do tego przedziału, pojawi się błąd dziedziny (ang. domain error). asin double asin (double arg); long double asinl (long double arg); Funkcje te obliczają arcus sinus arg . Argument musi należeć do przedziału [-1,1]. Jeżeli argument nie należy do tego przedziału, pojawi się błąd dziedziny (ang. domain error). Język ANSI C – typy rzeczywiste atan double atan (double arg); long double atanl (long double arg); Funkcje atan and atanl dla argumentu rzeczywistego obliczają wartość arcusa tangensa (wynik w zakresie -pi/2 do pi/2). double atan2 (double y, double x); long double atan2l (long double y, long double x); Funkcje atan2 and atan2l dla argumentu rzeczywistego obliczają wartość arcusa tangensa y/x (wynik w zakresie -pi/2 do pi/2). Język ANSI C – typy rzeczywiste double ceil (double num); long double ceill (long double num); Funkcje zwracają (obliczają) najmniejszą liczbę całkowitą typu double nie mniejszą niż num. Np. Dla num 1.05 funkcja zwraca wartość 2.0, a dla -1.05, zwraca -1.0. double cos (double arg); double cosl (long double arg); Funkcja ta zwraca cosinus arg. Kąt jest podawany w radianach. Zwracana wartość w zakresie [-1,1]. double cosh (double arg); long double coshl (long double arg); Funkcja ta zwraca cosinus hiperboliczny arg. Język ANSI C – typy rzeczywiste double exp (double num); long double expl (long double num); Funkcja oblicza .e x W pewnych sytuacjach argumenty przekazane do tych funkcji powodują nadmiar arytmetyczny lub wynik nie może zostać obliczony. Gdy powstaje nadmiar arytmetyczny, funkcja exp zwraca HUGE_VAL a expl zwraca _LHUGE_VAL. Rezultaty o bardzo dużej wartości powodują ustawienie zmiennej globalnej errno na ERANGE Result out of range. Przy niedomiarze arytmetycznym funkcje te zwracają 0.0, errno nie jest zmieniana. Traktowanie błędu dla tych funkcji może być zmienione poprzez funkcje _matherr and _matherrl. Język ANSI C – typy rzeczywiste /* _matherr example biblioteka standardowa C++ Builder 6.0*/ // przykład dla zaawansowanych – przechwytywanie i korekcja błędu // dziedziny #include <math.h> #include <string.h> #include <stdio.h> int _matherr (struct _exception *a) { if (a->type == DOMAIN) if (!strcmp(a->name,"sqrt")) { a->retval = sqrt (-(a->arg1)); return 1; } return 0; } int main(void) { double x = -2.0, y; y = sqrt(x); printf("_Matherr corrected value: %lf\n",y); return 0; } Język ANSI C – typy rzeczywiste double fabs (double num); long double fabsl (long double num); Funkcje obliczają wartość bezwzględną argumentu num. double floor (double num); long double floorl (long double num); Funkcje te zwracają (obliczają) największą liczbę całkowitą nie większą niż num. Np. dla 1.05 funkcja zwraca wartość 1.0, a dla -1.05, zwraca -2.0. double fmod (double a, double b); long double fmodl (long double a, long double b); Funkcje obliczają resztę z dzielenia a/b. Język ANSI C – typy rzeczywiste double frexp (double num, int *pexp); long double frexpl (long double num, int *pexp); Funkcje te rozkładają liczbę num na mantysę z przedziału [0.5,1.0] oraz wykładnik całkowity exp, takie że num=mantysa ⋅ 2 exp . Przykład. int exp, mantysa; double num=0.5; mantysa=frexp(num,&exp); double ldexp (double⋅ 2num, int exp); long double ldexpl (long double num, int exp); exp Funkcje obliczają wartość wyrażenia num ⋅ 2exp . Język ANSI C – typy rzeczywiste double log (double num); long double logl (long double num); Funkcje obliczają logarytm naturalny z num. Jeżeli num<0, występuje błąd dziedziny, dla num=0 błąd zakresu. double log10 (double num); long double log10l (long double num); Funkcje obliczają logarytm dziesiętny z num. Jeżeli num<0, występuje błąd dziedziny, dla num=0 błąd zakresu. Język ANSI C – typy rzeczywiste double modf (double num, int *pi); long double modfl (long double num, int *pi); Funkcje te rozkładają liczbę num na część całkowitą i część ułamkową. Funkcja zwraca część ułamkową, a część całkowitą wstawia do zmiennej wskazywanej przez wskaźnik pi. Przykład. int cz_calkowita,cz_ulamkowa; double num=0.5; cz_ulamkowa=modf(num,&cz_calkowita); double pow(double base, double exp); long double powl (long double base, long double exp ); Funkcje obliczają wartość base podniesioną do potęgi exp. Reguły obliczania takie jak dla funkcji exp. Jeżeli argument odpowiadający base przekazany do pow lub powl jest rzeczywisty i mniejszy od 0, a argument odpowiadający exp nie jest całkowity lub jeśli argument odpowiadający base jest równy 0 a argument odpowiadający exp jest mniejszy od 0, lub stosuje się wywołanie pow(0,0), zmienna globalna errno jest ustawiana na wartość EDOM Domain error. . Język ANSI C – typy rzeczywiste double sin (double arg); long double sinl (long double arg); Funkcje te zwracają sinus arg. Kąt jest podawany w radianach. Zwracana wartość w zakresie [-1,1]. double sinh (double arg); long double sinhl (long double arg); Funkcje te zwracają sinus hiperboliczny arg. double sqrt (double arg); long double sqrtl (long double arg); Funkcja ta zwraca dodatni pierwiastek kwadratowy z arg. Jeżeli arg mniejszy od 0, zmienna globalna errno jest ustawiana na wartość EDOM Domain error. Język ANSI C – typy rzeczywiste double tan (double arg); long double tanl (long double arg); Funkcje te zwracają tangens arg. Wartość arg jest podawana w radianach. double tanh (double arg); long double tanhl (long double arg); Funkcje te zwracają tangens hiperboliczny arg. Język ANSI C – typy rzeczywiste Przykład. Napisać program obliczający dane wyrażenie dla zmiennych x i z typu double wprowadzanych z klawiatury. Przy wprowadzaniu danych sprawdzać czy dane reprezentują liczby, czy należą do dziedziny oraz czy nie nastąpi przekroczenie zakresu typu double. y= log sin x + tgx + e x5 + ⎣3 log 2 ( x − 1) + ctgz + π 3 x x ⎦ Język ANSI C – typy rzeczywiste Przykład. Obliczanie wyrażenia ( wersja bez sprawdzania danych) #include <stdio.h> #include <conio.h> #include <math.h> int main(int argc, char* argv[]) { double x,z,p1,p2,p3,p4,p5,p6,q1,q2,q3,licznik,mianownik,wynik; int k; printf("\n Podaj x:"); scanf("%lf",&x); printf("\n Podaj z:"); scanf("%lf",&z); p1=fabs(sin(x)); p2=log10(p1); p3=fabs(tan(x)); p3=sqrt(p3); p4=fabs(pow(x,5)); p5=exp(p4); p6=floor(pow(3,x)); q1=log10(x-1)/log10(2); q2=pow(1/tan(z),1/3.0); q3=pow(M_PI,x); Język ANSI C – typy rzeczywiste // cz.2 programu obliczającego wyrażenie licznik=p2+p3+p5+p6; mianownik=q1+q2+q3; if (mianownik) { wynik=licznik/mianownik; printf("\n Wartosc wyrazenia wynosi=%f",wynik); getch(); } else { printf("\n Dzielenie przez zero"); getch(); } return 0; } Język ANSI C – typy rzeczywiste Dziedzina: a) ze względu na funkcję logarytm w liczniku x musi być różny od wielokrotności pi. b) ze względu na funkcję logarytm w mianowniku, musi być x>1. Ograniczenie na x wynika też z wartości przybieranych przez człon e x5 Musi być spełniona zależność x5 308 e ≤ 10 Graniczną wartość x możemy określić jako x = 5 308 ⋅ ln 10 ≅ 3.71 Język ANSI C – typy rzeczywiste // Fragment programu związany ze sprawdzaniem danych wejsciowych #include <stdio.h> #include <conio.h> #include <math.h> int main(int argc, char* argv[]) { double x,z,p1,p2,p3,p4,p5,p6,q1,q2,q3,licznik,mianownik,wynik; int k; do { printf("\n Podaj x:"); k= scanf("%lf”,&x); fflush(stdin); if (k==0) printf(”\n Niewlasciwy format liczby”); else if (x<=1 || x==M_PI||x>3.7) printf(”\n x nie należy do dziedziny”); } while (k==0 || x<=1|| x==M_PI ||x>3.7 ); do { printf("\n Podaj z:"); k=scanf("%lf",&z); } while(!k||fmod(z,M_PI)==0); Język ANSI C – typy rzeczywiste Z obliczaniem wyrażeń w języku C wiążą się trzy zagadnienia: a) problem kolejności obliczania podwyrażeń b) konwersja typów w wyrażeniach c) rzutowanie (zmiana typu wyrażenia) Ad a. Język C nie określa kolejności w jakiej są obliczane podwyrażenia danego wyrażenia. Kompilator ma więc swobodę w zmianie kolejności obliczania tak aby uzyskać zoptymalizowany kod. Przykładowo w wyrażeniu x=f1() +f2(); nie można określić czy funkcja f1 będzie wywołana jako pierwsza. Język ANSI C – typy rzeczywiste Ad.b. Jeżeli w wyrażeniu występują zmienne i stałe różnych typów, są one wszystkie przekształcane do jednego wspólnego typu. Kompilator przekształca wszystkie operandy do największego typu występującego w wyrażeniu. W pierwszym rzędzie wartości typu char i short int przekształcane są do typu int. Przekształcenia typów przebiegają zgodnie z powyższymi regułami: - jeżeli operand jest typu long double, to drugi operand jest przekształcany do typu long double, -jeżeli operand jest typu double, to drugi operand jest przekształcany do typu double, -jeżeli operand jest typu unsigned long, to drugi operand jest przekształcany do typu unsigned long, -jeżeli operand jest typu float, to drugi operand jest przekształcany do typu float, -jeżeli operand jest typu long int, to drugi operand jest przekształcany do typu long int, Język ANSI C – typy rzeczywiste Ad. b (ciąg dalszy-przekształcenia typów) -jeżeli operand jest typu unsigned int , to drugi operand jest przekształcany do typu unsigned int, Istnieje jednak pewien specjalny przypadek: jeżeli jeden operand jest typu long a drugi typu unsigned int, i wartości typu unsigned int nie można przedstawić przy użyciu typu long, wtedy oba operandy są przekształcane do typu unsigned int. Język ANSI C – typy rzeczywiste Ad. b (ciąg dalszy-przekształcenia typów) Po zastosowaniu powyższych reguł każda para operandów ma ten sam typ oraz wynik operacji jest tego samego typu, co typ obu operandów. Przykład. char ch=3; int i=5; float f=1.5; double d=2.5,wynik; wynik=(ch / i) + int int (f * d) - double float double float double double (f + i); Język ANSI C – typy rzeczywiste Ad.c. (rzutowanie) Typ wyrażenia można narzucić lub zmienić przy pomocy tzw. rzutowania. Ogólna postać rzutowania to: (typ) wyrażenie, lub typ ( wyrażenie) Rzutowanie jest operatorem o priorytecie takim jak pozostałe operatory jednoargumentowe. Język ANSI C – typy rzeczywiste Przykład. Napisać fragment programu określający procentową ilość udanych prób w stosunku do ogólnej ilości prób. // Przykład. Zmiana typu poprzez rzutowanie #include <stdio.h> #include <conio.h> #include <math.h> int main(int argc, char* argv[]) { int ilosc_prob, ilosc_udanych_prob; double udzial_procentowy; printf("\n Podaj ilosc prob :"); scanf("%d",&ilosc_prob); printf("\n Podaj ilosc udanych prob:"); scanf("%d",&ilosc_udanych_prob); udzial_procentowy= double (ilosc_udanych_prob)/ilosc_prob*100; printf("\n Udzial procentowy udanych prob=%6.2f%", udzial_procentowy); getch(); return 0; } Język ANSI C – instrukcje Instrukcja złożona (grupująca) Instrukcja złożona składa się z nawiasu klamrowego otwierającego, dowolnych instrukcji (mogą być również kolejne instrukcje złożone) i nawiasu klamrowego zamykającego: Przykład. Instrukcja złożona. { printf("Instrukcja 1" ); printf("Instrukcja 2\n"); } Wszystkie instrukcje w języku C z wyjątkiem instrukcji złożonej kończą się średnikiem Instrukcja złożona jest stosowana wtedy, gdy istnieje konieczność zgrupowania kilku instrukcji, tak aby były traktowane jako jedna instrukcja. Język ANSI C – instrukcje Instrukcja while Instrukcja while jest instrukcją iteracyjną, umożliwiającą powtarzanie pewnego ciągu operacji. Instrukcja while ma następującą postać: while (wyrażenie) akcja W instrukcji while najpierw określa się czy wyrażenie jest prawdziwe czy też fałszywe. C wymaga by wyrażenie było zawarte w nawiasach.. Jeżeli wyrażenie jest prawdziwe, wykonywana jest akcja i następuje powrót do obliczania wyrażenia. wyrażenie jest ponownie testowane i jeżeli jest prawdziwe znowu wykonywana jest akcja i następuje kolejny powrót do obliczania wyrażenia. Jeżeli jednak przy którymś obliczaniu wyrażenia, stwierdzone zostanie, że wyrażenie jest fałszywe, następuje natychmiastowe przejście do instrukcji znajdującej się po instrukcji while. Język ANSI C – instrukcje Akcja może się składać z jednej instrukcji lub też szeregu instrukcji zawartych w nawiasach { }. Użycie tych nawiasów jest konieczne jeśli akcja obejmuje więcej niż jedną instrukcję. Przykład. Instrukcja while. int x=0; while (x != 2) x = x + 1; printf (“\n x = %d”, x); Język ANSI C – instrukcje Fragment ten wydrukuje x=2 Zmienna x jest inicjalizowana wartością 0 przy definicji. Ponieważ != jest operatorem relacyjnym „różne” (nierówne), wyrażenie x != 2 ma wartość prawda i wykonywane jest instrukcja x = x + 1, która dodaje 1 do x, i wynik umieszcza w x. Następuje powrót do obliczania wyrażenia po while. Wyrażenie x != 2 jest prawdziwe (gdyż x równa się 1), więc znowu dodawane jest 1 do x i następuje powrót do obliczania wyrażenia po while. Wyrażenie x != 2 jest teraz fałszywe (bo x równa się 2) więc zostanie wykonana instrukcja printf (“\n x = %d”, x); która pisze x=2 Język ANSI C – instrukcje Instrukcja do while Instrukcja do while jest podobna do instrukcji while, z tą różnicą, że wyrażenie kontrolujące pętlę jest na końcu pętli. Cała pętla wykonywana jest przynajmniej raz. do akcja while (wyrażenie); Przy wykonywaniu pętli do while wykonywana jest najpierw akcja, a następnie wykonywane jest sprawdzenie, czy wyrażenie jest prawdziwe czy też nie. Jeżeli jest prawdziwe, następuje powrót do początku pętli. Jeżeli w którymś momencie wyrażenie staje się fałszywe, wykonywana jest pierwsza instrukcja występująca po pętli. Część pętli oznaczona jako akcja może być jedną instrukcją lub też grupą instrukcji zamkniętą w nawiasy { }. Język ANSI C – instrukcje Instrukcja do while jest użyteczna wtedy, gdy test w sposób naturalny znajduje się na końcu pętli. Przykładem takiej sytuacji może być weryfikacja wartości wprowadzanej przez użytkownika z klawiatury. Po wprowadzeniu wartości, jest ona sprawdzana. Jeżeli wartość jest niedopuszczalna przy danym formacie, użytkownik zachęcany jest do wprowadzenia innej wartości. Przykład. Użycie instrukcji do while do wprowadzenia liczby dodatniej. int wartosc, k; do { printf (“\nPodaj liczbe calkowita:”); k = scanf (“%d”, &wartosc); fflush(stdin); //Czyszczenie strumienia wejściowego } while (!k || wartosc <=0); Język ANSI C – instrukcje Instrukcja warunkowa if Instrukcja warunkowa if umożliwia uzależnienie wykonania pewnej instrukcji od spełnienia pewnego warunku. Warunek ten jest reprezentowany przez wyrażenie umieszczone po słowie if. Wyrażenie to przybiera wartość prawda lub wartość fałsz (wszystkie wartości różne od 0 są w języku C traktowane jako prawda, wartość 0 jako fałsz). Instrukcja ta ma następującą postać if (wyrażenie) akcja; Język ANSI C – instrukcje Wyrażenie musi być zawarte w nawiasach. Aby wykonać if najpierw oblicza się wyrażenie i określa się czy jest prawdziwe czy też fałszywe. Jeżeli wyrażenie jest prawdziwe, zostaje wykonana akcja i sterowanie przechodzi do instrukcji programu umieszczonej po akcji. Jeżeli wyrażenie jest fałszywe, akcja zostaje pominięta i następuje przejście do instrukcji umieszczonej po akcji. Część if określona jako akcja składa się z pojedynczej instrukcji lub instrukcji złożonej (pewnej liczby instrukcji umieszczonych w nawiasach {}). Język ANSI C – instrukcje Przykład. Instrukcja if int kod; printf(”\n Podaj kod:”); scanf(”%d”,&kod); if (kod= =1) printf ("\n warunek jest spełniony"); Jeżeli z klawiatury podano wartość 1, fragment programu drukuje napis warunek jest spełniony Nie należy mylić znaku = z parą znaków = =. Można wprawdzie napisać if (x=1), jednak wyrażenie po if jest tutaj równe 1 niezależnie od wartości x, więc instrukcja if nie będzie pełnić swojej roli. Język ANSI C – instrukcje Instrukcja if else Instrukcja ta ma następującą postać: if (wyrażenie) akcja1 else akcja2 Podobnie jak dla instrukcji if, najpierw obliczane jest wyrażenie po if. Jeżeli wyrażenie ma wartość logiczną prawda, wykonywana jest akcja1, natomiast jeżeli wyrażenie ma wartość fałsz, jest wykonywana akcja2. Język ANSI C – instrukcje Przykład. Instrukcja if else int kod; printf(”\n Podaj kod”); scanf(” %d”,&kod); if (kod==3245) printf(”\n Podano prawidłowy kod”); else printf(”\n Kod nie jest poprawny”); Język ANSI C – instrukcje Przykład. Instrukcja if else ze złożonymi instrukcjami po if i po else. int kod; printf(”\n Podaj kod”); scanf(” %d”,&kod); if (kod!=2) { printf(”\n Instrukcje te wykonują się gdy ”); printf(”\n zmienna kod ma wartość rozna od 2”); } else { printf(”\n Instrukcje te się wykonuja”); printf(”\n gdy zmienna kod ma wartosc 2 ”); } Język ANSI C – instrukcje Przykład. Określić napisy drukowane przez poniższy fragment programu dla zmiennej kod=1 i 2. int kod; printf(”\n Podaj kod”); scanf(” %d”,&kod); if (kod==2) { printf(”\n Matematyk”); if (kod==2) printf(” artysta”); else printf(” programista”); } else printf(”\n Specjalista programista”); Język ANSI C – instrukcje Instrukcje if zagnieżdżone W instrukcji takiej po if lub po else występuje kolejna instrukcja warunkowa if lub if else. Instrukcja taka ma budowę następującą: if (wyrażenie1) akcja1; else if (wyrażenie2) akcja2; else if (wyrażenie3) . . . else if (wyrażenie n) akcja n; Język ANSI C – instrukcje Działanie instrukcji if zagnieżdżone Instrukcja taka funkcjonuje w sposób następujący: jeżeli prawdziwy jest warunek pierwszy (wyrażenie1) to wykonywana jest akcja1 i następuje przejście do instrukcji umieszczonej po akcji n-tej. Ogólnie, jeżeli jest prawdziwe wyrażenie i-te, to jest wykonywana i-ta akcja i następuje przejście do instrukcji po wyrażeniu n-tym. Jest wykonywana tylko jedna akcja (grupa akcji w nawiasie klamrowym), pozostałe akcje są odrzucane. Język ANSI C – instrukcje Przykład. Instrukcja if zagnieżdżone. int kod; printf(”\n Podaj kod”); scanf(” %d”,&kod); if (kod= =1) printf("Akcja ta jest wykonywana gdy kod jest równy 1");/*instrukcja 1*/ else if (kod= =2) printf("Akcja ta jest wykonywana gdy kod jest równy 2");/*instrukcja 2*/ else if (kod<=3) printf("Akcja ta jest wykonywana gdy kod jest mniejszy równy 3");/*instrukcja 3*/ Gdy zmienna kod ma wartość 1, wykonywana jest instrukcja 1, pomimo że wyrażenie kod<=3 jest prawdziwe, jednak sterowanie do niego w tym przypadku nie dochodzi. Język ANSI C – instrukcje Instrukcja if else z akcją domyślną if (wyrażenie1) akcja1; else if (wyrażenie2) akcja2; else if (wyrażenie3) . . . else if (wyrażenie n) akcja n; else akcja; Akcja jest tutaj działaniem domyślnym, które jest wykonywane, gdy żadna z akcji od 1 do n nie zostanie wykonana. Język ANSI C – instrukcje Przykład. Instrukcja if else z akcją domyślną. int kod; printf(”\n Podaj kod”); scanf(” %d”,&kod); if (kod= =1) printf(”Akcja ta jest wykonywana, gdy kod jest rowny 1"); else if (kod= =2) printf("Akcja ta jest wykonywana, gdy kod jest rowny 2"); else if (kod<=3) printf("Akcja ta jest wykonywana gdy kod jest mniejszy lub rowny 3"); else printf("Akcja ta jest wykonywana, gdy " "żadna z akcji 1, 2, 3 nie wykona się"); Język ANSI C – operator ? (wyrażenie warunkowe) Operator ? pozwala na zastąpienie niektórych instrukcji if-else. Operator ten ma postać: wyrażenie1 ? wyrażenie2 :wyrażenie3 Operator ten działa w sposób następujący: najpierw jest obliczane wyrażenie1, jeśli ma ono wartość logiczną prawda, wtedy jest obliczane wyrażenie2 i staje się ono wartością całego wyrażenia, jeśli zaś wyrażenie1 ma wartość logiczną fałsz, obliczane jest wyrażenie3 i ono staje się wartością całego wyrażenia. Przykład. int x, y; x=5; y=x>10? 50 : 100; Język ANSI C – operator ? (wyrażenie warunkowe) Przykład. z=a>b? a: b; // max(a,b) Powyższa instrukcja wyznacza większą z wartości a i b. Wyrażenie warunkowe może być stosowane wszędzie tam, gdzie w języku C może wystąpić wyrażenie. Priorytet operatora ? jest względnie niski ( tuż nad operatorem przypisania) stąd nie jest konieczne stosowanie nawiasów, aby wymusić odpowiednią kolejność wykonywania operacji. Język ANSI C – operator przecinkowy Użycie operator przecinkowego jest stosowany przedstawić w sposób następujący: wyr1, wyr2,…,wyrN Wyrażenia oddzielone przecinkami oblicza się od lewej do przy czym typem i wartością całego wyrażenia są typ i wartość wyrażenia wyrN. Przykład. int x, y; x= (y=15;y+1); Wartość x jest równa 16. Język ANSI C – operatory inkrementacji i dekrementacji Dla operatorów tych stosowane są następujące symbole . ++ - dla operatora inkrementacji (zwiększania), oraz -- - dla operatora dekrementacji ( zmniejszania). Operatory te zwiększają wartość swego argumentu o 1 (operator ++), lub też zmniejszają o 1 ( operator --). Operatory te mogą być używane w następujących formach: ++ operand - ++ - operator preinkrementacji, operand++ - ++ - operator postinkrementacji, -- operand - -- operand -- - operator predekrementacji, -- operator postdekrementacji, Język ANSI C – operatory inkrementacji i dekrementacji Operand w powyższych wyrażeniach z operatorami ++ lub -- może być typu arytmetycznego lub wskaźnikowego, musi być jednak tzw. Lwartością (ang. Lvalue), tzn. musi reprezentować pewną lokację pamięci. Użycie powyższych operatorów powoduje dwa efekty: - wartość operandu jest modyfikowana ( zwiększana lub zmniejszana o 1), - wyrażeniu z operandem jest przypisywana pewna wartość. Język ANSI C – operatory inkrementacji i dekrementacji Działanie operatora postinkrementacji (operand ++). - wartość operandu jest zwiększana o 1, - wartość wyrażenia operand ++ jest równa początkowej wartości operandu. Przykład. x=3; y=x++; y=x++; x jest zwiększany o1 (x=4) wartość wyrażenia jest równa początkowej wartości x ( y=3) Język ANSI C – operatory inkrementacji i dekrementacji Działanie operatora preinkrementacji (++operand). - wartość operandu jest zwiększana o 1, - wartość wyrażenia ++operand jest równa zwiększonej o 1 wartości operandu. Przykład. x=3; y=++x; y=++x; x jest zwiększany o1 (x=4) wartość wyrażenia jest równa zwiększonej wartości x ( y=4) Język ANSI C – operatory inkrementacji i dekrementacji Działanie operatora postdekrementacji (operand - -). - wartość operandu jest zmniejszana o 1, - wartość wyrażenia operand -- jest równa początkowej wartości operandu. Przykład. x=3; y=x--; y=x--; x jest zmniejszany o1 (x=2) wartość wyrażenia jest równa początkowej wartości x ( y=3) Język ANSI C – operatory inkrementacji i dekrementacji Działanie operatora predekrementacji (- - operand). - wartość operandu jest zmniejszana o 1, - wartość wyrażenia operand -- jest zmniejszonej o 1 wartości operandu. Przykład. x=3; y=--x; y=--x; x jest zmniejszany o1 (x=2) wartość wyrażenia jest równa zmniejszonej wartości x ( y=2) Język ANSI C – instrukcje Przykład. Użycie operatorów preinkrementacji i postinkrementacji. #include <stdio.h> #include <conio.h> int main(int argc, char* argv[]) { int x,y; x= (y=15,y=y+1,y=y+1,++y); printf("\n x=%d",x); x= (y=15,y=y+1,y=y+1,y++); printf("\n x=%d",x); getch(); return 0; } Język ANSI C – instrukcje Instrukcja for Instrukcja for, zwana też pętlą for, jest instrukcją iteracyjną podobnie jak instrukcje while i do while. Stosowana jest ona do wielokrotnego powtarzania pewnego segmentu kodu. Pętla for w języku C jest podobna do pętli for w innych językach programowania, posiada jednak znacznie większe możliwości. Pętla for ma następującą postać: for (wyr1; wyr2; wyr3) akcja Język ANSI C – instrukcje Wyrażenie wyr1 jest stosowane do inicjalizacji pętli, wyrażenie wyr2 jest używane do testowania warunku kontynuacji pętli, wyrażenie wyr3 służy do uaktualniania pętli. Akcja stanowi treść (ciało) pętli. Działanie: Krok1. Obliczenie wyrażenia wyr1 (jest ono obliczane jeden raz i tylko jeden raz na początku działania pętli). Krok 2a. Obliczenie wyr2 i sprawdzenie czy wyrażenie wyr2 jest prawdziwe czy też fałszywe. Krok 2b. Jeżeli wyr2 jest fałszywe, zakończenie pętli i przejście do instrukcji umieszczonej bezpośrednio po pętli. Jeżeli wyr2 jest prawdziwe, wykonanie akcji. Krok 3. Obliczenie wyrażenia wyr3, następnie przejście do Kroku 2. Język ANSI C – instrukcje Ciało pętli (akcja) może być pojedynczą instrukcją albo też instrukcją złożoną (grupującą), jeśli jest konieczne wykonanie w pętli więcej niż jednej instrukcji Przykład. Użycie pętli for. int sum=0; int i; for (i=1; i<=4;i=i+1) sum=sum+i; W powyższym przykładzie w pętli for najpierw jest wykonywana instrukcja i=1 , następnie sprawdzany jest warunek i<=4, dla i=1 jest on prawdziwy, więc wykonywana jest instrukcja sum=sum+i; i następuje przejście do instrukcji i=i+1, i przyjmuje wartość 2, i wykonywane jest sprawdzenie i<=4, warunek jest prawdziwy, więc ponownie wykonywana jest instrukcja sum=sum+i. Gdy i przybierze wartość 5, warunek i<=4 nie spełniony i pętla kończy się. Język ANSI C – instrukcje Przykład. Użycie pętli for ( typowy zapis) #include <stdio.h> #include <conio.h> int main(int argc, char* argv[]) { int i,sum; for( i=0,sum=0; i<=4;i++) sum=sum+i; printf("\n sum=%d",sum); getch(); return 0; } W wyrażeniu wyr1 użyto operator przecinkowy. Język ANSI C – instrukcje #include <stdio.h> #include <conio.h> int main(int argc, char* argv[]) { int x, y; for( x=0,y=0; x+y<10 ;x++) { y=getch(); // wczytywanie y jako znaku putchar(y); // drukowanie wczytanego znaku y=y -'0'; // wyznaczanie cyfry reprezentowanej przez wczytany znak } printf("\n x=%d y=%d", x,y); getch(); return 0; } Język ANSI C – instrukcje Odmiany pętli for 1. Pętla nieskończona for ( ; ; ) printf (” Pętla ta będzie działać w nieskończoność”); Brak wyr2 uważany jest za wyrażenie prawdziwe. 2. Pętla bez treści(ciała) for (i=0;i<100000;i++) ; // pętla taka może realizować opóźnienie w programie Ogólnie w pętli for może nie być któregoś z wyrażeń albo też wszystkich, jednak zawsze muszą być dwa średniki. Przykład. Pętla bez wyrażenia wyr3. for( i=0,sum=0; i<=4;) { sum=sum+i; i++; } Język ANSI C – instrukcje Instrukcja break Instrukcja break powoduje natychmiastowe przerwanie wykonywania pętli, w której została umieszczona. Przykład . Użycie instrukcji break w pętli. for (i=0;i<5;i++) { printf("\n i=%d ",i); if (i==3) break; printf("\n Koncowa instrukcja petli"); } printf("\n Poza petla"); W przykładzie powyższym, gdy zmienna i osiągnie wartość 3, warunek po if ma wartość logiczną prawda, co powoduje wykonanie jest instrukcja break, która daje natychmiastowe przerwanie wykonywania pętli. Język ANSI C – instrukcje Instrukcja continue Instrukcja continue jest podobna do instrukcji break w tym sensie, że również przerywa działanie pętli, jednak pętla nie ulega zakończeniu, lecz następuje przeskok do wykonania wyrażeń sterujących pętlą. Dla pętli for mamy for (wyr1;wyr2;wyr3) { blok instrukcji (1) if (warunek) continue; blok instrukcji (2) } Jeżeli warunek po if jest prawdziwy, to wykonanie continue powoduje natychmiastowe przejście, bez wykonania bloku instrukcji2 do obliczenia wyrażenia wyr3 i następnie do obliczenia wyrażenia wyr2. Jeżeli wyrażenie wyr2 ma wartość prawda, wykonanie pętli jest kontynuowane. . Język ANSI C – instrukcje Dla pętli while i pętli do while następuje natychmiastowe przejście do obliczenia wyrażenia sterującego pętlą, czyli dla pętli while na początek pętli, a dla pętli do while na koniec pętli. Przykład . Program oblicza średnią liczb dodatnich podawanych na wejscie, liczby ujemne są odrzucane przy użyciu instrukcji continue. #include <stdio.h> #include <conio.h> main() { double x, suma=0; int ilosc=0; while ( printf ("\n Podaj liczbe :"), scanf("%lf" ,&x)!=0) { if (x<0.0) continue; suma=suma+x; ilosc++; } if (ilosc>0) printf("\n Srednia=%f", suma/ilosc); else printf("\n Nie podano zadnych liczb dodatnich"); getch(); t 0 Język ANSI C – instrukcje Instrukcja switch switch( wyrazenie_calkowite) { case etykieta_1: instrukcja 1; case etykieta_2: instrukcja 1; . . . case etykieta_n: instrukcja_n; default: instrukcje; } Wyrażenie po słowie switch, zwane wyrażeniem wyboru lub selektorem, musi przyjmować wartości całkowite. Etykiety są całkowitymi wartościami stałymi lub wyrażeniami stałymi. Jeśli podczas wykonywania instrukcji switch jedna z etykiet ma wartość równą wartości selektora (oznaczonego jako wyrazenie_calkowite), to wykonanie instrukcji switch rozpoczyna się od wykonania instrukcji znajdującej się przy tej etykiecie. Język ANSI C – instrukcje Jeżeli w instrukcji switch występuje słowo default (ang. domyślny), instrukcje umieszczone po słowie default są wykonywane, gdy żadna z etykiet nie ma wartości równej selektorowi. Etykieta default jest opcjonalna, i jeżeli nie występuje, to następuje przejście do pierwszej instrukcji po instrukcji switch. Jeżeli chce się uzyskać wykonanie tylko jednej konkretnej instrukcji, należy po każdym wariancie umieścić słowo break wraz ze średnikiem. Spowoduje to natychmiastowe przerwanie wykonywania instrukcji switch po zrealizowaniu instrukcji związane z danym wariantem. Język ANSI C – instrukcje Przykład. W przykładzie wczytywana jest liczba całkowita i jeżeli jest to liczba 0,1,2 lub 3, drukowany jest komunikat informujący o wartości liczby. Jeżeli jest podaną liczbą jest inna liczba, drukowany jest komunikat Inna liczba. #include <stdio.h> #include <conio.h> main() { int liczba; printf("\n Podaj liczbe:"); scanf("%d", &liczba); switch( liczba) { case 0: printf("\n Liczba 0"); break; case 1: printf("\n Liczba 1"); break; case 2:case 3: printf("\n Liczba 2 lub 3"); break; default : printf("\n Inna liczba"); } getch(); return 0; } Język ANSI C – instrukcje Instrukcja goto (cz.1) Instrukcja goto to tzw. instrukcja skoku powodująca bezwarunkowe sterowania do pewnego punktu w programie opatrzonego etykietą. Etykieta jest identyfikatorem i zakończona jest dwukropkiem. Etykietę można umieścić przed każdą instrukcją w funkcji, w której występuje instrukcja goto. Przykład. int j=1; E1: j++; if (j<=100) goto E1; printf(”\n j=%d”, j); Język ANSI C – instrukcje Instrukcja goto (cz.2) Stosowanie instrukcji goto nie jest zalecane, gdyż zwykle jej wielokrotne użycie powoduje, iż program staje się mniej czytelny jak również trudniejszy do modyfikacji. Istnieją jednak sytuacje, gdzie instrukcja goto jest wygodnym rozwiązaniem. Przykład. int i,j,k; for ( i=0;i<100;i++) for (j=0;j<100;j++) for (k=0;k<50;k++) if (i+j+k>10000) goto E2; E2: printf (”\n Instrukcja poza petlami”); Język ANSI C – tablice Tablice w języku C są pewnymi strukturami danych stanowiącymi zbiory zmiennych tego samego typu, do których odwołujemy się przy użyciu wspólnej nazwy. Tablice jednowymiarowe mają strukturę w pewnym sensie zbliżoną do struktur matematycznych takich jak wektory, a tablice dwuwymiarowe do macierzy. W języku C tablice umieszczane są w ciągłych obszarach pamięci. Najniższy adres odpowiada pierwszemu elementowi tablicy, a najwyższy ostatniemu. Definicja tablicy jednowymiarowej ma następującą postać: typ nazwa_tablicy [rozmiar]; typ określa typ każdego elementu tablicy, rozmiar musi być określony w standardzie C89 przy użyciu wyrażenia stałego. Tak więc rozmiar tablicy jest ustalany w trakcie kompilacji. Dostęp do elementów tablicy uzyskuje się poprzez tzw. indeksowanie. Język ANSI C – tablice Indeksowanie realizuje się umieszczając indeks ( numer elementu tablicy ) w nawiasach kwadratowych za nazwą tablicy. W języku C elementy indeksowane są od zera. Przykład. int a[20]; a[0]=1; // wpisanie do pierwszego elementu tablicy wartości 1, // po lewej stronie zastosowano operator indeksowania i indeks 0 a[3]=2; a[5]=2*a[0]-5*a[3]+10; // do elementu a[5] wstawiana jest wartość 2. Elementy tablicy mogą być traktowane w ten sam sposób jak zmienne danego typu. Język ANSI C – tablice Poza zastosowaniem instrukcji podstawienia, elementom tablicy można nadać wartości przy użyciu tzw. inicjalizacji lub instrukcji wejścia. Przykład. Inicjalizacja tablicy. int a[5]={ 1, 3, -1, 4, 5}; Ilość elementów inicjalizujących może być równa lub mniejsza od ilości elementów tablicy. Jeżeli nie stosuje się inicjalizacji przy definicji tablicy, jej elementy mogą mieć wartości przypadkowe, gdy tablica jest definiowana wewnątrz funkcji. Tablice definiowane poza funkcjami bez inicjalizacji (tablice zewnętrzne) są zerowane. Język ANSI C – tablice Przykład. Wczytywanie i drukowanie tablicy #include <stdio.h> #include <conio.h> int main() { double x[10]; int i; for (i=0;i<10;i++) { printf(”\n Podaj element x[%d]=”,i); scanf(”%lf”,&x[i]); fflush(stdin); } for (i=0;i<10;i++) printf(”\n x[%d]=%f”,i, x[i]); getch(); return 0; } Język ANSI C – tablice Przykład. Wczytywanie i drukowanie tablicy ze sprawdzaniem formatu #include <stdio.h> #include <conio.h> int main() { double x[10]; int i; for (i=0;i<10;i++) { do { printf(”\n Podaj element x[%d]=”,i); k=scanf(”%lf”,&x[i]); fflush(stdin); } while (k==0); } for (i=0;i<10;i++) printf(”\n x[%d]=%f”,i, x[i]); getch(); return 0; Język ANSI C – tablice Przykład. Sumowanie różnych kategorii elementów tablicy. int a[10]={ 1, 3, -1, 4, 5,6,9,2,15,12}; int i, suma=0, suma_p=0, suma_ind=0, ind1=2, ind2=6; for (i=0;i<10;i++) for (i=0;i<10;i++) suma = suma + a[i]; if (a[i] %2==0) suma_p = suma_p+a[i]; for (i=ind1;i<=ind2 ;i++) suma_ind = suma_ind + a[i]; Język ANSI C – tablice Przykład. Wyszukiwanie w tablicy elementu minimalnego i maksymalnego int a[10]={ 1, 3, -1, 4, 5,6,9,2,15,12}; int i, amin, amax; amin=a[0]; for (i=1;i<10;i++) if ( a[i] < amin) amin = a[i]; amax=a[0]; for (i=1;i<10;i++) if ( a[i] > amax) amax= a[i]; printf(”\n element minimalny =%d ”,amin); printf(”\n element maksymalny =%d ”,amax); Język ANSI C – tablice Przykład. Wyszukiwanie w tablicy zadanej wartości i zapis wartości indeksów elementów równych tej wartości #include <stdio.h> #include <conio.h> int main(int argc, char* argv[]) { int a[10]={ 1, 2, -1, 4, 5,6,9,2,15,12}; int i, k=0,element, poz[10]; printf("\n Podaj poszukiwany element :"); scanf("%d",&element); for (i=0;i<10;i++) if ( a[i] ==element ) poz[k++]=i; if (k) { printf("\n Pozycje tablicy na ktorych wystepuje element=%d", element); for (i=0;i<k;i++) printf(" %d ", poz[i]); } else printf("\n Zadany element nie wystepuje w tablicy"); getch(); return 0; } Język ANSI C – tablice Sortowanie jest procesem ustawiania zbioru obiektów w określonym porządku. Sortowanie stosuje się w celu ułatwienia późniejszego wyszukiwania elementów sortowanego zbioru. Problem sortowania można sformułować następująco: Dane są obiekty a1 , a2 ,..., an Sortowanie (niemalejąco) polega na przestawianiu tych obiektów aż do chwili osiągnięcia uporządkowania ak1 , ak2 ,..., ak3 takiego, że dla danej funkcji porządkującej f zachodzi związek: f (ak1 ) ≤ f (ak2 ) ≤ ... ≤ f (ak3 ) Język ANSI C – tablice Zwykle nie oblicza się wartości funkcji porządkującej, lecz przechowuje się je w jawnej postaci jako składowe każdego obiektu. Wartość tej funkcji nazywana jest kluczem obiektu. Dla tablic kluczem obiektu jest wartość elementu. Metody sortowania obiektów w miejscu można podzielić na trzy zasadnicze grupy: - sortowanie przez zamianę (ang. exchange sort), - sortowanie przez wybieranie (selekcję, ang. selection sort), - sortowanie przez wstawianie (ang. insertion sort), Przykłady metod: - sortowanie przez prostą zamianę ( sortowanie bąbelkowe) - sortowanie przez proste wybieranie, - sortowanie przez proste wstawianie, Język ANSI C – tablice Sortowanie przez prostą zamianę( sortowanie bąbelkowe) Metoda ta polega na porównywaniu w kolejnych przebiegach kluczy kolejnych sąsiadujących elementów (dla tablic wartości elementów tablicy) i ewentualnej zamianie miejscami tych elementów, jeżeli nie spełniają zadanej relacji. Po zakończeniu danego przebiegu następuje sprawdzenie czy nastąpiły w nim zamiany. Jeśli tak, realizowany jest kolejny przebieg, jeśli nie, to sortowanie zostało zakończone. Język ANSI C – tablice Sortowanie przez prostą zamianę( sortowanie bąbelkowe) Przykład. Posortować niemalejąco tablicę 5-elementową zawierającą elementy 8,6,10,2,1. Pokazane zostaną poniżej stany tablicy w kolejnych przebiegach. I. przebieg 8,6,10,2,1 6,8,10,2,1 6,8,10,2,1 6,8,2,10,1 6,8,2,1,10 II. przebieg 6,8,2,1,10 6,8,2,1,10 6,2,8,1,10 6,2,1,8,10 6,2,1,8,10 III. przebieg 6,2,1,8,10 2,6,1,8,10 2,1,6,8,10 2,1,6,8,10 2,1,6,8,10 IV. przebieg 2,1,6,8,10 1,2,6,8,10 1,2,6,8,10 1,2,6,8,10 1,2,6,8,10 V. przebieg 1,2,6,8,10 1,2,6,8,10 1,2,6,8,10 1,2,6,8,10 1,2,6,8,10 Język ANSI C – tablice // Sortowanie przez prostą zamianę (bąbelkowe) #define n 5 // sortowanie niemalejąco #include <stdio.h> #include <conio.h> int main(int argc, char **argv) { int i, nr=1,p, zam; int a[5]={8,6,10,2,1}; do { zam=0; printf("\n przebieg nr=%d",nr++); for (i=0;i<n -1;i++) if (a[i+1]<a[i]) { p=a[i]; a[i]=a[i+1]; a[i+1]=p; zam=1; } } while (zam); printf("\n Stan wektora po sortowaniu"); for (i=0;i<n;i++) printf(" % d ",a[i]);getch(); } Język ANSI C – tablice Sortowanie przez proste wybieranie Metodę tę można opisać następująco:( sortowanie niemalejąco) - wybrać element o najmniejszym kluczu ( dla tablic element o najmniejszej wartości) - wymienić go z pierwszym elementem. Operacje te powtarza się z pozostałymi n-1 obiektami, następnie z n-2 obiektami, aż pozostanie jeden obiekt - największy. Język ANSI C – tablice // Sortowanie przez proste wybieranie #define n 8 #include <stdio.h> #include <conio.h> int main(int argc, char **argv) { int i,i1,j,k,p,amin; int a[8]={9,5,-2,7,-4,15,-20,100}; for (i=0;i<n;i++) { amin=a[i]; k=i; for (j=i+1;j<n;j++) if (a[j]<amin) { amin=a[j]; k=j; } if (k!=i) { p=a[i]; a[i]=a[k]; a[k]=p; } } printf("\n Stan wektora po sortowaniu"); for (i=0;i<n;i++) printf(" % d ",a[i]); getch();} Język ANSI C – tablice Sortowanie przez proste wstawianie Metoda ta jest stosowana przez grających w karty. Zaczynamy od „ pustej” lewej ręki, po czym bierzemy ze stołu kolejne karty i wstawiamy w odpowiednie miejsca w zbiorze kart trzymanych w lewej ręce. Dla danej karty znajdujemy właściwe miejsce, porównując ją kolejno z kartami w lewej ręce. 9 5 2 7 5 9 2 5 9 2 5 7 9 1 2 5 7 1 9 Język ANSI C – tablice #define n 5 // Sortowanie przez wstawianie #include <stdio.h> #include <conio.h> int main(int argc, char **argv) int i,j,x; int a[5]={9,5,2,7,1}; j=0; while (j<n) { x=a[j]; i=j-1; while (i>=0 && a[i]>x) { a[i+1]=a[i]; i=i-1; } a[i+1]=x; j=j+1; } printf("\n Stan wektora po sortowaniu"); for (i=0;i<n;i++) printf(" % d ",a[i]);getch(); } Język ANSI C – funkcje W języku ANSI C program składa się z jednego lub większej liczby segmentów kodu zwanych funkcjami, z których jedna musi nazywać się main(). Wykonanie programu zaczyna się od funkcji main() i program powinien kończyć się wraz z zakończeniem działania funkcji main()( program również może się zakończyć, gdy pojawi się sytuacja nienormalna-wyjątek, np. dzielenie przez zero, albo też gdy zostanie zakończony w sposób jawny- ctrl-break, powodem zakończenia może być też wywołanie funkcji exit(). Język ANSI C – funkcje Powody stosowania funkcji w programach 1. Program jest zbyt duży by pisać go jako jedną funkcję main(). 2. Podział zadania programistycznego na mniejsze, które mogą być opracowywane niezależnie( np. w różnym czasie lub równocześnie przez różnych programistów). 3. Modularyzacja programu- podział programu na jednostki realizujące wyodrębnione logicznie zadania. 4. Ułatwienia w uruchamianiu dużych programów ( można niezależnie uruchamiać i testować poszczególne funkcje). 5. Korzystanie z bibliotek lub funkcji stworzonych przez innych programistów. 6. Unikanie powtarzania w programie tego samego kodu, gdy pewne zadania trzeba wykonywać wielokrotnie. Język ANSI C – funkcje Funkcje a struktura programu w jezyku C (cz. I) -Program w języku C może być programem jednoplikowym lub wieloplikowym. W programie jednoplikowym wszystkie funkcje znajdują się w jednym pliku lub są do niego dołączane przy zastosowaniu dyrektywy #include. W programie wieloplikowym pliki są łączone w sposób zależny od środowiska ( np. w C++ Builder opcja Project >Add to Project). - Funkcje nie muszą występować w pliku w określonym porządku ( pewne sposoby uporządkowania mogą wymagać dodatkowych deklaracji), jednak umieszczenie main() jako pierwszej funkcji w pliku ułatwia analizę programu). Język ANSI C – funkcje Funkcje a struktura programu w języku C (cz. II) - Każda funkcja może wywoływać dowolną inną funkcję. - Nie jest dopuszczalne zagnieżdżanie funkcji, tzn. nie można jednej funkcji definiować wewnątrz innej funkcji. - W programie występuje tylko jedna funkcja main() i musi ona wystąpić niezależnie od liczby plików źródłowych. Język ANSI C – funkcje Przykład. Prosty program stosujący funkcję do obliczania kwadratu pewnej wartości. #include <stdio.h> #include <conio.h> int kwadrat (int num) { int pom; // zmienna lokalna funkcji pom=num*num; return pom; } // zmienna pom nie jest konieczna, może być // return num*num; main() { int liczba, wartosc; printf(”\n Podaj wartosc :”); scanf(”%d”,&wartosc); liczba=kwadrat(wartosc);/* zmienna wartosc jest tzw. argumentem funkcji, po prawej stronie znaku podstawienia umieszczono tzw. wywołanie funkcji czyli uruchomienie jej kodu. Zmienna wartość zawiera liczbę, która ma być podniesiona do kwadratu */ Język ANSI C – funkcje printf (”\n Kwadrat liczby %d wynosi :%d”, wartosc, liczba); getch(); return 0; } Język ANSI C – funkcje #include <stdio.h> #include <conio.h> int kwadrat (int); // deklaracja funkcji main() { int liczba, wartosc; printf(”\n Podaj wartosc :”); scanf(”%d”,&wartosc); liczba=kwadrat(wartosc); printf (”\n Kwadrat liczby %d wynosi :%d”, wartosc, liczba); getch(); return 0; } int kwadrat (int num) { int pom; pom=num*num; return pom; } Język ANSI C – funkcje Zadanie. Napisać i wywołać w main() dla dwóch par argumentów następujące funkcje: a) funkcję obliczającą sumę swoich argumentów typu int b) funkcję obliczającą różnicę swoich argumentów typu int c) funkcję obliczającą iloczyn swoich argumentów typu int Język ANSI C – funkcje Przykład. Napisać i zastosować w programie funkcję silnia. #include <stdio.h> #include <conio.h> int silnia (int n ) { int i, sil=1; if (n>0) for (i=1;i<=n;i++) sil=sil*i; return sil; } main() { int l1=5, l2=6; sil1=silnia (l1); sil2=silnia (l2); printf(”\n % d ! wynosi %d”, l1,sil1); printf(”\n % d ! wynosi %d”, l2,sil2); getch(); return 0; } Język ANSI C – funkcje Przykład. Napisać i zastosować w programie funkcję wczytajd wczytującą zmienną typu double. #include <stdio.h> #include <conio.h> double wczytajd (void ) { int k; double x; do { printf(”\n Podaj zmienna :”); k=scanf(”%lf”,&x); if (k==0) printf(”\n Niepoprawny format liczby !”); fflush(stdin); } while (k==0); // można też while (!k); return x; } main() { double x1,x2; x1=wczytajd (); x2=wczytajd(); printf(”\n x1 wynosi %f”, x1); printf(”\n x2 wynosi %f”, x2); getch(); return 0; } Język ANSI C – funkcje Ogólna budowa funkcji Definicja funkcji Typ_zwracanej_wartości nazwa_funkcji (deklaracja parametrów formalnych, jeśli występują) { deklaracje instrukcje } Definicje poszczególnych funkcji mogą się pojawić w jednym pliku lub większej liczbie plików źródłowych, przy czym żadna funkcja nie może być rozdzielona między plikami (definicja funkcji musi być umieszczona w jednym pliku). Język ANSI C – funkcje Deklaracja funkcji ( cz. I) Deklaracja funkcji stanowi informację dla innych funkcji stosujących daną funkcję o typie zwracanej wartości, liczbie i typach parametrów funkcji. Deklaracja ma postać skróconego nagłówka funkcji zakończonego średnikiem. Przykład. Deklaracje funkcji int kwadrat (int, int); int echo_line (void); void print_hist ( int size); void print_prompt( void); Nazwy po typach parametrów są opcjonalne i są ignorowane przez kompilator. Nazwy parametrów stosować można dla celów dokumentacji, pozwalają one zorientowąć się co do roli poszczególnych parametrów bez sięgania do definicji funkcji. Język ANSI C – funkcje Definicja a deklaracja funkcji Definicja funkcji występować jednokrotnie. 1. Deklaracja funkcji f może występować wewnątrz funkcji g , która wywołuje daną funkcję f lub też na zewnątrz wszystkich funkcji. 2. Deklaracja funkcji f wewnątrz funkcji g służy tylko funkcji g. 3. Deklaracja funkcji f na zewnątrz wszystkich funkcji służy wszystkim tym funkcjom, których definicje umieszczono po deklaracji funkcji f . Język ANSI C – funkcje Instrukcja return (cz. I) Instrukcja return ( ang. powróć, zwróć) pełni dwie zasadnicze funkcje: 1. Zwraca sterowanie programem do miejsca wywołania funkcji ( do funkcji wywołującej). 2. Umożliwia przekazanie wartości obliczonej przez funkcję do funkcji wywołującej. Język ANSI C – funkcje Instrukcja return cz. II Funkcja, która zwraca wartość, powinna mieć przynajmniej jedną instrukcję return w formie return (wyrażenie); lub return wyrażenie; gdzie wyrażenie jest jakimkolwiek legalnym wyrażeniem języka C. Obie formy mają to samo znaczenie. Do funkcji wywołującej zwracana (przekazywana) jest wartość wyrażenia. Funkcja przy wywołaniu może zwrócić co najwyżej jedną wartość. Język ANSI C – funkcje Instrukcja return cz. III Przykład. Instrukcja return. a) return ( 25*5 + fun2(x); b) return (25*5+ fun2(x)); c) double modul ( double x) { return x>0 ? x : -x; } Niezależnie od tego czy wyrażenie po return jest skomplikowane, czy też nie, zwracana jest jedna wartość. Język ANSI C – funkcje Instrukcja return IV Funkcja może posiadać kilka instrukcji return, jednak w czasie danego wywołania funkcji może zostać wykonana tylko jedna instrukcja return. Nie powinno się stosować w funkcji kilku instrukcji return, gdyż funkcja staje się mniej zrozumiała oraz trudniejsza do testowania i modyfikacji. Instrukcja return nie jest konieczna w funkcji, która nie zwraca żadnej wartości, ale jeśli jest używana to w postaci return; Jeśli nie ma instrukcji return, wykonywane są kolejne instrukcje w treści funkcji, aż do momentu dojścia do nawiasu zamykającego funkcję. Język ANSI C – wskaźniki Wskaźniki cz. I Wskaźniki to typ obiektów, przechowujących adresy innych obiektów np. zmiennych. Adres to konkretna lokalizacja pewnego obiektu w pamięci. Jeżeli pewien obiekt typu wskaźnikowego (np. zmienna) przechowuje adres innego obiektu, to mówimy, że wskazuje na ten obiekt. Język ANSI C – wskaźniki Adres pamięci Zawartość 1000 1008 1004 20 1008 0 1012 160 1016 1 Zmienna umieszczona pod adresem 1000 wskazuje na zmienną umieszczoną pod adresem 1008. Język ANSI C – wskaźniki Definicja prostej zmiennej wskaźnikowej Jeżeli zmienna ma przechować adresy innych obiektów, należy ją odpowiednio zdefiniować. Definicja zmiennej wskaźnikowej składa się z nazwy typu wskazywanych obiektów, gwiazdki oraz nazwy zmiennej typ_wskazywanych_obiektów * nazwa_zmiennej ; typ_wskazywanych_obiektów zwany jest zwykle typem wskaźnika. Przykład. Definicja zmiennej wskazującej na obiekty typu int. int * px; Język ANSI C – wskaźniki Operatory wskaźnikowe W języku C istnieją dwa operatory wskaźnikowe: - operator adresowy & - operator adresowania pośredniego * ( dereferencji) Jednoargumentowy operator & przyłożony do operandu zwraca adres operandu w pamięci ( dokładniej - wyrażenie &operand reprezentuje adres operandu). Jednoargumentowy operator * przyłożony do operandu wskaźnikowego, zwraca wartość znajdującą się pod adresem zawartym w obiekcie (zmiennej) występującym po operatorze. Język ANSI C – funkcje Przykład. Proste działania przy użyciu wskaźników. int *px, *py; // definicja zmiennej wskaźnikowej px int i=5, j,k=10; px=&i; // przypisanie adresu zmiennej i wskaźnikowi px; j=*px; // podstawienie i do j przy użyciu wskaźnika *px= i+k; // podstawienie sumy i+k do i py=px; *py=20; // podstawienie do i wartości 20 printf(”\n i=%d”, *py); // drukowanie i przy użyciu wskaźnika py *py=*py+5; printf(”\n i=%d”, *py); Język ANSI C – funkcje Przykład. Wczytywanie i drukowanie zmiennej przy użyciu wskaźnika #include <stdio.h> #include <conio.h> main() { double *p; double x; p=&x; printf(”\n Podaj x:”); scanf(”%lf ”, p); printf(”\n x=%f”,*p); getch(); return 0; } Język ANSI C – funkcje Wskaźniki i tablice Nazwa tablicy w C jest stałym wskaźnikiem do pierwszego elementu tablicy. Np. jeżeli stworzymy definicje int a[5]; int *pa; i zastosujemy instrukcję pa=a; to wskaźnik pa będzie wskazywał na a[0], czyli pierwszy element tablicy a. Stosując wskaźniki można wykonać te same operacje na tablicach, co przy użyciu indeksowania. Język ANSI C – funkcje Przykład. Proste działania na tablicach przy użyciu wskaźników #include <stdio.h> #include <conio.h> main() { int a[10]; int *pa,i; pa=a; // przypisanie wskaźnikowi pa adresu a[0] *pa=10; // zapis 1 do a[0] pa++; // zwiększenie wskaźnika, wskazuje on teraz na a[1] *pa=20; // zapis 2 do a[1]; pa=pa+1; // wskaźnik wskazuje na element a[2] for (i=2; i<10; i++) { printf("\n Podaj a[%d]=",i); scanf("%d",pa); pa++; } pa=a; // ustawienie wskaźnika na początek tablicy // alternatywnie pa= pa -10 for (i=0;i<10;i++) printf("\n a[%d]=%d", i,*pa++); getch();} Język ANSI C – funkcje Komunikacja funkcji z otoczeniem Każda funkcja w C może dysponować mechanizmami komunikacji z otoczeniem. Funkcja może pobierać wartości lub adresy od funkcji wywołującej, może zwracać wartość do funkcji wywołującej, jak również modyfikować argumenty, których adresy zostały do niej przekazane. Istnieją dwa główne sposoby przekazywania argumentów: - przekazywanie przez wartość - przekazywanie przez zmienną( przekazywanie przez referencję, przez wskaźnik) Język ANSI C – funkcje Przekazywanie przez wartość Przy przekazywaniu przez wartość, wartości argumentów są kopiowane do parametrów formalnych, zmiany parametrów formalnych nie mają wpływu na wartości argumentów ( potocznie mówi się, że funkcja działa na kopiach argumentów). Przy takim sposobie przekazywania parametrów funkcja wywoływana nie może zmienić argumentów zastosowanych przy wywołaniu. Argument może być wyrażeniem, którego wartość jest najpierw obliczana i następnie przekazywana do funkcji wywołującej. Język ANSI C – funkcje Przykład. Przekazywanie przez wartość. #include <stdio.h> #include <conio.h> int sqr (int); // deklaracja funkcji main() { int t=10, kw; kw=sqr(t); // argument wywołania – zmienna t nie jest modyfikowana, pomimo że // parametr formalny odpowiadający t, jest modyfikowany. printf (”\n Kwadrat liczby %d wynosi :%d”,t, kw); getch(); return 0; } int sqr (int k) { k=k*k; return k; } // Przypisanie k=k*k modyfikuje tylko lokalną zmienną k. Język ANSI C – funkcje Przekazywanie przez zmienną Przy przekazywaniu przez zmienną, do funkcji przekazuje się wskaźnik do argumentu ( adres argumentu), a nie jego wartość. Funkcja wywoływana dysponując tym wskaźnikiem może zmienić argument znajdujący się poza funkcją. Mechanizm przekazywania przez zmienną umożliwia przekazanie na zewnątrz funkcji więcej niż jedną wartość. Język ANSI C – funkcje Przykład. Funkcja zwracająca dwie wartości #include <stdio.h> #include <conio.h> int dodaj_mnoz ( int , int, int *); main() { int x=10, y=20, suma, iloczyn; suma=dodaj_mnoz(x,y,&iloczyn); printf(”\n Suma= %d iloczyn=%d ”, suma, iloczyn); getch(); return 0; } int dodaj_mnoz ( int a, int b, int pc*) { int sum, ilo; sum=a+b; ilo=a*b; *pc=ilo; return sum; } Język ANSI C – funkcje Przykład. Funkcja zamieniająca swe argumenty. #include <stdio.h> #include <conio.h> void swap ( int *, int *); main() { int i=10, j=20; printf(”\n Wartości i oraz j przed zamianą i=%d j=%d:”, i,j); swap(&i, &j); printf(”\n Wartości i oraz j po zamianie i=%d j=%d:”, i,j); getch(); return 0; } void swap ( int *px, int *py) { temp=*px;// zapisz do temp wartość znajdującą się pod // adresem px *px=*py; // umieść w komórce wskazywanej przez px, // wartość znajdującą się pod adresem // wskazywanym przez py *py=temp; // umieść w komórce wskazywanej przez py, // wartość z komórki temp } Język ANSI C – funkcje Przekazywanie tablic jako argumentów Gdy argumentem funkcji wywoływanej jest tablica, do funkcji przekazywany jest jej adres ( adres pierwszego elementu). W związku z tym funkcja operuje na przekazanej jej tablicy, a nie na kopii. Funkcja ma możliwość zmiany tej zawartości. Język ANSI C – funkcje Przykład. Funkcja oblicza sumę elementów tablicy i umieszcza tę sumę w pierwszym elemencie tablicy , #include <stdio.h> #include <conio.h> void suma_tab( int [ ], int ); main() { int sum, a[5]= { 0,1,2, -1,3}; suma_tab(a,5); printf(”\n Suma elementów tablicy a=%d”, a[0]); getch(); return 0; } void suma_tab( int b[ ], int n) { int i,suma; for (i=0;i<n; i++) suma=suma+b[i]; b[0]=suma; } Język ANSI C – funkcje Przykład. Funkcje wczytujące i drukujące tablicę jednowymiarową. #include <stdio.h> #include <conio.h> void wczyt_tab( int [ ], int ); void druk_tab( int [ ], int ); main() { int a[5],b[10]; wczyt_tab(a,5); wczyt_tab(b,10); druk_tab(a,5); druk_tab(b,10); getch(); return 0; } void wczyt _tab( int x[ ], int n) { int i; for (i=0;i<n; i++) { printf(”\n Podaj element %d=”,i); scanf(”%d”,&x[i]);} } Język ANSI C – funkcje Przykład cd. Funkcje wczytujące i drukujące tablicę jednowymiarową. void druk _tab( int x[ ], int n) { int i; for (i=0;i<n; i++) printf(”\n Element %d=%d ”,i, x[i]); } Język ANSI C – funkcje Przykład. Funkcja obliczająca w tablicy sumy liczb parzystych, nieparzystych, dodatnich i ujemnych. #include <stdio.h> #include <conio.h> void wczyt_tab( int [ ], int ); int oblicz_sumy(int [ ], int n,int *pnp,int *pd, int *pu); main() { int a[10], suma_p, suma_np, suma_d, suma_u; wczyt_tab(a,10); suma_p=oblicz_sumy(a,10,&suma_np, &suma_d, &suma_u); printf("\n suma_p=%d suma_np=%d suma_d=%d suma_u=%d",\ suma_p, suma_np, suma_d, suma_u); getch(); return 0; } void wczyt_tab(int x[ ], int n) { int i; for (i=0;i<n; i++) { printf("\n Podaj element %d=",i); scanf("%d",&x[i]);} } Język ANSI C – funkcje Przykład cd. Funkcja obliczająca w tablicy sumy liczb parzystych, nieparzystych, dodatnich i ujemnych ( wersja 1) int oblicz_sumy(int x[ ], int n, int *pnp, int *pd, int *pu) { int i, suma_p=0, suma_np=0, suma_d=0, suma_u=0; for (i=0;i<n; i++) { if (x[i]%2==0) suma_p+=x[i]; else suma_np+=x[i]; if (x[i]>0) suma_d+=x[i]; else suma_u+=x[i]; *pnp=suma_np; *pd=suma_d; *pu=suma_u; } return suma_p; } Język ANSI C – funkcje Przykład cd. Funkcja obliczająca w tablicy ilości liczb parzystych, nieparzystych, dodatnich i ujemnych ( wersja 2- krótsza) int oblicz_sumy(int x[ ], int n, int *pnp, int *pd, int *pu) { int i,suma_p=0; *pnp=*pd=*pu=0; for (i=0;i<n; i++) { if (x[i]%2==0) suma_p+=x[i]; else *pnp+=x[i]; if (x[i]>0) *pd+=x[i]; else *pu+=x[i]; } return suma_p; } Język ANSI C – tablice wielowymiarowe W języku C poza tablicami jednowymiarowymi można stosować tablice wielowymiarowe. Tablice dwuwymiarowe odpowiadają macierzom. Możliwość definiowania tablic wielowymiarowych w C wynika stąd, że elementy tablicy mogą być tablicami. Zapis int a[3][3]; definiuje tablicę o trzech wierszach i trzech kolumnach, lub też jedno-wymiarową tablicę o trzech elementach, z których każdy jest tablicą składającą się z trzech elementów. Elementy tablicy a zapisujemy jako a[i] [j]. Język ANSI C – tablice wielowymiarowe Inicjalizacja tablic dwuwymiarowych Przykład. Zainicjować tablicę b[3] [3]. int b[3][3]= { {2,1,1}, {3,4,2}, {2,3,4}}; W wyniku takiej inicjalizacji otrzymujemy następujące wartości elementów : b[0][0] :2 b[0][1] : 1 b[1][0] :3 b[1][1] : 4 b[2][0] :2 b[2][1] : 3 b[0][2] :1 b[1][2] :2 b[2][2] :3 Tablicę b [3][3] można też zainicjować w sposób następujący: int b[3][3]= {2,1,1,3,4,2,2,3,3}; Tablice wielowymiarowe są zapamiętywane w C wierszami ( ostatni indeks z prawej strony zmienia się najszybciej ). Przykładowo dla tablicy b mamy następujące rozmieszczenie tablicy w pamięci b[0][0], b[0][1], b[0][2], b[1][0], b[1][1], b[1][2], b[2][0], b[2][1], b[2][2]. Język ANSI C – tablice wielowymiarowe i funkcje Gdy tablica wielowymiarowa jest parametrem funkcji, konieczne jest podanie wszystkich wymiarów poza pierwszym. Przykład. Napisać program wczytujący i drukujący dwie tablice o wymiarze 3x3. #include <stdio.h> #include <conio.h> void wczyt2D( int x[ ][3], int n) { int i,j,k; for (i=0; i<n;i++) for (j=0;j<3;j++) do { printf (”\n Podaj element [%d][%d]=”,i, j); k=scanf(”%d”, &x[i][j]); fflush(stdin); } while (k==0); return; } Język ANSI C – tablice wielowymiarowe i funkcje Przykład c.d. void druk2D( int x[ ][3], int n) { int i,j,k; for (i=0; i<n;i++) { for (j=0;j<3;j++) printf (”element [%d][%d]=%d”,i, j, x[i][j]); printf(”\n”); } return; } int main() { int a[3][3], b[3][3]; wczyt2D(a,3); wczyt2D(b,3); druk2D(a,3); druk2D(b,3); getch(); return 0; } Język ANSI C – funkcje #include <stdio.h> #include <conio.h> void kopiuj2D( int x[ ][3], int n); int main() { int b[3][3]= { {2,1,1}, {3,4,2}, {2,3,4}}; int a[3][3]; kopiuj2D( a,b); getch(); } void kopiuj2D( int x[ ][3],y[ ][3], int n) { int i,j,k; for (i=0; i<n;i++) for (j=0;j<3;j++) x[i][y]=y[i][j]; } Język ANSI C – funkcje Przykład. Przepisywanie tablicy dwuwymiarowej do tablicy jednowymiarowej. #include <stdio.h> #include <conio.h> void kopiuj1D_2D( int b[ ][2],int n1, int d[ ]) { int i,j,k=0; for (i=0;i<n1;i++) for (j=0;j<2;j++) d[k++]=b[i][j]; } int main () { int tab2D[2][2]={ {1,2},{2,3}}; int tab1D[4]; int i; kopiuj1D_2D(tab2D, 2, tab1D); for (i=0;i<4; i++) printf(" %d ", tab1D[i]); getch(); } Język ANSI C – funkcje Przykład. Mnożenie macierzy. #include <stdio.h> #include <conio.h> #define m 3 void mnoz_mac( int a[ ][m],int b[][m], int c[ ][m], int n) { int i,j,k,s; for (i=0;i<n;i++) for (k=0;k<n;k++) { s=0; for (j=0;j<n;j++) s=s+a[i][j]* b[j][k]; c[i][k]=s; } } int main () { int u[m][m]={ {1,2,1},{1,1,1}, {2,1,1}}; int v[m][m]={ {2,1,0},{1,2,0}, {3,2,1}}; mnoz_mac( u,v, z,3); for (i=0; i<n;i++) { for (j=0;j<3;j++) printf (”element [%d][%d]=%d”,i, j, x[i][j]); printf(”\n”); getch(); } Język ANSI C – dynamiczna alokacja pamięci cz. I W języku ANSI C istnieją zasadniczo dwa rodzaje zmiennych: - zmienne zwykłe definiowane w funkcji main() lub na zewnątrz wszystkich funkcji, lub w pewnym bloku. Każda zmienna tego rodzaju posiada swoją nazwę oraz określony typ. - zmienne dynamiczne tworzone i usuwane w trakcie działania programu; taki sposób przydzielania pamięci zwany jest alokacją w trakcie działania programu (ang. run-time allocation). Zmienne te nie posiadają nazw, znane są wyłącznie adresy przydzielonej im pamięci ( wskaźniki do tej pamięci). Do przydzielania pamięci zmiennym dynamicznym służą w ANSI C funkcje malloc i calloc. Do usuwania zmiennych dynamicznych stosuje się funkcję free. Język ANSI C – dynamiczna alokacja pamięci cz. I Funkcje malloc i calloc (stdlib.h) Każda z tych funkcji alokuje przydziela pamięć i zwraca adres tej pamięci (wskaźnik do tej pamięci). Rozmiar przydzielanej pamięci nie musi być znany podczas kompilacji. Funkcja malloc Nagłówek funkcji tej ma postać następującą: void * malloc (int); Funkcja malloc oczekuje, jako swojego argumentu liczby bajtów, które mają być przydzielone w danym wywołaniu funkcji. Jeżeli przydzielenie pamięci jest możliwe, funkcja zwraca wskaźnik do tej pamięci, jeśli nie, funkcja zwraca NULL (zerowy wskaźnik). Zwracany wskaźnik jest typu void*, czyli jest to wskaźnik do void. Wskaźnik ten musi być przekształcony (przez rzutowanie) na wskaźnik do żądanego typu. Język C gwarantuje, że wskaźnik do void może być przekształcony na wskaźnik do każdego innego typu. Język ANSI C – dynamiczna alokacja pamięci Przykład. Zastosowanie funkcji malloc do alokacji pamięci dla zmiennej dynamicznej typu int. #include <stdio.h> #include <conio.h> int main() { int *ptr; ptr=(int*) malloc( sizeof(int)); if (ptr==NULL){ printf(”\n Przydzielenie pamięci nie było możliwe”); getch();return 1;} printf(” Podaj wartosc :”); scanf(”%d”, ptr); printf(”\n Wartosc to :”, *ptr); free(ptr); getch(); return 0; } Język ANSI C – dynamiczna alokacja pamięci cz. I Funkcja calloc Nagłówek funkcji tej ma postać następującą: void * calloc (int,int); Funkcja calloc oczekuje dwóch argumentów typu int. Pierwszy argument oznacza liczbę bloków pamięci, które mają zostać przydzielone, a drugi rozmiar pojedynczego bloku. Funkcja zwraca wskaźnik do pierwszego bloku. Wskaźnik ten jest typu void* i musi być rzutowany na wskaźnik do wymaganego typu. Język ANSI C – funkcje Przykład. Zastosowanie funkcji calloc do alokacji pamięci dla tablicy liczb typu int. #include <stdio.h> #include <conio.h> int main() { int *ptr; ptr=(int *) calloc(5, sizeof(int)); for (i=0;i<5;i++) { printf(”\n Podaj element %d”, i); scanf(”%d”, ptr++); } ptr=ptr-5; for (i=0;i<5;i++) { printf(”\n Element [%d]=%d”, *ptr++); ptr-=5; free(ptr); getch(); Język ANSI C – dynamiczna alokacja pamięci cz. I Przykład. Dynamiczna alokacja tablicy z użyciem funkcji zwracającej wskaźnik. #include <stdio.h> #include <conio.h> #include <stdlib.h> int * wczyt_tab(int n); int main(int argc, char **argv) { int *pa,i,n=5; pa=wczyt_tab(5); for (i=0;i<n;i++) printf("\n Element[%d]=%d",i, *pa++); pa-=5; free(pa); getch();} Język ANSI C – funkcje Przykład cz. II ( ciąg dalszy programu) // definicja funkcji zwracającej wskaźnik do wczytanej tablicy int * wczyt_tab(int n) { int i,*px; px=(int*) malloc(n*sizeof(int)); for (i=0;i<n;i++) { printf("\n Podaj element[%d]=",i); scanf("%d",px++); } px=px-n; return px; } Język ANSI C – funkcje Przykład. Program wczytujący tablicę dwuwymiarową przy użyciu wskaźnika, do funkcji przekazywany jest wskaźnik do pierwszego elementu tablicy dwuwymiarowej. #include <stdio.h> #include <conio.h> #include <stdlib.h> //Do funkcji przekazywany jest wskaźnik do pierwszego elementu tablicy //dwuwymiarowej oraz liczba wierszy i liczba kolumn. void wczyt(int *px, int w, int k) { int i,j; for (i=0;i<w;i++) for (j=0;j<k;j++) { printf("\n Podaj element [%d][%d]=",i,j); scanf("%d",px++); } } Język ANSI C – funkcje // Przykład c.d. zastosowanie funkcji wczyt do wczytania // tablicy dwuwymiarowej 3x3. int main() { int x[3][3]; int *px; int i,j; clrscr(); px=&x[0][0]; wczyt(px,3,3); for (i=0;i<3;i++) for (j=0;j<3;j++) printf("\n element [%d][%d]=%d",i ,j, x[i][j]); getch(); return 0; } Język ANSI C – funkcje Przykład. Funkcja wczyt_D alokuje pamięć dla tablicy dwuwymiarowej o zadanej liczbie kolumn i zadanej liczbie wierszy, wczytuje tablicę i zwraca wskaźnik do jej pierwszego elementu. int *wczyt_D(int w, int k) { int *px; int i,j; px=(int*)calloc(w*k,sizeof(int)); for (i=0;i<w;i++) for (j=0;j<k;j++) { printf("\n Podaj element [%d][%d]=",i,j); scanf("%d",px++); } px=px-w*k; return px; } Język ANSI C – funkcje // Przykład c.d. zastosowanie funkcji wczyt_D do wczytania // tablicy dwuwymiarowej 3x3. int main() { int *py,i,j; clrscr(); py=wczyt_D(3,3); for (i=0;i<3;i++) for (j=0;j<3;j++) printf("\n element [%d][%d]=%d",i,j,*py++); py-=9; free(py); getch(); return 0;} Język ANSI C – operatory bitowe Język C pozwala programiście na bezpośrednie współdziałanie ze sprzętem ( hardware’m) poprzez użycie operatorów bitowych i wyrażeń bitowych. Operatory bitowe umożliwiają działanie na pojedynczych bitach. Operatory te działają wyłącznie z typami całkowitymi np. takimi jak char czy też int. Aby stosować operatory bitowe w danym systemie powinno się zgromadzić następujące informacje: - liczba bitów, które tworzą bajt, - liczba bajtów dla typów całkowitych, - system kodowania znaków (np. ASCII) - typ stosowanej reprezentacji liczb ujemnych( np. kod U2) Poniżej założymy 8-bitowy bajt i 16-bitową komórkę dla przechowywania liczb całkowitych oraz kod U2. Język ANSI C – operatory bitowe Operatory bitowe to: - ‘ ~ ‘ - bitowy operator uzupełnienia do 1, -’&’ - bitowy operator koniunkcji (iloczynu logicznego), -’ | ‘ - bitowy operator alternatywy (sumy logicznej), - ’^’ - bitowy operator różnicy symetrycznej (XOR), - ‘<<‘ - bitowy operator przesunięcia w lewo, - ‘>>’ - bitowy operator przesunięcia w prawo. Język ANSI C – operatory bitowe Bitowy operator uzupełnienia do 1, ‘ ~ ‘ (zwany tyldą), neguje poszczególne bity swojego argumentu (zamienia zera na jedynki a jedynki na zera). Przykład. short int x,y; x=6; y=~x; printf(„\n Wartosc y=%d”, y); Wartość ~x wynosi 7. Wynika to ze stosowania kodu U2 reprezentowania liczb ujemnych. Liczba 6 ma następującą reprezentację binarną 0000 0000 0000 0110 Po negacji otrzymujemy 1111 1111 1111 1001 czyli -7 w U2. ( Można to łatwo sprawdzić: +7 0000 0000 0000 0111 -> -7 w U1 1111 1111 1111 1000 , czyli po dodaniu 1, otrzymujemy 1111 1111 1111 1001) Język ANSI C – operatory bitowe Rozszerzenie znakowe ( ang. sign extension) W operacjach wykonywanych na bitach występuje problem konwersji pomiędzy różnymi formatami liczb całkowitych (np. char i short int). Przy konwersjach takich istnieje konieczność zachowania wartości konwertowanej liczby. Dla liczb ujemnych przy przechodzeniu z formatu krótszego na dłuższy, wszystkie bity starsze, które nie występowały w formacie krótszym zamieniane są na 1. Działanie takie nie powoduje zmiany wartości liczby ujemnej. Przykład. Dana jest liczba -6 w kodzie U2 umieszczona w komórce 8-bitowej 0000 0110 +6 w U2, 1111 1001 -6 w U1 1111 1010 -6 w U2 Po rozszerzeniu do 16 bitów mamy 1111 1111 1111 1010 Można sprawdzić, że kod ten reprezentuje -6 w U2. +16 0000 0000 0000 0110, - 6 w U1 1111 1111 1111 1001 , -6 w U2 1111 1111 1111 1010 Język ANSI C – operatory bitowe Bitowe operatory logiczne -’&’ -’ | ‘ - ’^’ - bitowy operator koniunkcji (iloczynu logicznego), - bitowy operator alternatywy (sumy logicznej), - bitowy operator różnicy symetrycznej (XOR). Operatory te działają niezależnie na parach bitów o tej samej wadze obydwu swoich argumentów. Wyniki działania poszczególnych operatorów pokazano w tabeli. Dla dwóch danych bitów b1 i b2 otrzymujemy: b1 b2 b1&b2 b1|b2 b1^b2 0 0 0 0 0 0 1 0 1 1 1 0 0 1 1 1 1 1 1 0 Język ANSI C – operatory bitowe Przykład. Dane są następujące definicje short int lanbit1=12; short int lanbit2=-35 lanbit1 000 000 000 1100 lanbit2 1111 1111 1101 1101 Wykonać podane poniżej działania w programie i poprzez obliczenia na liczbach binarnych 1. ~lanbit1 -13 9. lanbit1^lanbit2 -47 2. ~lanbit2 34 10. ~(lanbit1^lanbit2) 46 3. lanbit1&lanbit2 12 4. ~lanbit1&lanbit2 -47 5. ~(lanbit1&lanbit2) -13 6. lanbit1| lanbit2 -35 7. ~(lanbit1|lanbit2) 34 8. (~lanbit1|lanbit2) Język ANSI C – operatory bitowe Przykład. Maskowanie w słowie 8-go bitu. #include <stdio.h> #include <conio.h> int main() { int c; int maska=127; while ((c=getchar())!=EOF) putchar( c&maska); getch(); } Język ANSI C – operatory bitowe Przykład. Wyznaczanie reprezentacji binarnej liczby 8-bitowej. #include <stdio.h> #include <conio.h> int main() { char x=12; int i,k=1, bit[8]; for (i=0;i<8;i++) { if (x&k) bit[i]=1; else bit[i]=0; k*=2; } for (i=0;i<8;i++) printf(” %d ”,bit[i]); getch(); return 0; } Język ANSI C – operatory bitowe Operatory przesunięć bitowych (ang. bitshift operators) Operatory bitowe przesuwają bity swego argumentu znajdującego się po lewej stronie operatora o zadaną liczbę bitów w lewo lub w prawo. Wyrażenia z użyciem tych operatorów można zapisać następująco: E1<< E2 lub E1>>E2, gdzie E1 reprezentuje przed wykonaniem operacji wzorzec bitowy, który ma zostać przesunięty, dla operatora<< w lewo, a dla operatora >> w prawo. Jeśli nie ma nadmiaru arytmetycznego, dla operatora << przesunięcie jest E2 równoważne mnożeniu przez 2 a na najmłodsze pozycje pierwszego argumentu wchodzą zera. Działanie operatora >> odpowiada dzieleniu całkowitemu przez 2 E 2 dla typów bez znaku ( unsigned), wtedy na skrajne pozycje z lewej strony wchodzą zera. Dla typów ze znakiem, jeżeli wchodzą zera dla liczb nieujemnych, a jedynki dla liczb ujemnych mówimy o przesunięciu arytmetycznym, a jeżeli zera niezależnie od znaku liczby, to jest to tzw. przesunięcie logiczne. Język ANSI C – operatory bitowe Przykład. Użycie operatorów bitowych przesunięcia w lewo i w prawo. #include <stdio.h> #include <conio.h> int main() { int x1=‘A’, x2=‘0’; int y1=3,y2=2; printf(”\n %d”, x1<<y1);// kod ASCII znaku ‘A’ jest równy 65 stąd // w wyniku przesunięcia otrzymujemy 520 printf(”\n %d ”, x2>>y2); // przesunięcie prawo o 2 odpowiada dzieleniu // całkowitemu, otrzymujemy getch(); } Język ANSI C – operatory bitowe Przykład. Ustawianie określonego bitu w słowie. Ustawianie bitu o numerze nr_bitu na 1 x= x | 1<< nr_bitu; Najpierw realizowane jest przesunięcie 1 o nr_bitu w lewo, czyli wstawienie 1 na określoną, następnie wykonywana jest suma logiczna, co powoduje ustawienie bitu na 1, jeśli był równy 0. Ustawianie bitu o numerze nr_bitu na 0 x= x& ~(1<<nr_bitu); Na miejsce bitu o numerze nr_bitu wstawiane jest 0, i następnie wykonywany jest iloczyn logiczny, co powoduje ustawienie bitu na 0, jeżeli był równy 1. Język ANSI C – operatory bitowe Przykład. Dane jest słowo 8-bitowe. Napisać funkcję wyznaczającą wartość dziesiętną grupy n bitów począwszy od pozycji p. int obl_wart_bitow (char x, int n, int p) // n – liczba bitów, p –pozycja ( pozycje numerowane od 0) { return x>>(p+1-n)& ~( ~0<<n); } 2n −1 Zasada działania funkcji tej jest następująca: - przesunięcie bloku n-bitowego na najmłodsze pozycje ( pozycje o wagach od 1 do ( działanie x>> (p+1-n) ) - utworzenie maski wcinającej n najmłodszych bitów ze słowa 8-bitowego ( maska ta ma 1 na n najmłodszych bitach i zero na pozostałych) - obliczenie iloczynu bitowego przesuniętego bloku n-bitowego i maski. Język ANSI C – operatory bitowe Przykład. Operatory bitowe (ciąg dalszy). Tworzenie maski - ~0 - oznacza utworzenie słowa składającego się z samych jedynek, - ~0<<n – przesunięcie maski w lewo o n pozycji , powoduje, że na najmłodszych n pozycjach pojawiają się zera, - ~( ~0<<n) – po zanegowaniu na najmłodszych n pozycjach pojawiają się jedynki, a na pozostałych zera, czyli utworzona została potrzebna maska. Język ANSI C – operatory bitowe Zadania z operatorów do realizacji na laboratorium. 1. Dla zadanej liczby dodatniej typu char zanegować jej bity przy użyciu operatora ~, i obliczyć przy użyciu kodu U2 wartość bezwzględną otrzymanej liczby, porównać z wynikiem na ekranie. 2. Dla zadanej liczby ujemnej typu char zanegować jej bity przy użyciu operatora ~, i obliczyć przy użyciu kodu U2 wartość bezwzględną otrzymanej liczby, porównać z wynikiem na ekranie. 3. Wyznaczyć wzorzec bitowy danej liczby typu short int i zapisać w tablicy, wydrukować tablicę. 4. Utworzyć maskę bitową zawierającą same zera poza wybranym bitem równym 1 stosując operatory przesunięć bitowych. 5. Wyznaczyć dla wartości typu char wartość dziesiętną wybranej grupy n bitów począwszy od pozycji o indeksie p. 6. Sprawdzić czy n-ty bit ma wartość 1. 7. Zmienić wartość n-tego bitu liczby stosując operatory bitowe. 8. Wstawić do słowa kodowego typu char na pozycję najstarszą wartość 1, jeśli liczba jedynek jest nieparzysta, lub zero jeżeli jest parzysta. Język ANSI C – zasięgi, klasy pamięci i kwalifikatory Zasięg Jeżeli pewien obiekt (np. zmienna) zostanie zdefiniowany w programie, to można go użyć w odpowiedniej części programu, której rozmiar jest określany przez tzw. zasięg identyfikatora tego obiektu (zmiennej). W języku C wyróżniamy cztery zasięgi: -zasięg bloku -zasięg funkcji -zasięg prototypu -zasięg pliku Język ANSI C – zasięgi, klasy pamięci i kwalifikatory Zasięg bloku cz. 1 Blok to lista instrukcji umieszczona w klamrach { }. Wszystkie identyfikatory zadeklarowane na początku bloku mają zasięg ograniczony do bloku, w którym zostały zdefiniowane. { int i,j; i+=1; j*=2;...... } Ciało funkcji stanowi blok, stąd parametry formalne mają zasięg ograniczony do ciała funkcji. Język ANSI C – zasięgi, klasy pamięci i kwalifikatory Zasięg bloku cz. 2 Gdy bloki są zagnieżdżane ( jeden blok znajduje się wewnątrz drugiego bloku) definicja identyfikatora w bloku zewnętrznym rozciąga się również na blok wewnęrzny. Jeżeli w bloku wewnętrznym zdefiniowano identyfikator o tej samej nazwie co identyfikator zdefiniowany w bloku zewnętrznym, to identyfikator z bloku zewnętrznego nie będzie dostępny. Mówimy wtedy, że identyfikator wewnętrzny przesłania identyfikator zewnętrzny. { double x=20; { int i, x[10]; .... } x+=10; } Język ANSI C – zasięgi, klasy pamięci i kwalifikatory cz. 3 Zasięg prototypu Zasięg ten odnosi się do nazw zdefiniowanych w prototypach funkcji. W prototypach nie muszą występować nazwy parametrów, jeżeli jednak występują, to nie muszą być zgodne z nazwami parametrów formalnych, ani też ich nazwy nie powodują konfliktu z jakimikolwiek innymi nazwami. Przykład. double * f2 (int *pd, int n); Prototyp funkcji zwracającej wskaźnik do double. Nazwy pd i n nie muszą występować. Język ANSI C – zasięgi, klasy pamięci i kwalifikatory Zasięg funkcji Odnosi się on do etykiet stosowanych w instrukcjach skoku goto. Etykiety wewnątrz funkcji nie mogą się powtarzać. Zasięg pliku Identyfikatory zdefiniowane na zewnątrz wszystkich bloków mają zasięg ograniczony do pliku. Oznacza to, że są one widoczne od miejsca, w którym zostały zdefiniowane do końca pliku. Definicje umieszczone w pliku nagłówkowym, który został włączony dyrektywą #include, obowiązują do końca pliku, w którym została umieszczona ta dyrektywa. Język ANSI C – zasięgi, klasy pamięci i kwalifikatory Klasy pamięci dla zmiennych Klasa pamięci określa typ pamięci, w której przechowywana jest zmienna. Klasa pamięci zmiennej określa moment utworzenia zmiennej, czas przechowywania jej wartości, jak również moment usunięcia danej zmiennej. Wyróżnia się następujące klasy pamięci - klasa pamięci auto - klasa pamięci static - klasa pamięci register - klasa pamięci external Język ANSI C – zasięgi, klasy pamięci i kwalifikatory Klasa pamięci auto ( zmienne automatyczne) Zmienna zdefiniowana wewnątrz funkcji otrzymuje domyślną klasę auto ( zwana jest zmienną automatyczną). Definicję zmiennej automatycznej można zapisać następująco auto int liczba; jednak zwykle słowo auto jest pomijane. Pamięć dla zmiennej jest alokowana (przydzielana), gdy sterowanie programu wchodzi do bloku zawierającego definicję danej zmiennej i jest dealokowana, gdy sterowanie opuszcza ten blok. Termin auto wywodzi się stąd, iż proces ten odbywa się automatycznie bez udziału programisty. Zmienna typu auto jest widoczna tylko w bloku, w którym znajduje się jej definicja. Jeżeli zmienna typu auto jest jednocześnie definiowana i inicjalizowana, to jej inicjalizacja jest powtarzana za każdym razem, gdy sterowanie programu wchodzi do bloku ją zawierającego. Język ANSI C – zasięgi, klasy pamięci i kwalifikatory Klasa pamięci static Zmienna o klasie pamięci static może być zdefiniowana wewnątrz bloku np. wewnątrz funkcji albo też na zewnątrz wszystkich bloków. Zmienne statyczne są tworzone przed rozpoczęciem wykonywania programu i istnieją przez cały czas jego wykonywania. Definicja zmiennej statycznej typu int ma postać static int liczba; Zmienne statyczne zdefiniowane wewnątrz bloku mają ten sam zasięg co inne zmienne zdefiniowane w bloku. Jednak zachowują one swą wartość po opuszczeniu bloku przez sterowanie programu. Powoduje to na przykład w przypadku funkcji, że wartość statycznej zmiennej lokalnej może być przechowywana pomiędzy kolejnymi wywołaniami funkcji. Język ANSI C – zasięgi, klasy pamięci i kwalifikatory Przykład. Funkcja zliczająca i zwracająca ilość swoich wywołań. int uchwyt_bledu (void) { static int liczba; liczba++; return liczba; } Zmienna liczba jest inicjalizowana tylko jeden raz przy pierwszym wywołaniu funkcji. Przy każdym kolejnym wywołaniu, wartość zmiennej liczba jest zwiększana o 1 z przechowywana między wywołaniami funkcji, gdy sterowanie programu opuszcza blok funkcji. Język ANSI C – zasięgi, klasy pamięci i kwalifikatory Zmienna statyczna c.d. W przypadku zmiennej statycznej, która została zdefiniowana poza wszystkimi blokami ( na zewnątrz wszystkich funkcji), definicja taka ogranicza widoczność zmiennej do pliku, w którym definicja ta wystąpiła. Przykład. Zasięg zmiennej statycznej. W skład projektu wchodzą dwa pliki : plik1.c i plik2.c // plik1.c static double x; int f1 (void) { ….} double f2( double x) {……..} // plik2.c static int x; char * f3( char *s) { …} W obu plikach występują zmienne o nazwie x, jednak są one dostępne tylko tam, gdzie występują ich definicje. Język ANSI C – zasięgi, klasy pamięci i kwalifikatory Klasa pamięci register Definiując zmienną typu register tworzy się zalecenie dla kompilatora, aby umieścił daną zmienną w rejestrze procesora, traktowanym jako komórka pamięci. Powodem stosowania takiego zalecenia jest czas dostępu, który jest zwykle dla rejestrów znacznie krótszy niż dla pamięci zewnętrznej. Kompilatory mogą uwzględniać to zalecenie lub nie, z drugiej strony jednak sam kompilator stara się umieszczać w rejestrach częściej używane zmienne np. liczniki pętli. Jeżeli zmienna nie stanie się zmienną register, to będzie zmienną automatyczną. #include <stdio.h> #include <conio.h> int main() { register int i; for (i=1;i<1000;i++) sum+=i; printf(”\n Suma=%d”,suma); getch(); } Język ANSI C – zasięgi, klasy pamięci i kwalifikatory Klasa pamięci extern ( program z jednym plikiem źródłowym) Język ANSI C – pliki Wprowadzenie cz. 1 Plik jest podstawową jednostką służącą do przechowywania informacji w każdym systemie operacyjnym. Każdy plik ma swoją nazwę, długość w bajtach, atrybuty określające sposób korzystania z pliku oraz nadane uprawnienia dla poszczególnych użytkowników określające sposób korzystania z pliku. W systemach komputerowych występują różnego rodzaju urządzenia, do których przesyłane są dane czy też są z nich pobierane. System operacyjny izoluje jednak programy użytkowe od szczegółów pracy poszczególnych urządzeń. Jest to realizowane przy użyciu tzw. strumieni. Strumienie stanowią element pośredniczący między programem a systemem operacyjnym. Wyróżniamy strumienie binarne, które stanowią ciągi bajtów bez żadnej specjalnej struktury. Plik związany ze takim strumieniem jest to tzw. plik binarny. Strumień może też posiadać pewną strukturę – składać się z linii tekstu zakończonych znakiem końca linii. Plik związany z takim strumieniem to tzw. plik tekstowy.). Język ANSI C – pliki Wprowadzenie cz. 2 Strumienie są zwykle buforowane, oznacza to, że np. zapis w programie do pliku jest tylko zapisem do pewnego obszaru pamięci. Zawartość bufora jest zapisywana na nośniku, gdy bufor zostanie napełniony, lub też program zażąda tego w sposób jawny. Długość pliku może być ograniczona wielkością nośnika lub też ograniczenie może wynikać z pewnych własności systemowych (zakres typu całkowitego). Elementy pliku są zwykle dostępne sekwencyjnie, co oznacza, że w danym momencie istnieje dostęp tylko do jednego elementu pliku. Liczba elementów pliku może się zmieniać w trakcie przetwarzania pliku. Koniec pliku jest zaznaczany z punktu widzenia języka C znakiem EOF( ang. end-of-file). W pliku jest to znak ctrl-Z (ASCII 26). Język ANSI C – pliki Z każdym plikiem jest związana pewna zmienna, tzw. wskaźnik pliku określająca aktualnie dostępny element pliku. Numeracja elementów pliku zaczyna się od zera. Identyfikacja pliku w systemie operacyjnym może się odbywać poprzez wskaźnik do pliku (ang. file pointer) lub też uchwyt pliku (ang. handle) bedący pewną liczbą identyfikującą plik w ramach systemu operacyjnego. Język C nie ma wbudowanych operacji wejścia-wyjścia związanych z realizacją funkcji plikowych, stosowane są dla ich realizacji funkcje biblioteczne z pliku stdio.h. Aby rozpocząć realizację operacji plikowych na istniejącym pliku lub też utworzyć nowy plik, należy zdefiniować wskaźnik do pewnej struktury zawierającej konieczne informacje dla uzyskania dostępu do pliku. Język ANSI C – pliki Definicja wskaźnika do pliku może mieć następującą postać: FILE *fp; FILE jest typem strukturowym zdefiniowanym w pliku stdio.h. Przetwarzanie pliku w trakcie działania programu może polegać na wykonywaniu różnego rodzaju operacji na pliku np. utworzeniu lub otwarciu pliku, zapisie elementów, odczycie elementów, usuwaniu elementów czy też porządkowaniu (np. sortowaniu). Rozpoczęcie operacji na pliku wymaga jego utworzenia lub otwarcia. W języku C plik jest tworzony lub otwierany przy użyciu funkcji fopen (istnieje też funkcja open do tzw. operacji niskopoziomowych). Po zakończeniu operacji na pliku, plik jest zamykany przy użyciu funkcji fclose. Zamknięcie pliku powoduje zarejestrowanie zmian dokonanych w pliku w trakcie jego przetwarzania. Język ANSI C – pliki Funkcja fopen i parametry otwarcia pliku cz.1 Funkcja fopen posiada dwa parametry. Pierwszy jest parametrem typu tablica znaków określający nazwę pliku, drugi parametr też będący tablicą znaków, określa tzw. tryb utworzenia lub otwarcia pliku. Przykład. FILE *fp; fp=fopen("Plik1.dat", "w"); Nazwa pliku podana bez ścieżki dostępu oznacza, że plik będzie się znajdował w tym katalogu, w którym zapisano projekt. Można też podać ścieżkę dostępu do pliku w sposób następujący: fp=fopen("C:\\moje dokumenty\\Plik2.dat", "w"); (należy stosować podwójny ukośnik dla uzyskania pojedyńczego znaku \, dla tego znaku wymgana jest tzw. sekwencja ucieczki). Język ANSI C – pliki Funkcja fopen i parametry otwarcia pliku cz.2 Funkcja fopen zwraca wskaźnik do pliku, jeśli jego otwarcie lub utworzenie było możliwe. Jeżeli nie było to możliwe funkcja fopen zwraca zerowy wskaźnik NULL. Wskazanym jest sprawdzanie uzyskanej wartości fp przed rozpoczęciem operacji plikowych w następujący sposób #include <stdio.h> #include <conio.h> #include <stdlib.h> FILE *fp; fp=fopen("Plik1.dat", "w"); if (fp!=NULL) { // wystarczy if (fp) printf(" Plik został utworzony"); getch();} else printf("\n Plik nie zostal utworzony"); getch(); exit(1);} Język ANSI C – pliki Funkcja fopen i parametry otwarcia pliku cz.3 Tryby otwarcia plików cz. 1 Tryb Jeśli plik istnieje Jeśli plik nie istnieje "r" Otwiera plik do czytania Błąd "w" Otwiera nowy plik, jeśli istniał plik o danej nazwie jego zawartość jest kasowana Tworzy nowy plik "a" Otwiera plik do dopisywania Tworzy nowy plik Język ANSI C – pliki Funkcja fopen i parametry otwarcia pliku cz.4 Tryby otwarcia plików cz. 2 Tryb Jeśli plik istnieje Jeśli plik nie istnieje "r+" Otwiera plik do odczytu i zapisu Błąd "w+" Otwiera nowy plik do zapisu i odczytu, jeśli istniał plik o danej nazwie jego zawartość jest kasowana Tworzy nowy plik "a+" Otwiera plik do dopisywania i odczytu Tworzy nowy plik Język ANSI C – pliki Funkcja fopen i parametry otwarcia pliku cz.4 Powyższe tryby dotyczą zasadniczo plików tekstowych ( zależy to od ustawienia zmiennej systemowej _fmode). Dla plików binarnych należy dodać do nazwy trybu literę b, np. "rb", "wb", "r+b", "w+b". Dla trybów tekstowych należy dodać literę t, czyli "rt", "wt", "r+t", "w+t". Język ANSI C – pliki Standardowe funkcje plikowe /ANSI C i BB/ fopen putc feof fflush fclose getc ferror setbuf ftell fgetc fgetpos setvbuf fwrite fprintf fsetpos fread fscanf remove fseek fputs rewind fputc fgets clearerror Pewnych funkcji ze standardu nie ma w BB, natomiast w BB występuje cały szereg dodatkowych funkcji plikowych(np. do operacji niskopoziomowych czy też do działań na katalogach, które są specyficzne dla danej grupy systemów operacyjnych). Język ANSI C – pliki Funkcja fopen FILE *fopen(char const *file_name, char const access_mode); Argument pierwszy wskazuje plik do otwarcia, argument drugi mode określa czy plik (strumień) zostanie otwarty do zapisu, odczytu czy obu tych operacji. Określa on też tryb (binarny czy tekstowy). Funkcja zwraca wskaźnik do pliku (wskaźnik do struktury FILE wykorzystywanej przy dostępie do strumienia) albo też NULL. FILE *fp; fp=fopen (”Plik3.dat”, ”r+b”); Maksymalną liczbę otwartych plików określa stała FOPEN_MAX, maksymalną długość nazwy stałą FILENAME_MAX. Język ANSI C – pliki Funkcja fclose int fclose (FILE *fp); Funkcja ta zamyka plik (strumień) związany ze strukturą FILE wskazywaną przez fp. Funkcja zwraca 0, jeśli działanie się powiodło lub też EOF. W przypadku strumieni wyjściowych, strumień jest opróżniany przed zamknięciem pliku. FILE *fp; fclose (fp); Język ANSI C – pliki Funkcja ftell long int ftell(FILE *fp); Zwraca pozycję wskaźnika pliku wskazywanego prze fp. Dla plików binarnych pozycja jest mierzona w bajtach od początku pliku. Jeżeli powstał błąd, zwraca -1L i nadaje zmiennej globalnej errno wartość dodatnią. errno jest ustawiana na jedną z dwóch wartości : EBADF Niewłaściwy wskaźnik do pliku ESPIPE Niedopuszczalna operacja seek na urządzeniu Język ANSI C – pliki Funkcja fwrite size_t fwrite(void *buffer, size_t size, size_t count, FILE *fp ); Funkcja zapisuje do pliku wskazywanego przez fp, z miejsca pamięci wskazywanego przez buffer, count bloków danych, każdy o rozmiarze size. Funkcja zwraca liczbę zapisanych bloków. Przykład. int i=5; FILE *fplik; fplik=fopen(”Plik4.dat”, ”w+b”); fwrite(&i, sizeof(int),1,fplik); fclose(fplik); Język ANSI C – pliki Funkcja fwrite size_t fread(void *buffer, size_t size, size_t count, FILE *fp ); Funkcja wczytuje z pliku wskazywanego przez fp, do miejsca pamięci wskazywanego przez buffer, count bloków danych, każdy o rozmiarze size. Funkcja zwraca liczbę odczytanych bloków. Przykład. int i; FILE *fplik; fplik=fopen(”Plik4.dat”, ”r+b”); fread(&i, sizeof(int),1,fplik); printf(”\n Element=%d”, i); fclose(fplik); Język ANSI C – pliki Funkcja fseek int fseek(FILE *file_pointer, long int offset, int whence ); Funkcja ustawia nową pozycję bieżącą pliku wskazywanego przez fp. Następna operacja plikowa rozpocznie się od tej pozycji. Nowa pozycja jest oddalona o offset bajtów od punktu odniesienia określonego przez trzeci parametr whence. Parametr ten może przyjmować następujące wartości: SEEK_SET – przesunięcie jest realizowane względem początku pliku, SEEK_CUR – przesunięcie jest realizowane względem bieżącej pozycji, SEEK_END – przesunięcie jest realizowane względem końca pliku Funkcja zwraca wartość różną od zera w przypadku wystąpienia błędu. Język ANSI C – pliki Przykład. Zapis 5 liczb typu int i odczyt pliku. #include <stdio.h> #include <conio.h> int main() { int i, buf, tab[5]={ 1,-1,2,3,5}; FILE *fp; fp=fopen("Pliktest1.dat","w+b"); for (i=0;i<5;i++) // Zapis 5 liczb do pliku fwrite(&tab[i],sizeof(int),1,fp); fclose(fp); // zamiast zamykania i ponownego otwierania // można użyć fseek(fp,0,0) lub rewind. fp=fopen("Pliktest1.dat","r+b"); for (i=0;i<5;i++) // Odczyt 5 liczb z pliku { fread(&buf,sizeof(int),1,fp); printf("\n Element pliku nr %d =%d",i,buf); } fclose(fp); getch(); return 0; } Język ANSI C – pliki Przykład. Dany jest plik test1.dat liczb typu int, dopisać zadaną ilość liczb n=5 do pliku ( na końcu pliku) i odczytać plik. #include <stdio.h> #include <conio.h> #include <stdlib.h> int main() { int i, buf, ilosc_elementow_typu_int, n=5; FILE *fp; fp=fopen("Pliktest1.dat","r+b"); fseek(fp,0,SEEK_END); for (i=0;i<n;i++) { printf("\n Podaj liczbe :"); scanf("%d",&buf): fwrite(&buf,sizeof(int),1,fp); } // Wyznaczenie ilości elementów w pliku po dopisaniu ilosc_elementow_typu_int= ftell(fp)/sizeof(int); Język ANSI C – pliki Przykład. cd. for (i=0;i< ilosc_elementow_typu_int;i++) { fread(&buf,sizeof(int),1,fp); printf("\n Element pliku nr %d =%d",i,buf); } fclose(fp); getch(); } Język ANSI C – pliki Przykład. Dany jest plik test2.dat liczb typu double, zsumować liczby dodatnie w pliku, a następnie wyznaczyć wartość minimalną w pliku. #include <stdio.h> #include <conio.h> int main() { int i, ilosc_elementow_typu_double; double buf, suma_d=0, amin; FILE *fp; fp=fopen("Pliktest2.dat","r+b"); fseek(fp,0,SEEK_END); ilosc_elementow_typu_double= ftell(fp)/sizeof(double); // Wyznaczanie sumy liczb dodatnich for (i=0;i< ilosc_elementow_typu_double;i++) { fread(&buf,sizeof(double),1,fp); if (buf>0) suma_d+=buf; } Język ANSI C – pliki Przykład. cd. fseek(fp, 0, SEEK_SET); fread(&amin,sizeof(double),1,fp); for (i=1;i< ilosc_elementow_typu_double;i++) { fread(&buf,sizeof(double),1,fp); if (buf<amin) amin=buf; } printf("\n Element minimalny =%f",amin); fclose(fp); getch(); } Język ANSI C – pliki Przykład. Dany jest plik test1.dat liczb typu int , zamienić miejscami element drugi i elementem czwartym. #include <stdio.h> #include <conio.h> #include <stdlib.h> int main() { int i, buf1,buf2, buf, ilosc_elementow_typu_int; FILE *fp; fp=fopen("Pliktest1.dat","r+b"); fseek(fp,0,SEEK_END); ilosc_elementow_typu_int= ftell(fp)/sizeof(int); if (ilosc_elementow_typu_int<4) { printf("\n Nie ma wystarczajacej liczby elementow"); getch(); fclose(fp); exit(1); } Język ANSI C – pliki Przykład. c.d. fseek (fp,1*sizeof(int),SEEK_SET); fread(&buf1,sizeof(int), 1,fp); fseek (fp,3*sizeof(int),SEEK_SET); fread(&buf2,sizeof(int), 1,fp); fseek (fp,1*sizeof(int),SEEK_SET); fwrite(&buf2,sizeof(int), 1,fp); fseek (fp,3*sizeof(int),SEEK_SET); fwrite(&buf1,sizeof(int), 1,fp); fseek(fp,0,0); for (i=0;i< ilosc_elementow_typu_int;i++) { fread(&buf,sizeof(int),1,fp); printf("\n Element pliku nr %d =%d",i,buf); } fclose(fp); getch(); } Język ANSI C – pliki Przykład. W przykladzie przedstawiono dwie metody odczytu pliku. #include <stdio.h> #include <conio.h> #include <stdlib.h> int main(int argc, char **argv) { FILE *fp; double x[8]={1.5,2,3.5,4.5,5.5,6.8,7.5,8}, buf; int i,k,ilosc_bajtow_w_pliku,ilosc_elementow_typu_double_w_pliku; fp=fopen("pliktest.dat","w+b"); if (fp) { printf("\n Plik zostal utworzony"); getch(); } else { printf("\n Plik nie daje utworzyc"); getch(); exit(1);} for (i=0;i<8;i++) fwrite(&x[i],sizeof(double),1,fp); fclose(fp); Język ANSI C – pliki fp=fopen("pliktest.dat","r+b"); // Metody odczytu pliku // Metoda 1 - z użyciem wyznaczania liczby elementów pliku fseek(fp,0,2); ilosc_bajtow_w_pliku=ftell(fp); ilosc_elementow_typu_double_w_pliku=\ ilosc_bajtow_w_pliku/sizeof(double); printf("\n Metoda 1"); fseek(fp,0,0); for (i=0;i< ilosc_elementow_typu_double_w_pliku;i++) { fread(&buf,sizeof(double),1,fp); printf("\n Element %d = %lf",i,buf); } getch(); Język ANSI C – pliki printf("\n\n\n"); i=1; // Metoda 2 - użycie sprawdzania wartości zwracanej przez funkcję fread clrscr(); fseek(fp,0,0); printf("\n Metoda 2"); while (fread(&buf,sizeof(double),1,fp)) printf("\n Element %d = %lf",i++,buf); getch(); fclose(fp); return 0; } Język ANSI C – pliki Przykład. Tworzenie kopii pliku, należy utworzyć kopię pliku pliktest.dat o nazwie plikcopy.dat w bieżącym katalogu. #include <stdio.h> #include <conio.h> #include <stdlib.h> int main(int argc, char **argv) { FILE *fp,*fp1; char bufc; double x[8]={1.5,2,3.5,4.5,5.5,6.8,7.5,8}, buf; int i,k,ilosc_bajtow_w_pliku,ilosc_elementow_typu_double_w_pliku; fp=fopen("pliktest.dat","w+b"); if (fp) { printf("\n Plik zostal utworzony"); getch(); } else { printf("\n Plik nie daje utworzyc"); getch(); exit(1);} for (i=0;i<8;i++) fwrite(&x[i],sizeof(double),1,fp); Język ANSI C – pliki Przykład. c.d. (tworzenie kopii pliku) fp1=fopen("plikcopy.dat","w+b");// utworzenie nowego pustego pliku fseek(fp,0,0); while ( fread(&bufc,sizeof(char),1,fp)) // kopiowanie bajt po bajcie fwrite(&bufc,sizeof(char),1,fp1); printf("\n Kopia pliku"); i=1; fseek(fp1,0,0); while (fread(&buf,sizeof(double),1,fp1)) printf("\n Element %d = %lf",i++,buf); getch(); return 0; } Język ANSI C – tablice znaków Tablice znaków (łańcuchy) Tablice znaków stanowią specjalny rodzaj tablic o budowie ułatwiającej przetwarzanie tekstów. Przetwarzanie obejmuje szereg operacji takich jak tworzenie, modyfikacją i przeszukiwanie. Tablice znakowe mogą być definiowane w sposób następujący: char s1[5]; char s2[5 ]=”ABCD”; char s3[ ]=”abcd”; Definicje tablic s2 i s3 zawierają też inicjalizację. Tablice znaków zakończone są znakiem ‘\0’. Znak ten umożliwia wykrycie końca tablicy, a tym samym umożliwia traktowanie tablic znaków w nieco inny sposób niż zwykłych tablic. Język ANSI C – tablice znaków Budowa tablicy znaków Przykładowo, tablica s2 ma następującą budowę: 'A' 'B' 'C' 'D' '\0' Tak więc definiując tablicę należy przewidzieć jedną komórkę na znak końcowy ‘\0’. Język ANSI C – tablice znaków Stałe łańcuchowe i wskaźniki do znaków cz. 1 Stałe łańcuchowe ( stałe typu tablica znaków), np. ”Tekst” , są przechowywane w pamięci jako tablice znaków z ostatnim elementem równym ‘\0’. ”Tekst” jest typu char *, czyli wskaźnik do znaku. Wskaźnik do znaku może więc zostać zainicjowany stałą łańcuchową char *ps=”Tekst”; // sposób 1 Inicjalizacja taka jest równoważna parze instrukcji char *ps; // sposób 2 ps=”Tekst”; W przypadku sposobu 1 do wskaźnika ps jest przypisywany wskaźnik do tablicy znaków, a nie do *ps ( czyli nie do miejsca wskazywanego przez ps). Język ANSI C – tablice znaków Stałe łańcuchowe i wskaźniki do znaków cz. 2 Warto też zauważyć, że przy definicji tablicy znakowej char s2[5]; nie jest możliwa realizacja przypisania w programie s2=”ABCD” ;// przypisanie błędne Wynika to z faktu, że s2 jako nazwa tablicy jest typu stały wskaźnik do znaku, który to wskaźnik nie może być zmieniony poprzez przypisanie. Język ANSI C – tablice znaków Wczytywanie i drukowanie tablic znakowych cz.1 W języku C istnieje specjalny deskryptor formatu %s umożliwiający wczytywanie i drukowanie tablic znakowych przy użyciu funkcji scanf i printf, istnieją też funkcje biblioteczne realizujące te operacje. Jeśli zdefiniowano tablicę znakową char s1[5]; można ją wczytać i wydrukować w poniższy sposób: printf(”\ Podaj lancuch :”); scanf(”%s”,s1); /// nie stosuje się znaku & przed s2, gdyż s2 jest //wskaźnikiem. printf(”\n Wczytany lancuch to :%s ”, s1); Język ANSI C – tablice znaków Wczytywanie i drukowanie tablic znakowych cz.1 Jednak wczytywanie łańcuchów przy użyciu funkcji printf nie pozwala na wczytanie łańcuchów zawierających spacje, gdyż wczytywanie łańcucha jest przerywane po napotkaniu spacji. Dla takich łańcuchów należy zastosować funkcję gets lub też funkcję fgets. Funkcja gets te ma następujący prototyp: char *gets(char *s); Opis działania funkcji gets: Funkcja wczytuje znaki ze standardowego wejścia ( stdin) aż do momentu napotkania znaku nowej linii (Enter). Wczytywane znaki są wyświetlane na ekranie i zapamiętywane począwszy od miejsca wskazywanego przez s. Znak nowej linii jest zastępowany w łańcuchu znakiem końca łańcucha '\0'. Ciąg wejściowy może zawierać pewne białe znaki (np. spacje i znaki tabulacji poziomej). Funkcja zwraca s, gdy operacja się powiodła, lub NULL w przypadku wystąpienia błędu. Język ANSI C – tablice znaków Wczytywanie i drukowanie tablic znakowych cz.2 Długość wczytywanego ciągu znaków nie jest ograniczana, co przy ciągach dłuższych niż to wynika z argumentu funkcji gets, może powstać uszkodzenie sasiednich obszarów pamięci. Przykład. char lan [20]; printf ("\n Podaj lancuch:"); gets(lan); Bardziej uniwersalną funkcją służącą do wczytywania łańcuchów jest fgets. Język ANSI C – tablice znaków Wczytywanie i drukowanie tablic znakowych cz.3 Funkcja fgets ma nastepujący prototyp: char *fgets(char *s, int n, FILE *stream); Funkcja ta czyta do łańcucha s znaki ze strumienia wejściowego (pliku) określonego przez wskaźnik stream. Funkcja kończy wczytywanie znaków po przeczytaniu n - 1 znaków lub też po pojawieniu się znaku nowej linii. Funkcja wpisuje znak nowej linii do do łańcucha. Funkcja zwraca łańcuch lub NULL w przypadku pojawienia się końca pliku lub błedu. Przykład. Wczytywanie ze standardowego strumienia wejściowego char lan [20]; printf ("\n Podaj lancuch:"); fgets(lan,15,stdin); Język ANSI C – tablice znaków Wczytywanie drukowanie tablic znakowych cz.4 Do drukowania tablic znakowych można zastosować funkcje puts i fputs. Prototyp funkcji puts ma postać int puts(const char *s); Funkcja wyprowadza łańcuch na wyjście standardowe (stdout) i dołącza znak nowej linii. W przypadku pomyślnej realizacji funkcja zwraca wartość nieujemną, w przeciwnym przypadku EOF. int fputs(const char *s, FILE *stream); Funkcja zapisuje łańcuch s do do strumienia wyjściowego określonego przez wskaźnik stream. Funkcja nie zapisuje do pliku znaku '\0'. W przypadku powodzenia operacji funkcja zwraca wartość nieujemną, w przypadku błędu wartość EOF. Język ANSI C – tablice znaków Przykład. Zapis do pliku dwóch łańcuchów i odczyt. #include <stdio.h> #include <conio.h> #include <stdlib.h> #include <string.h> int main(int argc, char **argv) { char s1[10]="ABCDE"; char s2[10]="abcde", s3[10], s4[10]; FILE *fp; fp=fopen("Plik1.txt","w+"); fputs(s1,fp); fputc('\n',fp); fputs(s2,fp); fputc('\n',fp); // dopisanie znaku nowej linii, aby funkcja // fgets czytała tylko pierwszy łańcuch z pliku Język ANSI C – tablice znaków Przykład. c.d. Odczyt pliku i wydruk. fseek(fp,0,0); fgets(s3,10,fp); printf("%s",s3); fgets(s4,10,fp); printf("%s",s4); getch(); fclose(fp); return 0; } Język ANSI C – tablice znaków Standardowe funkcje łańcuchowe cz. 1 Poniżej omówione zostaną wybrane najczęściej spotykane funkcje związane z przetwarzaniem łańcuchów (pełny zestaw w <string.h> i <stdlib.h>. Funkcje te to: strlen, strcpy, strcat, strcmp, strlwr, strupr, atoi, itoa, strchr, strstr. Funkcja strlen size_t strlen(const char *s); Funkcja oblicza długość łańcucha s bez końcowego znaku '/0'. Przykład. char s[20]="12345678"; int dl; dl=strlen(s);// dl będzie równe 8 Język ANSI C – tablice znaków Standardowe funkcje łańcuchowe cz. 2 Funkcja strcpy char *strcpy(char *dest, const char *src); Kopiuje łańcuch src do łańcucha dest. Kopiowanie ulega zakończeniu po skopiowaniu znaku '\0' kończącego łańcuch src. Funkcja zwraca wskaźnik dest . Przykład. #include <stdio.h> #include <string.h> int main(void) { char lan1[10="ABC"; char lan2[ ]= "12345"; strcat(lan1, lan2); printf("%s", lan1); return 0; } Język ANSI C – tablice znaków Standardowe funkcje łańcuchowe cz. 3 Funkcja strcat char *strcat(char *dest, const char *src); Dołącza łańcuch src do końca łańcucha dest. Funkcja zwraca wskaźnik dest . Długość połączonego łańcucha jest równa strlen(dest)+strlen(src). Przykład. #include <stdio.h> #include <string.h> int main(void) { char lan1[20]="abcde"; char lan2[ ]= "123456789"; strcat(lan1, lan2); printf("%s", lan1); return 0; } Język ANSI C – tablice znaków Standardowe funkcje łańcuchowe cz. 4 Funkcja strcmp int strcmp(const char *s1, const char *s2); Porównuje łańcuch s1 z łańcuchem s2, porównując kody znaków (np. kody ASCII) obu łańcuchów. Porównanie kończy się, gdy w jednym z łańcuchów zostanie napotkany znak o większym kodzie lub też zostanie osiągnięty koniec jednego z łańcuchów ( wtedy dłuższy łańcuch uważany jest za większy). Funkcja zwraca - wartość <0, gdy s1<s2, -wartość 0, gdy s1==s2, -wartość >0, gdy s1>s2. Język ANSI C – tablice znaków Standardowe funkcje łańcuchowe cz. 5 Przykład. Zastosowanie funkcji strcmp. #include <stdio.h> #include <string.h> #include <conio.h> int main(void) { char lan1[10]="abcde",lan2[10 ]; int p; printf(" Podaj lancuch:"); scanf("%s", lan2); p= strcmp(lan1,lan2); if (p<0) printf(" lan1<lan2"); else if ( p==0) printf("\n lan1=lan2"); else printf("\n lan1>lan2"); getch(); return 0; } Język ANSI C – tablice znaków Przykład. Wczytywanie dwuwymiarowej tablicy znaków (tablicy łańcuchów) oraz sortowanie niemalejąco. (cz.1) // cz. 1 programu #include <stdio.h> #include <conio.h> #include <string.h> int main(int argc, char* argv[]) { char stab[5][10]; char spom[10]; int i,zam; for(i=0;i<5;i++) { printf("\n lan%d :",i); scanf("%s",stab[i]); // wczytywanie tablicy znaków będącej wierszem } // tablicy dwuwymiarowej for(i=0;i<5;i++) printf("\n lan %s",stab[i]); Język ANSI C – tablice znaków Cz. 2 programu do { zam=0; for (i=0;i<4;i++) if (strcmp(stab[i],stab[i+1])>0) { strcpy(spom, stab[i]); strcpy(stab[i], stab[i+1]); strcpy( stab[i+1],spom ); zam=1; } } while (zam); printf("\n\n Tablica po sortowaniu"); for(i=0;i<5;i++) printf("\n lan %s",stab[i]); getch(); return 0;} Język JęzykANSI ANSICC- struktury - wstęp Struktury Struktury ( zwane w innych językach programowania rekordami) stanowią typ służący do przetwarzania informacji powiązanych ze sobą. Definicja typu strukturowego ma następującą postać struct nazwa_typu { typ_pola_1 nazwa_pola_1; typ_pola_2 nazwa_pola_2; . . typ_pola_n nazwa_pola_n; }; Tak więc definicja typu strukturowego składa się ze słowa kluczowego struct, po którym następuje nazwa danego typu strukturowego ( czasami określana jako etykieta), po czym po nawiasie otwierającym występują typy poszczególnych składowych struktury oraz ich nazwy. Składowe typu strukturowego zwane są polami struktury. Język ANSI C - struktury Definicja typu strukturowego kończy się nawiasem zamykajacym. W typie strukturowym mogą wystąpić pola, które same mogą być typu strukturowego, mówimy wtedy o tzw. typie strukturowym zagnieżdżonym. Nazwy pól muszą być różne w ramach jednego typu strukturowego, jednak można stosować te same nazwy w ramach innych typów strukturowych. Nazwa pola struktury może być taka sama jak nazwa danego typu strukturowego czy też zmiennej strukturowej. Język ANSI C - struktury Przykład. W ramach jednego typu danych chcemy przechowywać pewne informacje o pracownikach. Zdefiniujemy uproszczony typ strukturowy pozwalający na przechowywanie takich informacji ( w rzeczywistych sytuacjach może być konieczne stosowanie nawet kilkudziesięciu pól). struct pracownik { char nazwisko [20]; char imie [20]; int wiek; double dochod; } rek1, rek2; rek1, rek2 są zmiennymi typu pracownik. Zmienne strukturowe można też zdefiniować w sposób następujący: struct pracownik rek3, rek4, rek5; Język ANSI C - struktury Można też zdefiniować tablicę struktur struct pracownik pracownicy [20]; Zmienna pracownicy jest tablicą struktur składającą się z 20 struktur typu pracownik. Dla realizacji dostępu do pól typu strukturowego, w języku C wprowadzono specjalny operator dostępu do pól struktury – operator „.”( kropka). Operator ten jest stosowany w sposób następujący: jeśli mamy zmienną strukturową rek1 typu pracownik, to możemy wprowadzić wartość do jej pola dochod w sposób następujący: rek1. dochod=1500; W ogólnym przypadku, polami zmiennych strukturowych możemy posługiwać się jak zmiennymi takiego typu, jak typ danego pola struktury. Język ANSI C - struktury Przykład. W ramach jednego typu danych chcemy przechowywać pewne informacje o pracownikach. Zdefiniujemy uproszczony typ strukturowy pozwalający na przechowywanie takich informacji ( w rzeczywistych sytuacjach może być konieczne stosowanie nawet kilkudziesięciu pól). struct pracownik { char nazwisko [20]; char imie [20]; int wiek; double dochod; } rek1, rek2; rek1, rek2 są zmiennymi typu pracownik. Zmienne strukturowe można też zdefiniować w sposób następujący: struct pracownik rek3, rek4, rek5; Język ANSI C - struktury Można też zdefiniować tablicę struktur struct pracownik pracownicy [20]; Zmienna pracownicy jest tablicą struktur składającą się z 20 struktur typu pracownik. Dla celów typem strukturowym, w języku C wprowadzono specjalny operator dostępu do pól struktury – operator „.”( kropka). Operator ten jest stosowany w sposób następujący: jeśli mamy zmienną strukturową rek1 typu pracownik, to możemy wprowadzić wartość do jej pola dochod jak następuje: rek1. dochod=1500; W ogólnym przypadku polami zmiennych strukturowych możemy posługiwać się jak zmiennymi takiego typu, jak typ danego pola struktury. Język ANSI C - struktury Wprowadzanie/wyprowadzanie informacji z /do pól zmiennych strukturowych cz. 1 1. Pola typu numerycznego ( typy całkowite i typy rzeczywiste) Jak zaznaczono powyżej, pola takie można stosować, jako zmienne danego typu rek2. dochod=1000; rek2. wiek=25; pracownicy[0].dochod=1200; pracownicy [0].wiek=24; scanf(”%d”,&rek3.dochod); scanf(”%d”,&rek3.wiek); scanf (”%lf”, &pracownicy[1].dochod); scanf (”%d”, &pracownicy[1].wiek); Język ANSI C - struktury Wprowadzanie/wyprowadzanie informacji z /do pól zmiennych strukturowych cz. 2 2. Pola typu tablica znaków strcpy (rek1.nazwisko, ” Kowalski”); scanf(”%s”, rek2.nazwisko); gets( rek3.nazwisko); Wyprowadzanie informacji odbywa się podobnie jak dla innych zmiennych. printf (”\n %d ”, rek1.dochod); printf(”\n %s ”, rek1.nazwisko); Język ANSI C - struktury Inicjalizacja struktur Zmienne typu strukturowego, podobnie jak tablice, mogą być inicjalizowane przy definicji. Przykład. Inicjalizacja pól zmiennej typu student. struct student { char nazwisko[20]; char imie [20]; int rok studiow; char wydzial[20]; }; struct student student1={ "Kowalski", "Marcin", 2, "EiA"}; struct student student2={ "Iksiński", "Andrzej", 3, "EiA"}; Język ANSI C - struktury Inicjalizacja tablic struktur Przykład. Inicjalizacja tablicy struktur typu student struct student { char nazwisko[20]; char imie [20]; int rok studiow; char wydzial[20]; }; struct student studenci[3]={ {”Kowalski”, ”Marcin”, 2, ”EiA”}, { ”Iksiński”, ”Andrzej”, 2, ”EiA”}, { ”Malinowski”, ”Dariusz”, 2, ”EiA”} }; Język ANSI C - struktury Przykład. Program rekordy1 wczytujący i drukujący pojedyncze struktury. #include <condefs.h> #include <stdio.h> #include <conio.h> #include <string.h> // program rekordy1 wczytuje dane do struktur // i następnie drukuje //--------------------------------------------------------------------------#pragma argsused int main(int argc, char **argv) { struct student { char nazwisko[20]; char imie [20]; int rok_studiow; char wydzial[20]; }; Język ANSI C – struktury struct student student1, student2; /* Bezpośrednie nadawanie wartości polom struktur */ strcpy(student1.nazwisko,"Kowalski"); strcpy(student1.imie,"Jan"); student1.rok_studiow=2; strcpy(student1.wydzial,"EiA"); Język ANSI C – struktury // Program rekordy1 cz. II /* Do wczytywania pól struktury student 2 z klawiatury zastosowano trzy różne funkcje wczytywania pól typu tablica znaków, by pokazać różne sposoby wczytywania takich pól */ printf ("\n Podaj dane 2-go studenta "); printf ("\n Nazwisko: "); scanf("%s",student2.nazwisko); printf ("\n Imie: "); fflush(stdin); fgets(student2.imie,15,stdin); printf("\n Rok studiow :"); scanf("%d",&student2.rok_studiow); printf ("\n Wydział: "); scanf("%s",student2.wydzial); Język ANSI C – struktury // program rekordy1 cz. III /* Drukowanie struktur student1 i student2*/ printf("\n\n Dane studenta 1 "); printf("\n Nazwisko :%s", student1.nazwisko); printf("\n Imie :%s", student1.imie); printf("\n Rok studiow :%d", student1.rok_studiow); printf("\n Wydzial :%s", student1.wydzial); printf("\n\n\n Dane studenta 2 "); printf("\n Nazwisko :%s", student2.nazwisko); printf("\n Imie :%s", student2.imie); printf(" Rok studiow :%d", student2.rok_studiow); printf("\n Wydzial :%s", student2.wydzial); getch(); return 0;} Język ANSI C – struktury Struktury zagnieżdżone Struktury zagnieżdżone to takie struktury w których przynajmniej jedno z pól jest typu strukturowego. struct nazwa_typu { typ_pola_1 nazwa_pola_1; struct nazwa_typu_strukturowego nazwa_pola_2; . . typ_pola_n nazwa_pola_n; }; Język ANSI C – struktury struct data { int dzien; int miesiac; int rok; } struct dane_studenta { char nazwisko[20]; char imie [20]; struct data data_ur ; int rok studiow; char wydzial[20]; }; struct dane_studenta dane_studenta1, dane_studenta2; Język ANSI C – struktury Przykład . Program rekordy2 wczytuje dane do tablicy struktur i następnie drukuje int main(int argc, char **argv) { struct data { int dzien; int miesiac; int rok; }; struct dane_studenta { char nazwisko[20]; char imie [20]; struct data data_ur; int rok_studiow; char wydzial[20]; }; struct dane_studenta studenci[4]; Język ANSI C – operatory // Program rekordy2 cz. II int i; /* Bezpośrednie nadawanie wartości polom tablicy struktur */ strcpy(studenci[0].nazwisko,"Kowalski"); strcpy(studenci[0].imie,"Jan"); studenci[0].rok_studiow=2; studenci[0].data_ur.dzien=2; studenci[0].data_ur.miesiac=5; studenci[0].data_ur.rok=1980; strcpy(studenci[0].wydzial,"EiA"); strcpy(studenci[1].nazwisko,"Nowak"); strcpy(studenci[1].imie,"Andrzej"); studenci[1].rok_studiow=2; studenci[1].data_ur.dzien=2; studenci[1].data_ur.miesiac=6; studenci[1].data_ur.rok=1981; strcpy(studenci[1].wydzial,"EiA"); Język ANSI C – struktury // program rekordy2 cz. III for (i=2;i<4;i++) { printf ("\n Podaj dane studenta %i",i+1); printf ("\n Nazwisko: "); scanf("%s",studenci[i].nazwisko); fflush(stdin); printf ("\n Imie: "); scanf("%s",studenci[i].imie); fflush(stdin); printf("\n Podaj date urodzenia "); printf("\n dzien :"); scanf ("%d",&studenci[i].data_ur.dzien); printf("\n miesiac :"); scanf ("%d",&studenci[i].data_ur.miesiac); printf("\n rok :"); scanf ("%d",&studenci[i].data_ur.rok); printf("\n Rok studiow :"); scanf("%d",&studenci[i].rok_studiow); printf ("\n Wydział: "); scanf("%s",studenci[i].wydzial); fflush(stdin); } Język ANSI C – struktury // Program rekordy2 cz. IV /* Drukowanie tablicy struktur */ for (i=0;i<4;i++) { printf("\n\n Dane studenta %d ", i+1); printf("\n Nazwisko printf("\n Imie :%s", studenci[i].nazwisko); :%s", studenci[i].imie); printf(" data urodzenia :%d.%d.%d ", studenci[i].data_ur.dzien,\ studenci[i].data_ur.miesiac,studenci[i].data_ur.rok); printf("\n Rok studiow :%d", studenci[i].rok_studiow); printf("\n Wydzial } getch(); return 0;} :%s", studenci[i].wydzial); Język ANSI C – struktury Przykład. Zapis struktur do pliku i odczyt. #include <condefs.h> #include <stdio.h> #include <conio.h> #include <stdlib.h> struct student { char nazwisko[20]; char imie[20]; }; FILE *fp; char s[20]="pliktest.dat"; Język ANSI C – struktury Przykład. c.d. Zapis i odczyt struktur int main(int argc, char **argv) { char ch; int i; struct student tab[4]={ {"iksinski","jan"},{"ygrekowski","andrzej "}, {"wojcik","marcin"},{"kowalski","marek"}}; //inicjalizacja tablicy struktur struct student tab1[4],st; fp=fopen(s,"w+b"); fwrite(&tab,sizeof(student),4,fp);// zapis tablicy struktur do pliku, // można zapisać 4 bloki pętla nie jest konieczna, fseek(fp,0,SEEK_SET); i=1; while (fread(&st,sizeof(student),1,fp)) { printf("\n Dane studenta %d",i++); printf("\n Nazwisko:%s", st.nazwisko); printf("\n Imie:%s", st.imie); } getch(); return 0; } Język ANSI C – struktury