WYKŁAD 4, 5 i 6 Wprowadzenie do języka C Mój pierwszy, drugi,..., n-ty program w języku C Programy: c1_1.c... c1_9.c Tomasz Zieliński
KOMPILATOR vs. INTERPRETER (1) - ogólnie C/C++ = kompilatory tłumaczą od razu cały program na kod maszynowy potem dopiero program jest wykonywany stosowane kiedy jest już znany algorytm rozwiązania Basic = interpretery Matlab tłumaczona i wykonywana jest linia po linii programu stosowane kiedy jest poszukiwany algorytm rozwiązania Kompilator Interpreter Zalety Szybkie działanie programu Możliwość optymalizacji kodu programu, np. minimalizacji potrzebnej pamięci Krótka droga do pomysłu do jego weryfikacji Wady Długa droga do pomysłu do jego weryfikacji Wolne działanie programu
KOMPILATOR vs. INTERPRETER (2) - praca nad programem f1.obj, f2.obj,... statyczne (lib) i dynamiczne (dll) dołączanie bibliotek system.lib Lib Maker Konsolidator my.lib system.dll my.dll wykonanie całego programu Edytor tekstu KOMPILATOR programu Linker System operacyjny prog.c prog.obj prog.exe INTERPRETER to oddzielny program / aplikacja 1 2 Edytor linii Program czyli zbiór linii 1 2 INTERPRETER Interpreter linii >>...?... (CR) Egzekutor linii
KOMPILATOR vs. INTERPRETER (3) szybkość pisania vs. szybkość działania czas napisania i uruchamiania (usunięcia błędów) programu czas wykonania programu Asembler Język C/C++ Matlab Simulink WNIOSEK: kiedy poszukuję rozwiązania problemu, wybieram język wysokiego poziomu, nawet graficzny, z dużą liczbą bibliotek (szybko piszę program, który wolno działa) kiedy znam rozwiązanie problemu wybieram język niskiego poziomu (wolno piszę program, który szybko działa)
Kod znak-moduł 0 0 0 1 0 0 0 0 = +16 (16) + 1 0 0 1 0 0 0 0 = 16 (144) --------------------------------- 1 0 1 0 0 0 0 0 = 32 (160) Kod uzupełnień do dwóch U2 ARYTMETYKA KOMPUTEROWA (1) (problem zapisu znaku liczby) 0 0 0 1 0 0 0 0 = +16 1 1 1 0 1 1 1 1 (zaneguj wszystkie bity) + 1 (dodaj 1 do wyniku negacji) ------------------------- 1 1 1 1 0 0 0 0 = 16 (128+64+32+16 = 240) +16 = 0 0 0 1 0 0 0 bez zmiany do pierwszej 1 włacznie, a potem negacja + 0 0 0 1 0 0 0 0 (+16) 1 1 1 1 0 0 0 0 (-16) -16 = 1 1 1 1 0 0 0 1 0.0 0 0 0 0 0 0 (+0)
ARYTMETYKA KOMPUTEROWA (2) (przykład zastosowania kodu U2) 00000110 = 6 00000101 = 5 00000110 = 6 -------------- + 11111011 = 5 11111010 ------------------------ + 1 1 00000001 = 1 -------------------- 11111011 = 5
ARYTMETYKA KOMPUTEROWA (3) (problem zapisu liczb niecałkowitych, ułamkowych w kodzie U2) 0 1 0 0 0 1 1 0 binarnie (70) ----------------------------------------------------------------------------------- 1 2 1 2 2 2 3 2 4 2 5 2 6 2 7 wagi 1 1/2 1/4 1/8 1/16 1/32 1/64 1/128 ----------------------------------------------------------------------------------- wynik 1/2 1/32 1/64 = 35/64 = 0.546875 1 0 1 1 1 0 1 0 binarnie (-70) ---------------------------------------------------------------------------------- 1 2 1 2 2 2 3 2 4 2 5 2 6 2 7 wagi 1 1/2 1/4 1/8 1/16 1/32 1/64 1/128 ---------------------------------------------------------------------------------- wynik 1 1/4 1/8 1/16 1/64 = 35/64 = 0.546875 PRZYKŁADY: 00000110 = 0 + (1/32 + 1/64) = 3/64 11111011 = 1 + (1/2 + 1/4 + 1/8 + 1/16 + 1/64 + 1/128) = 1 + 123/128 = 5/128
ARYTMETYKA KOMPUTEROWA (4) (problem zapisu bardzo małych/dużych liczb rzeczywistych) x = 0,009765625 = 0,9765625*10-2 = 0,625 * 2-6 x = (-1) z * m * 2 c x = z 1 bit c 8 bitów (p) m 23 bity, c 8 bitów (p) = c + 127, -126 c 127 8bitów 23 bity x = 1 01111001 1010 0000 0000 0000 0000 000 1/2 + 1/8 = 5/8 = 0,625 121 = 0 + 1*64 +1*32 + 1*16 + 1*8 + 0*4 + 0*2 + 1*1 121 = -6+127
/* Przyklad 1 - ver. 1 - mój pierwszy program */ // komentarz #include <stdio.h> // opis użycia funkcji z biblioteki stdio.h // w tym opis wywołania funkcji printf main() // początek programu // otwarcie bloku instrukcji programu float kasa, moje; // deklaracja zmiennych int osoby; // kasa, moje, osoby kasa = 1500.45; osoby = 4; // inicjalizacja wartości zmiennych // kasa i osoby moje = kasa/osoby; // użycie zmiennych kasa i osoby // obliczenie mojego kieszonkowego // nowa linia printf(" moje kieszonkowe = %7.3f \n", moje ); // wydruk na monitor // nowa linia return(0); // wyjście z programu // zamknięcie bloku instrukcji programu /* OPIS PROGRAMU 1) Program powinien zaczynać komentarz co dany program robi: /*... co ja robię?.. */. 2) Następnie dołączany jest zbiór opisów wywołań funkcji bibliotecznych: np. #include <stdio.h> - biblioteka standardowego wejścia (input) i wyjścia (output) #include <math.h> - biblioteka funkcji matematycznych sin(), cos(), exp(), log(),... #include <string.h> - biblioteka operacji na łańcuchach znaków 3) main()... mój program... - program główny (funkcja główna) char to kod znaku zapisany na 8 bitach (1 bajt) int to liczba całkowita ze znakiem zapisana na 16 bitach (2 bajtach) long to liczba całkowita ze znakiem zapisana na 32 bitach (4 bajtach) float to liczba zmiennoprzecinkowa zapisana na 32 bitach (4 bajtach) double to liczba zmiennoprzecinkowa zapisana na 64 bitach (8 bajtach) 4) W wyniku deklaracji typu zmiennych (liczba bitów i sposób zapisu) kompilator przydziela pamięć poszczególnym zmiennym. 5) W wyniku inicjalizacji zmiennych do przydzielonych pamięci zostają zapisane ich wartości. 6) Użycie zmiennych to operacja na ich nazwach (operatory arymetyczne: +, -, *, /, % - reszta z dzielenia dwóch liczb całkowitych). Przykładowo operacja moje = kasa/osoby oznacza: a) pobierz z pamięci wartości zmiennych kasa i osoby do rejestrów procesora b) podziel zawartość tych rejestrów c) zapisz wynik do pamięci przydzielonej do przechowywania zmiennej moje 7) Funkcja printf( moje kieszonkowe = %f PLN \n, moje) wypisze na ekranie monitora: moje kieszonkowe = 375.1125 PLN %d %f %c %s... - znaki przekształceń (decimal, float, character, string) \n \t \b - znaki sterujące kursorem */
/* Przyklad 1 ver. 2 - #define, scanf */ #include <stdio.h> #define OSOBY 4 // definicja stałej tekstowej OSOBY // podczas kompilacji tekst OSOBY main() // będzie zawsze zastępowany przez 4 float kasa, moje; printf("ile do podzialu? "); scanf("%f", &kasa); moje = kasa/osoby; // na ekranie monitora: nowa linia // na ekranie monitora: pytanie // wczytanie liczby z klawiatury // do zmiennej kasa printf(" moje kieszonkowe = %7.3f \n", moje ); return(0); /* OPIS PROGRAMU 1) Stałe tekstowe są definiowane na początku programu: dlatego jest je bardzo łatwo znaleźć oraz zmienić ich wartość. Np. liczba znaków w wierszu, długość bufora,... 2) Są one pisane dużymi literami: dlatego łatwo jest je odróżnić od zmiennych w tekście programu. 3) Funkcja scanf("%f", &kasa): a) wczytuje z klawiatury sekwencję znaków ASCII (zakończonych klawiszem ENTER), b) zamienia ją na liczbę binarną o zadanym formacie (u nas %f, czyli float) c) zapisuje ją do komórek pamięci, przydzielonych zmiennej kasa. 4) WAŻNE: kasa - nazwa zmiennej, &kasa - adres w pamięci zmiennej kasa, *adres - wartość liczby znajdującej się w pamięci pod zadanym adresem wskaźnik to zmienna będąca adresem w pamięci, pokazująca na liczbę określonego typu */ PRZYKŁAD definicji wskaźników do pamięci, pokazujących na liczby określonego typu: int x, y; // deklaracja zmiennych x i y int *pa, *pb; // deklaracja wskaźników pa i pb do zmiennych typu int pa = &x; pb = &y; // wskaźnik pa ma wskazywać na zmienną x, zaś pb - na zmienną y *pa = 10; *pb = 20; // zapisanie liczb 10 i 20 pod zadane adresy, czyli inicjalizacja // wartości zmiennych x oraz y
/* Przyklad 1 - ver. 3 - funkcja prosta */ #include <stdio.h> #define OSOBY 4 // deklaracja sposobu wywołania funkcji // z biblioteki stdio float wzor( float forsa ); // deklaracja sposobu wywołania // mojej funkcji /* program glowny ---------------------------------------------------------- */ main() float kasa, moje; printf("ile do podzialu? "); scanf("%f", &kasa); // wywołanie mojej funkcji moje = wzor( kasa ); // jest do niej przekazana wartość zmiennej // kasa, a odebrana wartość zmiennej moje printf("moje kieszonkowe = %7.3f \n", moje); return(0); /* definicja funkcji pomocniczej --------------------------------------------- */ float wzor( float forsa ) float temp; // funkcja otrzymuje i zwraca liczbę typu float // wewnętrzna zmienna pomocnicza temp = forsa/osoby; // obliczenie wartości zmiennej temp return( temp ); // przekazanie tej wartości do programu /* OPIS PROGRAMU 1) Sposób wywołania funkcji pisanych przez nas musi być zdefiniowany przed programem głównym, analogicznie jak dla funkcji bibliotecznych. 2) Należy podać: a) typy zmiennych przekazywanych do funkcji b) typ zmiennej odbieranej z funkcji za pomocą komendy return()
3) Do funkcji są przekazywane wartości zmiennych, tzn. liczby odczytane z pamięci spod adresów komórek przydzielonych zmiennym, lub sam adres do pamięci. W tym drugim przypadku funkcja może sama odczytać wartość zmiennej oraz ją zmodyfikować zapisując nową liczbę pod adres zmiennej. moje = wzór( kasa ) - przekazanie wartości zmiennej (kopii liczby z pamięci) moje = wzór( &kasa ) - przekazanie adresu zmiennej w pamięci W naszym przykładzie przekazujemy wartość zmiennej (kopię tego co jest w pamięci). 4) Funkcja ma dostęp do następujących wartości zmiennych: a) otrzymanych z programu nadrzędnego (wartość lub adres) b) globalnych, tzn. zdefiniowanych poza nawiasami programu głównego, a więc nie ukrytych wewnątrz jego nawiasów, czyli dostępnych dla wszystkich ; c) lokalnych, tzn. takich które sama powołała do życia W naszym przypadku: a) forsa to przekazana wartość kasy, b) stała tekstowa OSOBY jest zdefiniowana poza wszystkimi nawiasami, a więc może być także używana wewnątrz funkcji, c) temp to lokalna zmienna funkcji. 5) Wewnętrzne nazwy zmiennych, których używa funkcja, mogą być różne niż nazwy zmiennych, których wartości przekazano do funkcji. Pierwsze z nich są nazwami formalnymi a drugie nazwami wywołania. W naszym przykładzie możemy do funkcji wzor() przekazać wartość budżetu dowolnej rodziny, tzn. kasę1, kasę2 lub kasę3. Wewnątrz funkcji ta wartość zawsze będzie nazywana forsą. Oznacza to, że można wywoływać funkcję dla różnych danych. Funkcję piszemy raz, a używamy wielokrotnie. Prowadzi to do tzw. programowania strukturalnego (modułowego): program główny to pień, z którego wchodzimy do konarów - funkcji pomocniczych (z których możemy zagłębiać się dalej do dalszych gałęzi bocznych, czyli pod-funkcji). */
/* Przyklad 1 - ver. 4 - funkcja zlozona wieloargumentowa */ #include <stdio.h> #define OSOBY 3 void wzor( float forsa, float *osoba1, float *osoba2, float *osoba3 ); /* program glowny ----------------------------------------------------------------- */ main() float kasa; float tata, mama, ja; printf(" ile do podzialu? "); scanf("%f", &kasa); // wywołanie funkcji wzor( kasa, &tata, &mama, &ja ); // wartość kasy // adres do taty, mamy i do mnie printf("tata = %7.3f, mama = %7.3f, ja = %7.3f \n", tata, mama, ja); return(0); /* funkcja wlasna - pomocnicza ---------------------------------------------- */ void wzor( float forsa, float *osoba1, float *osoba2, float *osoba3 ) float srednia; /* wewnetrzna zmienna pomocnicza */ srednia = forsa/osoby; *osoba1 = 1.25 * srednia; *osoba2 = 1.00 * srednia; *osoba3 = 0.75 * srednia;
/* OPIS PROGRAMU 1) Nie jesteśmy już egoistami. Interesuje nas teraz także kieszonkowe taty, mamy, a nie tylko własne. 2) W związku z czym deklarujemy trzy zmienne typu float: tata, mama, ja. 3) Do funkcji wzor() przekazujemy adresy w pamięci do tych zmiennych: &tata, &mama, &ja. 4) Wewnątrz funkcji adresy te oznaczone jako: osoba1, osoba2, osoba3. Pisząc float *osoba1, mówimy: wartość pod adresem osoba1 jest liczbą typu float. Czyli mówimy, że osoba1 jest wskaźnikiem pokazującym na liczbę typu float. 4) Wewnątrz funkcji wyliczamy co trzeba i podstawiamy wynik pod adresy przekazane podczas wywołania funkcji, np.: *osoba1 = 1.25*srednia, czyli wartość pod adresem osoba1 ma być równa 1.25*srednia; tzn. podstaw wartość 1.25*srednia pod adres osoba1. 5) Teraz możemy pisać funkcje wieloargumentowe: wzor(a, b, c, &x, &y, &z) - wartości (kopie) zmiennych a, b,c, adresy do zmiennych x, y, z - funkcja nie może zmienić wartości zmiennej, której kopię otrzymała; może z niej tylko skorzystać; - funkcja może korzystać i zmieniać wartości zmiennych, których zna adres w pamięci */
/* Przyklad 1 ver. 5 - tablice, for, if */ #include <stdio.h> #define OSOBY 3 // void = funkcja nic nie void wzor( float forsa, float czlonkowie[] ); // zwraca poprzez return // przekazanie tablicy /* program glowny -------------------------------------------------------------------- */ main() float kasa; float rodzina[ OSOBY ]; char *imiona[ OSOBY ] = "tata", "mama", "ja"; int i; printf( "\n" ); printf( "ile do podzialu?" ); scanf( "%f", &kasa ); wzor( kasa, rodzina ); for( i=0; i<osoby; i++ ) printf( "osoba nr %d, czyli %s = %7.3f \n", i, imiona[i], rodzina[i] ); if ( rodzina[1] > rodzina[2] ) // >, >=, <, <=, ==,!= printf( "Jakis kant! Mam za malo! \n" ); else printf( "Jest dobrze! \n" ); printf( "\n" ); return(0);
/* funkcja wlasna - pomocnicza --------------------------------------------- */ void wzor( float forsa, float czlonkowie[] ) float srednia; /* wewnetrzna zmienna pomocnicza */ srednia = forsa/osoby; czlonkowie[0] = 1.25 * srednia; czlonkowie[1] = 1.00 * srednia; czlonkowie[2] = 0.75 * srednia; /* OPIS PROGRAMU 1) void wzor() - oznacza, że funkcja nic nie zwraca poprzez return. 2) void wzor( float forsa, float czlonkowie[] ) - do funkcji jest przekazana wartość zmiennej forsa oraz tablica czlonkowie[] liczb typu float; Tablica jest blokiem wartości liczbowych tego samego typu zapisanych w pamięci jedna za drugą. Przekazanie tablicy, to przekazanie adresu do początku tego bloku komórek pamięci. 3) float rodzina[ OSOBY ] - deklaracja tablicy 3-elementowej (gdyż OSOBY = 3), czyli trzech liczb zmiennoprzecinkowych typu float umieszczonych w pamięci jedna za drugą, jako jeden wspólny blok danych, oraz oznaczanych jako: rodzina[0], 0 - zerowe przesunięcie w stosunku do początku bloku rodzina[1], 1 - przesunięcie o szerokość jednej liczby typu float od początku bloku rodzina[2], 2 - przesunięcie o szerokość dwóch liczb typu float od początku bloku PRZYKŁAD: int a[3]; a[0] = 1; a[1] = 10; a[2] = a[0] + a[1]; 4) char *imiona[ OSOBY ] = "tata", "mama", "ja"; Deklaracja 3-elementowej tablicy adresów do łańcuchów znaków (tekstów, napisów) oraz równoczesna inicjalizacja wartości znajdujących się pod tymi adresami, czyli zapisanie do pamięci pod przydzielone adresy następujących słów: tata, mama, ja. 5) wzor( kasa, rodzina ) - wywołanie funkcji wzor() oraz przekazanie do niej wartości (kopii) zmiennej kasa oraz adresu do początku bloków komórek pamięci, w których jest przechowywana tablica rodzina, czyli trzy liczby typu float: rodzina[0], rodzina[1], rodzina[2]. Wewnątrz funkcji tablica rodzina nosi nazwę członkowie. Ponieważ adres początkowy bloku komórek pamięci jest ten sam, dlatego: czlonkowie[0] = rodzina[0] czlonkowie[1] = rodzina[1] czlonkowie[2] = rodzina[2] Wyliczone wartości kieszonkowe są podstawiane w następujący sposób: czlonkowie[0] = 1.25 * srednia; i tak dalej
6) Instrukcja sterująca for: for( inicjalizacja1, warunek2, operacja4 ) zbiór instrukcji 3 Kolejność: a) dokonaj inicjalizacji1 b) sprawdź warunek2 c) jeśli jest prawdziwy, to wykonaj zbiór instrukcji 3; w przeciwnym wypadku KONIEC instrukcji for d) wykonaj operację4 e) skocz do punktu b) W naszym programie dla kolejnych wartości 0, 1, 2 zmiennej i są wypisywane wartości tablic imiona[i] oraz rodzina[1], czyli: osoba nr 0, czyli tata = 625.187 osoba nr 1, czyli mama = 500.150 osoba nr 2, czyli ja = 375.112 7) Instrukcja sterująca if: if ( warunek ) if ( warunek ) wersja zbiór instrukcji A zbiór instrukcji A uproszczona else zbiór instrukcji B Kolejność: a) sprawdź warunek b) jeśli jest prawdziwy, to wykonaj zbiór instrukcji A c) w przeciwnym przypadku, wykonaj zbiór instrukcji B W naszym programie: ponieważ rodzina[1] > rodzina[2] (500.150 > 375.112) dlatego na monitorze zostanie wyświetlony napis Jakiś kant! Mam za mało!. Złożony warunek logiczny: AND (warunek1 && warunek2) np. if( (a>1) && (b<2) ) OR (warunek1 warunek 2) np. if( (a>b) (b>c) ) 8) Inne instrukcje sterujące: while( warunek ) do np. int i; i = 10; zbiór instrukcji zbiór instrukcji do ----------------------------- while( warunek ) i = i-1; a) sprawdź warunek ----------------------------------- while( i > 0) b) jeśli jest prawdziwy, a) wykonaj zbiór instrukcji to wykonaj zbiór instrukcji b) sprawdź warunek c) powrót do pkt. a) c) jeśli jest prawdziwy, to powrót do pkt. a) goto ETYKIETA switch( wyrażenie ) np. char c; c = getchar();... case wyrażenie-stałe: instr switch( c ) ETYKIETA: default: case a : printf( a ); break; case b : printf( b ); break; default: printf( NIE a, NIE b ); 9) Operatory bitowe są podobne do operatorów logicznych: AND = & np. c = a & b (AND odpowiadających sobie bitów liczb a, b) OR = np. c = a b (OR...) XOR = ^ np. c = a ^ b (XOR...) Przesuniecie bitowe liczby: >> - w prawo np. b = a >> 1 (przesuń bity liczby a o 1 bit w prawo, b 2x mniejsze) << - w lewo np. b = a << 1 (przesuń bity liczby a o 1 bit w lewo, b 2x większe) np. c = a << b (przesuń bity liczby a o b bitów w lewo) */
/* Przyklad 1 ver. 6 - wartości argumentów z linii wywolania */ /* Uruchomienie programu: c1_6 <liczba> <Enter> */ /* np.: c1_6 1500 <Enter> */ #include <stdio.h> #include <stdlib.h> #define OSOBY 3 void wzor( float forsa, float czlonkowie[] ); // funkcja obliczająca /* program glowny ----------------------------------------------------------- */ main(argc, argv) /* argc to liczba parametrów, u nas argc = 2 */ int argc; /* parametr 1 - argv[ 0 ] = nazwa programu */ char *argv[]; /* parametr 2 - argv[ 1 ] = wartość kasy */ float kasa; float rodzina[ OSOBY ]; char *imiona[ OSOBY ] = "tata", "mama", "ja"; int i; if (argc < 2) // sprawdzenie liczby argumentów, // powinny być dwa printf("\n Blad! Wywolanie programu: c1_6 <liczba> \n"); return(0); // konwersja łancucha znaków ASCII kasa = atof( argv[ 1 ] ); // na liczbę typu float i podstawienie do kasy printf( "\n kasa = %7.3f \n", kasa ); wzor( kasa, rodzina ); printf( "\n" ); // nowa linia na monitorze for( i=0; i<osoby; i++) printf( "osoba nr %d, czyli %s = %7.3f \n", i, imiona[i], rodzina[i] ); // nowa linia na monitorze return(0);
/* funkcja wlasna - pomocnicza ------------------------------------------------- */ void wzor( float forsa, float czlonkowie[] ) float srednia; // wewnętrzna zmienna pomocnicza srednia = forsa/osoby; *czlonkowie++ = 1.25 * srednia; // podstaw, zwiększ wskaźnik *czlonkowie++ = 1.00 * srednia; // podstaw, zwiększ wskaźnik *czlonkowie = 0.75 * srednia; // podstaw /* OPIS PROGRAMU Wprowadzanie wartości parametrów bezpośrednio z linii wywołania jest bardzo często stosowane (na niebiesko - nazwa programu, na czerwono - parametr zawsze wprowadzany jako sekwencja znaków ASCII): edit program.c gcc program.c Przykładowo w programach c9_3.c, c9_4.c oraz c9_5.c wprowadza się w ten sposób parametry nagrywania zbiorów dźwiękowych typu WAVE: c9_5 <nazwa.wav> < 44/32/24/22/16/11/8 > < s/m > < 16/8 > czestotliwości liczba próbkowania bitow nazwa w khz zbioru Mono wave Stereo np. c9_5 test.wav 44 s 16 = zbiór test.wav, 44 khz, stereo, 16 bitów Ponieważ parametry są wprowadzane jako sekwencja znaków ASCII, muszą być potem zamienione na zmienną określonego typu: i = atoi( argv[ 1 ] ); f = atof( argv[ 2 ] ); // ASCII to integer // ASCII to float */
/* Przyklad 1 ver. 7 - wartości argumentów ze zbioru */ #include <stdio.h> #include <stdlib.h> #define OSOBY 3 void wzor( float forsa, float czlonkowie[] ); /* program glowny -------------------------------------------------------------------- */ main() FILE *FileIn, *FileOut; // deklaracja uchwytów FileIn, FileOut // do zbiorów float kasa; float rodzina[ OSOBY ]; char *imiona[ OSOBY ] = "tato", "mama", "ja"; int i; // przyporządkuj zbiory do uchwytów FileIn = fopen( "we.dat", "r"); // otwórz zbior we.dat do czytania FileOut = fopen( "wy.dat", "w"); // otwórz zbior wy.dat do zapisu fscanf( FileIn, "%f", &kasa); // przeczytaj ze zbioru (uchwyt FileIn) fprintf( FileOut, "kasa = %7.3f \n", kasa ); // zapisz do zbioru (FileOut) wzor( kasa, rodzina ); // oblicz ile komu for( i=0; i<osoby; i++) fprintf( FileOut, " %s = %7.3f \n", imiona[i], rodzina[i] ); // zapisz fclose( FileIn ); fclose( FileOut ); // zamknij zbior (uchwyt FileIn), czyli we.dat // zamknij zbior (uchwyt FileOut), czyli wy.dat return(0); /* Kolejność: 1) zdefiniuj uchwyty do zbiorów, 2) otwórz odpowiednie zbiory do czytania ( r ), pisania ( w ), czytania & pisania ( rw ) i przyporządkuj je do uchwytów, 4) czytaj ze zbiorów i zapisuj do zbiorów, 5) zamknij zbiory */
/* Przykład 1 ver. 8 - struktury, czyli zmienne złożone */ #include <stdio.h> #define OSOBY 3 // DEKLARACJA STRUKTURY osoba struct osoba // Przykład inicjalizacji zmiennej "x" o tej strukturze char imie[10]; // struct osoba x; // deklaracja typu x float kieszonkowe; // strcpy( x.imie, "tata"); // imienia x ; // x.kieszonkowe = 500.123; // kieszonkowego x void wzor( float forsa, struct osoba iksinscy[] ); // program główny -------------------------------------------------------------------- main() // tablica zmiennych o strukturze osoba float kasa = 1500.45; // kowalscy[0].imie struct osoba kowalscy[ OSOBY ]; // kowalscy[0].kieszonkowe char *imiona[ OSOBY ] = "tata", "mama", "ja"; int i; for( i=0; i<osoby; i++ ) // To samo prościej: // kowalscy[0].imie = imiona[0]; strcpy( kowalscy[i].imie, imiona[i] ); // kowalscy[1].imie = imiona[1]; // kowalscy[2].imie = imiona[2]; wzor( kasa, kowalscy ); // wywołanie funkcji liczącej kieszonkowe for( i=0; i<osoby; i++) // wydruk kieszkonkowego członków rodziny printf("osoba %d, czyli %s = %7.3f \n", i, kowalscy[i].imie, kowalscy[i].kieszonkowe); getchar(); return(0);
// funkcja własna - pomocnicza --------------------------------------------- void wzor( float forsa, struct osoba iksinscy[] ) float srednia; // wewnętrzna zmienna pomocnicza srednia = forsa/osoby; iksinscy[0].kieszonkowe = 1.25 * srednia; iksinscy[1].kieszonkowe = 1.00 * srednia; iksinscy[2].kieszonkowe = 0.75 * srednia; /* OPIS PROGRAMU W języku C można definiować zmienne złożone ( wielozmienne ) o złożonej strukturze. struct wielozmienna // deklaracja struktury o nazwie wielozmienna int a; // która się składa z jednej liczby a typu int float b; // jednej liczby b typu float char c; // oraz jednego znaku c ; struct wielozmienna x; x.a = 1; x.b = 1.2345; x.c = r ; // deklaracja zmiennej złożonej x typu wielozmienna // inicjalizacja podzmiennej a zmiennej x (int) // inicjalizacja podzmiennej b zmiennej x (float) // inicjalizacja podzmiennej c zmiennej x (char) Wskaźnik (adres) do zmiennych złożonych można przekazywać do funkcji, np. funkcja( &x ); // wywołanie funkcji. przekazanie wskaźnika (adresu) void funkcja( struct wielozmienna *y) // definicja funkcji (*y).a = 1; // inaczej: y->a = 1; (*y).b = 1.2345; // inaczej: y->b = 1.2345; (*y).c = r // inaczej: y->c = r ; Możemy definiować tablice zmiennych złożonych, np. struct wielozmienna x[10]; x[0].a = 1; x[0].b = 1.2345; x[0].c = r ; Stuktury mogą mieć budowę drzewiastą, tzn. jedna struktura może wchodzić w skład innej struktury, np. struktura adres może wchodzić w skład struktur pacjent, student,... itp. struct student struct adres char nazwisko[30]; char miasto[20]; char imie[20]; long kod; struct adres adr; char ulica[40]; int wiek; int numer; int wzrost; long mieszkanie; zm; ; zm.adr.kod = 30059; */
/* Przykład 1 ver. 9 - lista dynamiczna członków rodziny */ /* patrz wykład nr 7 i 8 */ #include <stdio.h> #include <stdlib.h> #define OSOBY 3 // DEKLARACJA STRUKTURY osoba struct osoba // Przykład inicjalizacji zmiennej x o strukturze osoba: char imie[10]; // struct osoba x; float kieszonkowe; ; // strcpy( x.imie, "tata"); // inicjalizacja imienia x // x.kieszonkowe = 500.123; // inicjalizacja kieszonkowego x struct wezel struct osoba ktos; // wewnątrz węzła jest zmienna o strukturze osoba struct wezel *nastepny; // oraz adres do następnego węzła ; // np. struct wezel w; // strcpy(w.ktos.imie,"tata"); // w.ktos.kieszonkowe = 500.123; // (*w).nastepny =?; lub w->nastepny =?; // DEKLARACJA WĘZŁA LISTY JEDNOKIERUNKOWEJ typedef struct wezel *ADRwezel; // ADRwezel jest teraz nazwą nowego typu, // czyli adresu węzła void wzor( float forsa, ADRwezel lista ); // deklaracja wywołania funkcji // program główny --------------------------------------------------------------------- main() float kasa = 1500.45; // deklaracja i inicjalizacja wartości ADRwezel lista, w, w0, w1, w2; // deklaracja 5 adresów węzłów int i; // deklaracja zmiennej pomocniczej // dynamiczna alokacja pamięci dla 3 węzłów w0 = (ADRwezel) malloc( sizeof( struct wezel ) ); w1 = (ADRwezel) malloc( sizeof( struct wezel ) ); w2 = (ADRwezel) malloc( sizeof( struct wezel ) ); lista = w0; // lista = adres pierwszego węzła strcpy(w0->ktos.imie,"tata"); // podstaw imię osoby w węźle 0 strcpy(w1->ktos.imie,"mama"); // podstaw imię osoby w węźle 1 strcpy(w2->ktos.imie,"ja"); // podstaw imię osoby w węźle 2
// każdy węzeł, poza ostatnim, pokazuje na węzeł następny w0->nastepny = w1; // węzeł 0 pokazuje na węzeł 1 w1->nastepny = w2; // węzeł 1 pokazuje na węzeł 2 w2->nastepny = NULL; // węzeł 2 na nic nie pokazuje wzor( kasa, lista ); // wywołanie funkcji // nowa linia w = lista; // ustaw wskaźnik na początek listy while( w!= NULL ) // wydruk kieszonkowego printf( "osoba nr %d, czyli %s = %7.3f \n", i, w->ktos.imie, w->ktos.kieszonkowe ); w = w->nastepny; // ustaw wskaźnik na następny węzeł // nowa linia getchar(); // czekaj na znak z klawiatury free( w0 ); free( w1 ); free( w2 ); // zwolnienie pamięci węzłów return(0); // funkcja własna - pomocnicza -------------------------------------------------- void wzor( float forsa, ADRwezel w ) float srednia; // wewnętrzna zmienna pomocnicza srednia = forsa/osoby; w->ktos.kieszonkowe = 1.25 * srednia; // kieszonkowe w węźle 0 w = w->nastepny; // adres następnego węzła w->ktos.kieszonkowe = 1.00 * srednia; // kieszonkowe w węźle 1 w = w->nastepny; // adres następnego węzła w->ktos.kieszonkowe = 0.75 * srednia; // kieszonkowe w węźle 2