Pobieranie prezentacji. Proszę czekać

Pobieranie prezentacji. Proszę czekać

Języki Programowania Patryk Jasik Katedra Fizyki Teoretycznej i Informatyki Kwantowej Wydział Fizyki Technicznej i Matematyki Stosowanej Politechnika Gdańska.

Podobne prezentacje


Prezentacja na temat: "Języki Programowania Patryk Jasik Katedra Fizyki Teoretycznej i Informatyki Kwantowej Wydział Fizyki Technicznej i Matematyki Stosowanej Politechnika Gdańska."— Zapis prezentacji:

1 Języki Programowania Patryk Jasik Katedra Fizyki Teoretycznej i Informatyki Kwantowej Wydział Fizyki Technicznej i Matematyki Stosowanej Politechnika Gdańska Pokój 415GB E-mail: p.jasik@mif.pg.gda.plp.jasik@mif.pg.gda.pl Strona domowa: http://aqualung.mif.pg.gda.plhttp://aqualung.mif.pg.gda.pl Konsultacje: wtorek 17 – 18

2 Warunki zaliczenia przedmiotu Sprawdzian z programowania (40 pkt) Projekt programistyczny – aplikacja (40 pkt) Uzyskanie 50% punktów spośród wszystkich możliwych do zdobycia (40 pkt/80 pkt) Termin sprawdzianu z programowania: Termin przesłania projektu programistycznego

3 Warunki zaliczenia przedmiotu Zakres punktówOcena 80bdb plus 79 - 72bdb 71 - 64db plus 63 - 56db 55 - 48dst plus 47- 40dst

4 Literatura I. Sommerville – „Inżynieria oprogramowania”, WNT, Warszawa 2003 B.W. Kernighan, D.M. Ritchie – „Język ANSI C”, WNT, Warszawa 2003 C.L. Tondo, S.E. Gimpel – „Język ANSI C – ćwiczenia i rozwiązania”, WNT, Warszawa 2004 N. Wirth – „Algorytmy + struktury danych = programy”, WNT 2002 S. Prata – „Język C. Szkoła programowania”, Helion K.N. King - „Język C. Nowoczesne programowanie.”, Helion S. Oaulline – „Język C. Programowanie”, Helion M. Tłuczek – „Programowanie w języku C. Ćwiczenia praktyczne”. „Programowanie w C”, stworzone na Wikibooks, bibliotece wolnych podręczników, wydanie II, 2010 Niniejszy wykład powstał głównie na podstawie zaznaczonych pozycji.

5 Pierwszy program #include int main() { printf ("Witajcie! To jest wyklad z proceduralnych jezykow programowania."); return 0; }

6 Drugi program #include int main() { int x, y, liczba; printf(„Podaj pierwsza liczbe: \n"); scanf(„%i”, &x); printf(„Podaj druga liczbe: \n"); scanf(„%i”, &y); liczba = x+y; printf("To jest nasz wynik: x+y=%i\n", liczba); }

7 Kompilacja GCC - GNU Compiler Collection http://gcc.gnu.org/ Dev-C++ i wxDev-C++ http://orwelldevcpp.blogspot.com/ http://wxdsgn.sourceforge.net/ Microsoft Visual Studio https://www.dreamspark.com/ NetBeans http://netbeans.org/ Eclipse http://www.eclipse.org/ Qt Creator http://qt.digia.com/

8 Preprocesor To program interpretujący, którego zadaniem jest przetworzenie tekstu wejściowego, w sposób określony za pomocą poleceń preprocesora przez programistę, na tekst wyjściowy. Dopiero tak przetworzony tekst poddawany jest analizie składniowej i kompilacji. Wynikiem działania preprocesora jest więc tekst wyjściowy po przetworzeniu podlegający następnie kompilacji. Dyrektywy preprocesora mogą występować w ogólności w dowolnym miejscu programu, a rozróżnienie ich od tekstu kodu źródłowego w językach C i C++ dokonywane jest poprzez poprzedzenie dyrektywy znakiem hash #.

9 Preprocesor Do najważniejszych dyrektyw należą: #include … - dyrektywa włączająca tekst innego pliku źródłowego w miejscu jej wystąpienia w pliku podlegającym aktualnie przetwarzaniu, przy czym możliwe jest zagłębione występowanie dyrektywy include, #define … - definiuje stałe i makroinstrukcje (pseudofunkcje) #undef … - usuwa definicje stałej lub makra #if … - dyrektywy kompilacji warunkowej #elif … - działa podobnie jak else if w języku C #endif … - oznacza koniec bloku kompilacji warunkowej #ifdef … - znaczy to samo co #if defined(…) #ifndef … - znaczy to samo co #if !defined(…)

10 Biblioteki podstawowe assert.h - deniuje makro assert(), używane do diagnostyki programu. complex.h – zestaw funkcji opisujących działania na liczbach zespolonych. ctype.h - deklaruje funkcje do klasyfikacji i konwersji znaków (kody ASCII). errno.h - definiuje makra dla warunków błędu (EDOM i ERANGE) oraz zmienną całkowitą errno, poprzez którą funkcje biblioteczne zwracają kod błędu.

11 Biblioteki podstawowe float.h - definiuje zakres wartości, które mogą być zapamiętywane w typach zmiennoprzecinkowych. fenv.h – kontrola środowiska zmiennoprzecinkowego. inttypes.h – funkcje konwersji precyzji między typami całkowitymi (integer). iso646.h – definiuje makra w standardzie iso646 (and = &&, or = ||, not_eq = !=) limits.h - definiuje wartości ograniczające dla wszystkich danych typu całkowitego (_MIN, _MAX). locale.h - deklaruje strukturę lconv i funkcje niezbędne do przystosowania programu w C do poszczególnych środowisk lokalnych. math.h - deklaruje funkcje matematyczne oraz makro HUGE_VAL.

12 Biblioteki podstawowe setjmp.h - definiuje funkcje setjmp oraz longjmp, które mogą zachowywać i przywracać za pomocą zmiennej env stan programu. Definiuje również typ danych jmp_buf używany przez setjmp i longjmp. signal.h - definiuje symbole i procedury niezbędne dla obsługi zdarzeń wyjątkowych. stdarg.h - definiuje makra, które umożliwiają dostęp do nienazwanych argumentów w funkcji, która akceptuje zmienną liczbę argumentów. stdbool.h – obsługa typów logicznych. stdint.h – obsługa różnych typów w zakresie liczb całkowitym. stddef.h - deniuje standardowe typy danych ptrdi_t, size_t, wchar_t oraz symbol NULL.

13 Biblioteki podstawowe stdio.h - deklaruje funkcje i typy danych niezbędne do obsługi operacji we/wy (operacje na plikach, formatowane wyjście/wejście, f. realizujące we/wy znakowe, f. wyznaczające pozycję w pliku, obsługa błędów). stdlib.h - deklaruje wiele funkcji użyteczności takich, jak procedury konwersji łańcuchów, generator liczb losowych, procedury alokacji pamięci i procedury kontroli procesów (takie, jak abort, exit i system), wyszukiwanie, sortowanie. string.h - deklaruje funkcje do manipulowania łańcuchami takie, jak strcmp i strcpy. time.h - deklaruje typy daty i definiuje funkcje do manipulowania czasem. Definiuje typy clock_t i time_t oraz strukturę danych tm. wchar.h – manipulacje znakami regionalnymi (różne języki) wctype.h – klasyfikacja znaków regionalnych.

14 Jednostki leksykalne identyfikatory słowa kluczowe stałe napisy operatory separatory

15 Identyfikatory Identyfikator jest ciągiem liter i cyfr. Pierwszym znakiem ciągu musi być litera; znak podkreślenia _ zalicza się do liter. Rozróżnia się wielkie i małe litery alfabetu. Identyfikatory mogą być dowolnej długości, przy czym dla identyfikatorów wewnętrznych znaczenie ma co najmniej 31 znaków początkowych. Identyfikatorami wewnętrznymi są nazwy makr i wszystkie inne nazwy nie mające zewnętrznej łączności.

16 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

17 Stałe stała-całkowita stała-znakowa stała-zmiennopozycyjna stała-wyliczeniowa

18 Stałe znakowe nowy wiersz tabulacja pozioma tabulacja pionowa cofanie powrót karetki nowa strona alarm kreska ukośna w lewo znak zapytania apostrof cudzysłów liczba ósemkowa liczba szesnastkowa \n \t \v \b \r \f \a \\ \? \’ \” \ooo \xhh

19 Przekształcenia funkcji printf/scanf ZnakTyp argumentuPrzekształcany do postaci d, iintliczby dziesiętnej ze znakiem. ointliczby ósemkowej bez znaku (bez wiodącego zera) x, Xintliczby szesnastkowej bez znaku (bez wiodącego 0x) z użyciem liter abcdef dla 0x i ABCDEF dla 0X. uintliczby dziesiętnej bez znaku. cintpojedynczego znaku po przekształceniu do typu unsigned char. schar *Znaki tekstu są wypisywane aż do napotkania znaku ‘\0’ lub liczba wypisanych znaków osiągnie wskazaną precyzję. fdoubleliczby dziesiętnej [-]mmm.ddd, gdzie liczbę cyfr d określa precyzja. Domyślną precyzją jest 6; przy precyzji 0 opuszcza się kropkę dziesiętną. e, Edoubleliczby dziesiętnej [-]m.dddddde±xx lub [-]m.ddddddE±xx, gdzie liczbę cyfr d określa precyzja. g, GdoubleJeśli wykładnik potęgi jest mniejszy niż -4, większy lub równy precyzji, to stosuje się specyfikację %e lub %E; w przeciwnym przypadku %f. Nie wypisuje się nie znaczących zer i zbędnej kropki dziesiętnej. pvoid *wskaźnika (reprezentacja zależy od reprezentacji). nint *Liczbę dotychczas wypisanych znaków zapisuje się do odpowiedniego argumentu. Nie ma żadnego przekształcenia argumentu. %Nie ma żadnego przekształcenia argumentu-zostanie wypisany znak %.

20 Specyfikatory typów prostych void – „typ pusty” (ang. void znaczy “pusty”). Słowa kluczowego void można w określonych sytuacjach użyć tam, gdzie oczekiwana jest nazwa typu. void nie jest właściwym typem, bo nie można utworzyć zmiennej takiego typu; Typ void przydaje się do zaznaczania, że funkcja nie zwraca żadnej wartości lub że nie przyjmuje żadnych parametrów. _Bool – przechowuje wartość zero lub jeden. 1 bajt, tylko w wersji C99, stdbool.h char – Służy głównie do przechowywania znaków. Od kompilatora zależy, czy jest to liczba ze znakiem czy bez; w większości kompilatorów jest liczbą ze znakiem. 1 bajt, zdolny pomieścić jeden znak z lokalnego zbioru znaków (u 0-255, s -128 +127); int – Liczba całkowita, odpowiadająca podstawowemu rozmiarowi liczby całkowitej w danym komputerze. Podstawowy typ dla liczb całkowitych (2 lub 4 bajty). (-32768; +32767 lub -2 147 483 648; 2 147 483 647)

21 Specyfikatory typów prostych float – Podstawowy typ do przechowywania liczb zmiennoprzecinkowych. Nie można stosować go z modyfikatorem signed ani unsigned. 4 bajty, typ zmiennopozycyjny pojedynczej precyzji (-3,4*10 38 – +3,4*10 38 ). Najmniejsza reprezentowana liczba dodatnia wynosi 1,17*10 -38. W nowszym standardzie zgodny jest z normą IEEE 754. double – Liczba zmiennoprzecinkowa podwójnej precyzji (-1,79*10 308 – +1,79*10 308 ). Najmniejsza reprezentowana liczba dodatnia wynosi 2,22*10 -308. Podobnie jak float nie łączy się z modyfikatorem signed ani unsigned (8 bajtów). float _Complex – 8 bajtów, C99, complex.h double _Complex – 16 bajtów, C99, complex.h float _Imaginary, double _Imaginary

22 Specyfikatory typów prostych const – przypisanie stałej wartości dowolnego typu. Wartość stałej ustala się w kodzie programu i nigdy nie ulega ona zmianie. signed – typ ze znakiem, char, int unsigned – typ bez znaku, char, int short int – 2 bajty long int – 4 lub 8 bajtów long long int – 8 bajtów, C99 long double – 8, 10 lub 12 bajtów (-1,1*10 4932 – +1,1*10 4932 ). Najmniejsza reprezentowana liczba dodatnia wynosi 3,4*10 -4932. long double _Complex – 24 bajty, C99 long double _Imaginary – C99

23 Typy pochodne i złożone tablice – elementów danego typu funkcje – zawierające wartości danego typu wskaźniki – do elementów danego typu struktury – zawierające zestawy elementów różnych typów unie – zawierające dowolny z zestawu elementów o różnych typach pola bitowe – sterujące rozmiarem elementów struktury typedef – służy do definiowania typów pochodnych enum – typ wyliczeniowy, służy do tworzenia zmiennych, które powinny przechowywać tylko pewne z góry ustalone wartości

24 Modyfikatory typów volatile – znaczy ulotny, kompilator wyłączy dla takiej zmiennej optymalizację typu zastąpienia przez stałą lub zawartość rejestru, za to wygeneruje kod, który będzie odwoływał się zawsze do komórek pamięci danego obiektu. Zapobiegnie to błędowi, gdy obiekt zostaje zmieniony przez część programu, która nie ma zauważalnego dla kompilatora związku z danym fragmentem kodu lub nawet przez zupełnie inny proces. Przykład użycia: volatile float liczba; register – jeżeli zmienna będzie używana w programie bardzo często, można wykorzystać modyfikator register. Kompilator może wtedy umieścić zmienną w rejestrze, do którego ma szybki dostęp, co przyspieszy odwołania do tej zmiennej. Przykład użycia: register int liczba;

25 Modyfikatory typów static – pozwala na zdefiniowanie zmiennej statycznej. “Statyczność” polega na zachowaniu wartości pomiędzy kolejnymi definicjami tej samej zmiennej. Jest to przede wszystkim przydatne w funkcjach. Gdy zdefiniujemy zmienną w ciele funkcji, to zmienna ta będzie od nowa definiowana wraz z domyślną wartością (jeżeli taką podano). W wypadku zmiennej określonej jako statyczna, jej wartość się nie zmieni przy ponownym wywołaniu funkcji. extern – oznacza się tak zmienne globalne zadeklarowane w innych plikach, informujemy w ten sposób kompilator, żeby nie szukał jej w aktualnym pliku. auto – jest zupełnym archaizmem, który oznacza tyle, że zmienna jest lokalna. Ponieważ zmienna zadeklarowana w dowolnym bloku zawsze jest lokalna, modyfikator ten nie ma obecnie żadnego zastosowania praktycznego. Jest spadkiem po wcześniejszych językach programowania.

26 Rodzina operatorów Operatory arytmetyczne +-*/% Relacje i operatory logiczne >>=<<===!=&&|| Operatory zwiększania i zmniejszania ++-- Operatory bitowe ~&|^>><< Operatory i wyrażenia przypisania +=-=*=/=%= >=&=^=|= Operator warunkowy warunek ? wyr1 : wyr2

27 Operacje bitowe ~ 0 1 1 0 &0 1 0101 0 0 1 | 0101 1 ^0 1 0101 1 0 aa<<1a<<2a>>1a>>2 0001 0011 0101 1000 1111 1001 0010 0110 1010 0000 1110 0010 0100 1100 0100 0000 1100 0100 0000 0001 0010 0100 0111 0100 0000 0001 0010 0011 0010

28 Rodzina operatorów Przykład if((year % 4 ==0 && year % 100 != 0) || year % 400 == 0) printf(„%d jest rokiem przestępnym \n”, year); else printf(„%d nie jest rokiem przestępnym \n”, year);

29 Bloki sterujące if else else if switch case for while do while break continue goto

30 if else Przykład if (wyrażenie) { instrukcja1; instrukcja2; … } else { instrukcja3; instrukcja4; … }

31 else if Przykład if (wyrażenie1) { instrukcja1; instrukcja2; … } else if (wyrażenie2) { instrukcja3; instrukcja4; … } … else { instrukcja5; instrukcja6; … }

32 switch case Przykład switch (wyrażenie) { case wyrażenie-stałe1: instrukcje break; case wyrażenie-stałe2: instrukcje break; … default: instrukcje break; }

33 for Przykład for(wyr1; wyr2; wyr3) { instrukcja1; instrukcja2; … } for(i = 0; i < n; i++) { … }

34 while Przykład while(wyrażenie) { instrukcja1; instrukcja2; … }

35 do while Przykład do { instrukcja1; instrukcja2; … } while(wyrażenie);

36 Break Przykład /* trim: usuń z s końcowe znaki odstępu, tabulacji, nowego wiersza */ int trim(char s[]) { int n; for(n = strlen(s)-1; n>=0; n--) if(s[n] != ‘ ’ && s[n] != ‘\t’ && s[n] != ‘\n’) break; s[n] = ‘\0’; return n; }

37 continue i exit Przykład for(i =0; i < n; i++) { if(a[i] < 0) /*pomiń element ujemny*/ continue; … /*przetwarzaj element nieujemny*/ } exit(kod_wyjścia) 0 – poprawne zakończenie, EXIT_SUCCESS kod != 0 – błędne zakończenie, EXIT_FAILURE

38 Goto Przykład for (…) for (…) { … if (niepowodzenie) goto error; /*skocz do obsługi błedów*/ } … error: /*napraw sytuację lub wypisz komunikat*/

39 Funkcje wyjścia printf(„Ala ma kota \n”); puts(„Dave Matthews Band”); fputs(„1, 2, 3, … Próba mikrofonu!”, stdout); putchar(‘a’);putchar(‘\n’);

40 Funkcje wejścia scanf(„%f”, &liczba); scanf(„%99s”, tablica_znaków); gets(); - nie należy używać tej funkcji fgets(tablica_znaków, rozmiar_tablicy_znaków, stdin); getchar();

41 funkcje i procedury = moduły typ_funkcji nazwa_funkcji(typ_argumentu argument1, typ_argumentu argument2, …) void nazwa_procedury(typ_argumentu argument1, typ_argumentu argument2, …) Typy zmiennych przekazywane do funkcji muszą być zgodne z typami argumentów funkcji. Typ funkcji również powinien być określony. funkcja = moduł Plik, w którym została zapisana funkcja powinien być dołączony do programu głównego w preprocesorze #include „nazwa_funkcji.h”

42 funkcje i procedury = moduły Przykład int iloczyn(int x, int y){ int iloczyn_xy; iloczyn_xy = x*y; return iloczyn_xy; //return (x*y); } void pisz_komunikat(){ printf(„To jest komunikat\n”); } Wywołanie int z = iloczyn(5, 4); pisz_komunikat();

43 Tablice typ nazwa_tablicy[rozmiar]; Przykłady char tablica[20]; int tablica[3] = {1, 2, 3}; float macierz[10][10]; float macierz[2][2] = { {1.3, 5.6}, /* Pierwszy wiersz*/ {4.5, 8.9}, /* Drugi wiersz*/ }; 0123546789

44 Tablice #include #define ROZMIAR 3 int main(){ int tab[ROZMIAR] = {3,6,8}; int i; printf ("Druk tablicy tab:\n"); for (i=0; i<ROZMIAR; i++) { printf ("Element numer %d = %d\n", i, tab[i]); } return 0; }

45 wskaźniki Wskaźnik jest zmienną, która zawiera adres innej zmiennej. ……… p:c: Wskaźnik jest grupą komórek, które mogą pomieścić adres. Jeśli c jest obiektem typu char, a p jest wskaźnikiem, który wskazuje na c, to taką sytuację zilustrowano na obrazku. Jednoargumentowy operator & podaje adres obiektu p = &c; powyższa instrukcja przypisuje zmiennej p adres zmiennej c, zmienna p wskazuje na zmienną c

46 wskaźniki Jednoargumentowy operator * oznacza adresowanie pośrednie lub odwołanie pośrednie, a zastosowany do wskaźnika daje zawartość obiektu wskazywanego przez ten wskaźnik Przykład int x=1, y=2, z[10]; int *ip;//ip jest wskaźnikiem do obiektów typu int ip = &x;//teraz ip wskazuje na x y = *ip;//y ma teraz wartość 1 *ip = 0;//x ma teraz wartość 0 ip = &z[0];//teraz ip wskazuje na element z[0] Od wskaźnika wymaga się wskazywania obiektów określonego rodzaju. (Z jednym wyjątkiem – wskaźnik do void, czyli „wskaźnik do niczego”, może przechowywać wskaźnik do obiektów dowolnego typu, ale nie można go stosować do adresowania pośredniego.)

47 wskaźniki Jeżeli wskaźnik ip wskazuje na zmienną całkowitą x, to *ip może wystąpić wszędzie tam, gdzie może wystąpić x y = *ip + 5 *ip = *ip + 10 *ip += 1 ++*ip (*ip)++ iq = ip

48 wskaźniki źródło: http://pl.wikibooks.org/wiki/C/Wskaźniki http://pl.wikibooks.org/wiki/C/Wskaźniki Wskaźnik (ang. pointer) to specjalny rodzaj zmiennej, w której zapisany jest adres w pamięci komputera, tzn. wskaźnik wskazuje miejsce, gdzie zapisana jest jakaś informacja. Oczywiście nic nie stoi na przeszkodzie aby wskazywaną daną był inny wskaźnik do kolejnego miejsca w pamięci. Obrazowo możemy wyobrazić sobie pamięć komputera jako bibliotekę a zmienne jako książki. Zamiast brać książkę z półki samemu (analogicznie do korzystania wprost ze zwykłych zmiennych) możemy podać bibliotekarzowi wypisany rewers z numerem katalogowym książki a on znajdzie ją za nas. Analogia ta nie jest doskonała, ale pozwala wyobrazić sobie niektóre cechy wskaźników: kilka rewersów może dotyczyć tej samej książki, numer w rewersie możemy skreślić i użyć go do zamówienia innej książki, jeśli wpiszemy nieprawidłowy numer katalogowy to możemy dostać nie tą książkę, którą chcemy, albo też nie dostać nic. Warto też poznać w tym miejscu definicję adresu pamięci. Możemy powiedzieć, że adres to pewna liczba całkowita, jednoznacznie definiująca położenie pewnego obiektu (czyli np. znaku czy liczby) w pamięci komputera.

49 wskaźniki Wskaźnik a wskazujący na zmienną b. Zauważmy, że b przechowuje liczbę, podczas gdy a przechowuje adres b w pamięci (1462)

50 wskaźniki Przykład #include int main() { int liczba = 80; printf("Zmienna znajduje sie pod adresem: %p, i przechowuje wartosc: %d\n", &liczba, liczba); }

51 wskaźniki Przykład #include int main() { int liczba = 80; int *wskaznik = &liczba; printf("Wartosc zmiennej: %d; jej adres: %p.\n", liczba, &liczba); printf("Adres zapisany we wskazniku: %p, wskazywana wartosc: %d.\n", wskaznik, *wskaznik); *wskaznik = 42; printf("Wartosc zmiennej: %d, wartosc wskazywana przez wskaznik: %d\n", liczba, *wskaznik); liczba = 55; printf("Wartosc zmiennej: %d, wartosc wskazywana przez wskaznik: %d\n", liczba, *wskaznik); }

52 wskaźniki a tablice W języku C występuje ścisła zależność między wskaźnikami i tablicami. Każdą operację, którą można przeprowadzić za pomocą indeksowania tablicy, można również wykonać za pomocą wskaźników. Wersja wskaźnikowa będzie na ogół szybsza. Przykład: int a[10]; - a[0], a[1], a[2], …, a[9] a[i] oznacza i-ty element tablicy a int *pa; - wskaźnik do obiektów całkowitych pa = &a[0]; - przypisanie ustawi pa tak, aby wskazywał na 0 element tablicy, wskaźnik pa zawiera adres elementu a[0] x = *pa – przypisanie skopiuje zawartość a[0] do x Jeśli pa wskazuje na pewien element tablicy, to pa+1 wskazuje na element następny, pa+i odnosi się do i-tego elementu po pa, pa-i do i- tego elementu przed pa. Jeśli pa wskazuje na a[0], to *(pa+1) – odnosi się do zawartości a[1]; pa+i jest adresem a[i], a *(pa+i) jest zawartością a[i].

53 wskaźniki a funkcje Czasami zdarza się, że argumentem (lub argumentami) funkcji są wskaźniki. W przypadku "normalnych" zmiennych nasza funkcja działa tylko na lokalnych kopiach tychże argumentów, natomiast nie zmienia zmiennych, które zostały podane jako argument. Natomiast w przypadku wskaźnika, każda operacja na wartości wskazywanej powoduje zmianę wartości zmiennej zewnętrznej. Przykład: #include void func (int *zmienna) { *zmienna = 5; } int main() { int z=3; printf ("z=%d\n", z); // wypisze 3 func(&z); printf ("z=%d\n", z); // wypisze 5 } Funkcje w języku C nie tylko potrafią zwracać określoną wartość, lecz także zmieniać dane, podane im jako argumenty. Ten sposób przekazywania argumentów do funkcji jest nazywany przekazywaniem przez wskaźnik (w przeciwieństwie do normalnego przekazywania przez wartość).

54 Argumenty wywołania programu Działania każdego programu rozpoczyna się wywołaniem funkcji main z dwoma argumentami. Pierwszy, umownie nazywany argc (ang. argument count), jest liczbą argumentów, z jakimi program został wywołany; drugi argv (ang. argument vector), jest wskaźnikiem do tablicy zawierającej argumenty, każdy argument jako osobny tekst. Zgodnie z przyjętą konwencją: argv[0] – jest nazwą z jaką program został wywołany, licznik argc jest więc co najmniej równy 1. Wartość argc równa 1 oznacza, że w wierszu polecenia po nazwie programu nie było argumentów. Przykład:./nazwa_programu aaa bbb argc = 3, argv[0] = nazwa_programu, argv[1] = aaa, argv[2] = bbb Dodatkowo standard wymaga, aby element argv[argc] = argv[3] był wskaźnikiem pustym, czyli równym NULL.

55 Argumenty wywołania programu Przykład: #include main(int argc, char* argv[]) { int i; printf("argc = %d\n", argc); for (i = 0; i < argc; i++) printf("argv[%d] = \"%s\"\n", i, argv[i]); }

56 #include #include int main(int argc, char **argv) { /* Set defaults for all parameters: */ int a_value = 0; float b_value = 0.0; char* c_value = NULL; int d1_value = 0, d2_value = 0;int i; /* Start at i = 1 to skip the command name. */ for (i = 1; i < argc; i++) { /* Check for a switch (leading "-"). */ if (argv[i][0] == '-') { /* Use the next character to decide what to do. */ switch (argv[i][1]) { case 'a': a_value = atoi(argv[++i]); break; case 'b': b_value = atof(argv[++i]); break; case 'c': c_value = argv[++i]; break; case 'd': d1_value = atoi(argv[++i]); d2_value = atoi(argv[++i]); break; } printf("a = %d\n", a_value); printf("b = %f\n", b_value); if (c_value != NULL) printf("c = \"%s\"\n", c_value); printf("d1 = %d, d2 = %d\n", d1_value, d2_value); }

57 Preprocesor Utworzenie kodu przetworzonego przez preprocesor gcc test.c –E –o test.txt #include #include”plik_nagłówkowy.h” Linux #include "/usr/include/plik_nagłówkowy.h„ Windows #include „C:\\dev\includes\plik_nagłówkowy.h"

58 Preprocesor #define – pozwala zdefiniować stałą, funkcję lub słowo kluczowe, które zostanie zamienione w kodzie programu na odpowiednią wartość lub może zostać użyte w instrukcjach warunkowych dla preprocesora. #undef – odwołuje definicję #define NAZWA_STALEJ WARTOSC #define NAZWA_STALEJ Przykład: #define LICZBA 8 #define SUMA(a, b) (a+b)

59 Preprocesor #if – wprowadza warunek, który jeśli nie jest prawdziwy powoduje pominiecie kompilowania kodu, aż do napotkania jednej z poniższych instrukcji. #else – spowoduje skompilowanie kodu jeżeli warunek za #if jest nieprawdziwy, aż do napotkania któregoś z poniższych instrukcji. #elif – wprowadza nowy warunek, który będzie sprawdzony jeżeli poprzedni był nieprawdziwy. Stanowi połączenie instrukcji #if i #else. #endif – zamyka blok ostatniej instrukcji warunkowej.

60 Preprocesor #if INSTRUKCJE == 2 printf ("Podaj liczbe z przedziału 10 do 0\n"); #elif INSTRUKCJE == 1 printf ("Podaj liczbe: "); #else printf ("Podaj parametr: "); #endif scanf ("%d\n", &liczba);

61 Preprocesor #ifdef – spowoduje, że kompilator skompiluje poniższy kod tylko gdy została zdefiniowana odpowiednia stała. #ifndef – ma odwrotne działanie do #ifdef, a mianowicie brak definicji odpowiedniej stałej umożliwia kompilacje poniższego kodu. #else, #endif – maja identyczne zastosowanie jak wcześniej. #define INFO /*definicja stałej INFO*/ #ifdef INFO printf ("Twórcą tego programu jest Jan Kowalski\n"); #endif #ifndef INFO printf ("Twórcą tego programu jest znany programista\n"); #endif

62 Preprocesor #error – powoduje przerwanie kompilacji i wyświetlenie tekstu, który znajduje się za tą instrukcją. Przydatne gdy chcemy zabezpieczyć się przed zdefiniowaniem nieodpowiednich stałych. #if BLAD == 1 #error "Powazny bład kompilacji" #endif Jeżeli zdefiniujemy stałą BLAD z wartością 1, spowoduje to wyświetlenie w trakcie kompilacji komunikatu podobnego do poniższego: Fatal error program.c 6: Error directive: "Powazny blad kompilacji" in function main() *** 1 errors in Compile *** wraz z przerwaniem kompilacji.

63 Preprocesor #warning – wyświetla tekst, zawarty w cudzysłowach, jako ostrzeżenie. Jest często używany do sygnalizacji programiście, że dana część programu jest przestarzała lub może sprawiać problemy. #warning "To jest bardzo prosty program" Spowoduje to takie oto zachowanie kompilatora: test.c:3:2: warning: \#warning ‘‘To jest bardzo prosty program’’ Użycie dyrektywy #warning nie przerywa procesu kompilacji i służy tylko do wyświetlania komunikatów dla programisty w czasie kompilacji programu.

64 Preprocesor #line – powoduje wyzerowanie licznika linii kompilatora, który jest używany przy wyświetlaniu opisu błędów kompilacji. Pozwala to na szybkie znalezienie możliwej przyczyny błędu w rozbudowanym programie. printf ("Podaj wartosc funkcji"); #line printf ("W przedziale od 10 do 0\n); /* tutaj jest błąd - brak cudzysłowu zamykającego */ Jeżeli teraz nastąpi próba skompilowania tego kodu to kompilator poinformuje, że wystąpił błąd składni w linii 1, a nie np. 258.

65 Preprocesor #define MAKRO(arg1, arg2,...) (wyrażenie) W momencie wystąpienia MAKRA w tekście, preprocesor automatycznie zamieni makro na wyrażenie. Makra mogą być pewnego rodzaju alternatywami dla funkcji, ale powinno się ich używać tylko w specjalnych przypadkach. Ponieważ makro sprowadza się do prostego zastąpienia przez preprocesor wywołania makra przez jego tekst, jest bardzo podatne na trudne do zlokalizowania błędy (kompilator będzie podawał błędy w miejscach, w których nic nie widzimy, bo preprocesor wstawił tam tekst). Makra są szybsze (nie następuje wywołanie funkcji, które zawsze zajmuje trochę czasu), ale też mniej bezpieczne i elastyczne od funkcji.

66 Preprocesor #include #define KWADRAT(x) ((x)*(x)) int main (){ printf ("2 do kwadratu wynosi %d\n", KWADRAT(2)); return 0; }

67 Preprocesor #include #define wypisz(x) printf("%s=%i\n", #x, x); int main(){ int i=1; char a=5; wypisz(i); wypisz(a); return 0; }

68 Preprocesor #include #define abc(x) int zmienna ## x int main(){ abc(nasza); /* dzieki temu zadeklarujemy zmienna o nazwie zmiennanasza */ zmiennanasza = 2; return 0; }

69 Preprocesor Predefiniowane makra __DATE__ – data w momencie kompilacji __TIME__ – godzina w momencie kompilacji __FILE__ – łańcuch, który zawiera nazwę pliku, który aktualnie jest kompilowany przez kompilator __LINE__ – definiuje numer linijki __STDC__ – w kompilatorach zgodnych ze standardem ANSI lub nowszym makro to przyjmuje wartość 1 __STDC_VERSION__ – zależnie od poziomu zgodności kompilatora makro przyjmuje różne wartości: – jeżeli kompilator jest zgodny z ANSI (rok 1989) makro nie jest zdefiniowane, – jeżeli kompilator jest zgodny ze standardem z 1994 makro ma wartość 199409L, – jeżeli kompilator jest zgodny ze standardem z 1999 makro ma wartość 199901L. __func__ – makro jest zdefiniowane w standardzie C99, którego wartość to nazwa funkcji.

70 Preprocesor #include #if __STDC_VERSION__ >= 199901L /*Jeżeli mamy do dyspozycji identyfikator __func__ wykorzystajmy go. */ # define BUG(message) fprintf(stderr, "%s:%d: %s (w funkcji %s)\n", \ __FILE__, __LINE__, message, __func__) #else /* Jeżeli __func__ nie ma no to trudno. */ # define BUG(message) fprintf(stderr, "%s:%d: %s\n", \ __FILE__, __LINE__, message) #endif int main(void) { printf("Program ABC, data kompilacji: %s %s\n", __DATE__, __TIME__); BUG("Przykladowy komunikat bledu"); return 0; }

71 Operacje na plikach Istnieją dwie metody obsługi czytania i pisania do plików: wysokopoziomowa i niskopoziomowa. Nazwy funkcji z pierwszej grupy zaczynają się od litery "f" (np. fopen(), fread(), fclose()), a identyfikatorem pliku jest wskaźnik na strukturę typu FILE. Owa struktura to pewna grupa zmiennych, która przechowuje dane o danym pliku - jak na przykład aktualną pozycję w nim. Szczegółami nie należy się przejmować, funkcje biblioteki standardowej same zajmują się wykorzystaniem struktury FILE, programista może więc zapomnieć, czym tak naprawdę jest struktura FILE i traktować taką zmienną jako "uchwyt", identyfikator pliku. Druga grupa to funkcje typu read(), open(), write() i close(). Podstawowym identyfikatorem pliku jest liczba całkowita, która jednoznacznie identyfikuje dany plik w systemie operacyjnym. Liczba ta w systemach typu UNIX jest nazywana deskryptorem pliku.

72 Operacje na plikach Należy pamiętać, że nie wolno nam używać funkcji z obu tych grup jednocześnie w stosunku do jednego, otwartego pliku, tzn. nie można najpierw otworzyć pliku za pomocą fopen(), a następnie odczytywać danych z tego samego pliku za pomocą read(). Czym różnią się oba podejścia do obsługi plików? Otóż metoda wysokopoziomowa ma swój własny bufor, w którym znajdują się dane po odczytaniu z dysku a przed wysłaniem ich do programu użytkownika. W przypadku funkcji niskopoziomowych dane kopiowane są bezpośrednio z pliku do pamięci programu. W praktyce używanie funkcji wysokopoziomowych jest prostsze a przy czytaniu danych małymi porcjami również często szybsze.

73 Operacje na plikach Do zapisu danych do pliku można użyć funkcji fprintf, która wygląda bardzo podobnie do printf - jedyną różnicą jest to, że w fprintf musimy jako pierwszy argument podać identyfikator pliku. Nie jest to przypadek - obie funkcje tak naprawdę robią tak samo. Używana do wczytywania danych z klawiatury funkcja scanf też ma swój odpowiednik wśród funkcji operujących na plikach - jak nietrudno zgadnąć, nosi ona nazwę fscanf. W rzeczywistości język C traktuje tak samo klawiaturę i plik - są to źródła danych, podobnie jak ekran i plik, do których można dane kierować. Nie da się ukryć, że między klawiaturą i plikiem na dysku zachodzą podstawowe różnice i dostęp do nich odbywa się inaczej - jednak funkcje języka C pozwalają nam o tym zapomnieć i same zajmują się szczegółami technicznymi. Z punktu widzenia programisty, urządzenia te sprowadzają się do nadanego im identyfikatora. Uogólnione pliki nazywa się w C strumieniami. Każdy program w momencie uruchomienia "otrzymuje" od razu trzy otwarte strumienie (aby z nich korzystać należy dołączyć plik nagłówkowy stdio.h): stdin (wejście) stdout (wyjście) stderr (wyjście błędów)

74 Operacje na plikach Przykład: #include int main() { FILE *fp; /* używamy metody wysokopoziomowej - musimy mieć zatem identyfikator pliku, uwaga na gwiazdkę! */ char tekst[] = "Hello world"; if ((fp=fopen("test.txt", "w"))==NULL) { printf ("Nie mogę otworzyć pliku test.txt do zapisu!\n"); exit(1); } fprintf (fp, "%s", tekst); /* zapisz nasz łańcuch w pliku */ fclose (fp); /* zamknij plik */ return 0; }

75 Operacje na plikach FILE *fopen(const char *filename, const char *mode); Funkcja fopen() otwiera plik, którego nazwa podana jest w pierwszym argumencie. Drugim jest łańcuch znaków zwierający litery oznaczające sposób otwarcia pliku: "r" - otwiera plik do czytania "w" - otwiera plik do nadpisywania (zamazuje starą treść) "a" - otwiera plik do dopisywania (jeśli plik nie istnieje, to jest tworzony) "t" - otwiera plik w trybie tekstowym "b" - otwiera plik w trybie binarnym Litery można ze sobą łączyć, np. "rwb" albo "wt".

76 Operacje na plikach #include int main(int argc, char *argv[]) { FILE *fp; int c; if (argc < 2) { fprintf (stderr, "Uzycie: %s nazwa_pliku\n", argv[0]); exit (-1); } fp = fopen (argv[1], "w"); if (!fp) { fprintf (stderr, "Nie moge otworzyc pliku %s\n", argv[1]); exit (-1); } printf("Wcisnij Ctrl+D lub Ctrl+Z aby zakonczyc\n"); while ( (c = fgetc(stdin)) != EOF) { fputc(c, stdout); fputc(c, fp); } fclose(fp); return 0; }

77 Operacje na plikach Dzięki standardowym funkcjom języka C możemy m.in. określić długość pliku. Do tego celu służą funkcje fsetpos, fgetpos oraz fseek. Przy każdym odczycie/zapisie z/do pliku wskaźnik niejako "przesuwa" się o liczbę przeczytanych/zapisanych bajtów. Możemy jednak ustawić wskaźnik w dowolnie wybranym miejscu. Do tego właśnie służą wyżej wymienione funkcje. Aby odczytać rozmiar pliku powinniśmy ustawić nasz wskaźnik na koniec pliku, po czym odczytać ile bajtów od początku pliku się znajdujemy. Procedura ta działa wyjątkowo prosto i skutecznie. Użyjemy do tego tylko dwóch funkcji: fseek oraz fgetpos. Pierwsza służy do ustawiania wskaźnika na odpowiedniej pozycji w pliku, a druga do odczytywania na którym bajcie pliku znajduje się wskaźnik.

78 Operacje na plikach #include int main (int argc, char **argv) { FILE *fp = NULL; fpos_t dlugosc; if (argc != 2) { printf ("Użycie: %s \n", argv[0]); return 1; } if ((fp=fopen(argv[1], "rb"))==NULL) { printf ("Błąd otwarcia pliku: %s!\n", argv[1]); return 1; } fseek (fp, 0, SEEK_END); /* ustawiamy wskaźnik na koniec pliku fgetpos (fp, &dlugosc); printf ("Rozmiar pliku: %d\n", dlugosc); fclose (fp); return 0; }

79 Tworzenie plików graficznych #include int main() { const int dimx = 800; const int dimy = 800; int i, j; FILE * fp = fopen("first.ppm", "wb"); /* b - tryb binarny */ fprintf(fp, "P6\n%d %d\n255\n", dimx, dimy); for(j=0; j<dimy; ++j){ for(i=0; i<dimx; ++i){ static unsigned char color[3]; color[0]=i % 255; /* red */ color[1]=j % 255; /* green */ color[2]=(i*j) % 255; /* blue */ fwrite(color,1,3,fp); } fclose(fp); return 0;}

80 Tworzenie plików graficznych PPM (ang. portable pixmap) - zawiera obraz kolorowy (RGB) - maksymalnie 24 bity na piksel w trybie binarnym, i do 48 bitów w trybie tekstowym PBM (ang. portable bitmap) - zawiera obraz czarno-biały - 1 bit na piksel PGM (ang. portable graymap) - zawiera obraz w odcieniach szarości - 8 bitów na piksel Formaty zapisu grafiki rastrowej, używane głównie do wymiany grafiki między różnymi narzędziami graficznymi. Wszystkie trzy formaty określane są wspólnie jako PNM - (ang. portable anymap). P1 # komentarz 6 9 0 0 0 0 1 0 1 0 0 0 1 0 0 1 1 1 0 0 0 0 0

81 Napisy Napisy w języku C mogą być przyczyną wielu trudnych do wykrycia błędów w programach. Warto dobrze zrozumieć, jak należy operować na łańcuchach znaków i zachować szczególną ostrożność w tych miejscach, gdzie napisów używamy. Napis jest zapisywany w kodzie programu jako ciąg znaków zawarty pomiędzy dwoma cudzysłowami. printf ("Napis w jezyku C"); W pamięci taki łańcuch jest następującym po sobie ciągiem znaków (char), który kończy się znakiem “null” (czyli po prostu liczba zero), zapisywanym jako ’\0’.

82 Napisy char *tekst = "Jakiś tam tekst"; printf("%c\n", "przyklad"[0]); /*wypisze p - znaki w napisach są numerowane od zera */ printf("%c\n", tekst[2]); /* wypisze k */ Ponieważ napis w pamięci kończy się zerem umieszczonym tuż za jego zawartością, odwołanie się do znaku o indeksie równym długości napisu zwróci zero: printf("%d", "test"[4]); /* wypisze 0 */ Napisy możemy wczytywać z klawiatury i wypisywać na ekran przy pomocy dobrze znanych funkcji scanf, printf i pokrewnych. Formatem używanym dla napisów jest %s. printf("%s", tekst);

83 Napisy char *tekst = "Jakiś tam tekst"; /*Umieszcza napis w obszarze danych programu i przypisuje adres */ char tekst[] = "Jakis tam tekst"; /* Umieszcza napis w tablicy */ char tekst[] = {’J’,’a’,’k’,’i’,’s’,’ ’,’t’,’a’,’m’,’ ’, ’t’,’e’,’k’,’s’,’t’,’\0’}; /* Tekst to taka tablica jak każda inna */ char tekst[80] = "Ten tekst musi byc krótszy niz 80 znaków";

84 Napisy Porównywanie łańcuchów Napisy to tak naprawdę wskaźniki. Tak więc używając zwykłego operatora porównania ==, otrzymamy wynik porównania adresów, a nie tekstów. Do porównywania dwóch ciągów znaków należy użyć funkcji strcmp zadeklarowanej w pliku nagłówkowym string.h. Jako argument przyjmuje ona dwa napisy i zwraca wartość ujemną jeżeli napis pierwszy jest mniejszy od drugiego, 0 jeżeli napisy są równe lub wartość dodatnią jeżeli napis pierwszy jest większy od drugiego.

85 Napisy #include int main(void) { char str1[100], str2[100]; int cmp; puts("Podaj dwa ciagi znakow: "); fgets(str1, sizeof str1, stdin); fgets(str2, sizeof str2, stdin); cmp = strcmp(str1, str2); if (cmp<0) { puts("Pierwszy napis jest mniejszy."); } else if (cmp>0) { puts("Pierwszy napis jest wiekszy."); } else { puts("Napisy sa takie same."); } return 0;}

86 Napisy #include int main(void) { char str[100]; int cmp; fputs("Podaj ciag znakow: ", stdout); fgets(str, sizeof str, stdin); if (!strncmp(str, "foo", 3)) { puts("Podany ciag zaczyna sie od ’foo’."); } return 0; }

87 Napisy Kopiowanie napisów Do kopiowania ciągów znaków służy funkcja strcpy, która kopiuje drugi napis w miejsce pierwszego. Musimy pamiętać, by w pierwszym łańcuchu było wystarcząjaco dużo miejsca. char napis[100]; strcpy(napis, "Ala ma kota."); Znacznie bezpieczniej jest używać funkcji strncpy, która kopiuje co najwyżej tyle bajtów ile podano jako trzeci parametr. Uwaga! Jeżeli drugi napis jest za długi funkcja nie kopiuje znaku NULL na koniec pierwszego napisu, dlatego zawsze trzeba to robić ręcznie: char napis[100]; strncpy(napis, "Ala ma kota.", sizeof napis - 1); napis[sizeof napis - 1] = 0;

88 Napisy Łączenie napisów Do łączenia napisów służy funkcja strcat, która kopiuje drugi napis do pierwszego. Ponownie jak w przypadku strcpy musimy zagwarantować, by w pierwszym łańcuchu było wystarczająco dużo miejsca. #include int main(void) { char napis1[80] = "hello "; char *napis2 = "world"; strcat(napis1, napis2); puts(napis1); return 0; }

89 Napisy Łączenie napisów I ponownie jak w przypadku strncpy istnieje funkcja strncat, która skopiuje co najwyżej tyle bajtów ile podano jako trzeci argument i dodatkowo dopisze znak NULL. #include int main(void) { char napis1[80] = "hello "; char *napis2 = "world"; strncat(napis1, napis2, sizeof napis1 - 1); puts(napis1); return 0; }

90 Napisy UWAGA!!! Należy bardzo uważać na łączenie i kopiowanie napisów. Kompilator języka C nie wykryje nadpisania pamięci za zmienną łańcuchową i nie przydzieli dodatkowego obszaru pamięci. Może się zdarzyć, że program pomimo nadpisywania pamięci za łańcuchem będzie nadal działał, co bardzo utrudni wykrywanie tego typu błędów!

91 Napisy - dynamiczne alokowanie pamięci Zmienne programu przechowywane są na tzw. stosie i powstają, gdy program wchodzi do bloku, w którym zmienne są zadeklarowane. Zwalniane są w momencie, kiedy program opuszcza ten blok. Jeśli deklarujemy tak tablice, to ich rozmiar musi być znany w momencie kompilacji, żeby kompilator wygenerował kod rezerwujący odpowiednią ilość pamięci. Dostępny jest też drugi rodzaj rezerwacji (czyli alokacji) pamięci. Jest to alokacja na stercie. Sterta to obszar pamięci wspólny dla całego programu. Przechowywane są w nim zmienne, których czas życia nie jest związany z poszczególnymi blokami. Trzeba samemu rezerwować dla nich miejsce oraz to miejsce zwalniać, ale dzięki temu można to zrobić w dowolnym momencie działania programu.

92 Napisy - dynamiczne alokowanie pamięci calloc() - funkcja przydziela pamięć dla nmeb elementów o rozmiarze size każdy i zeruje przydzieloną pamięć. malloc() - funkcja przydziela pamięć o wielkości size bajtów. realloc() - funkcja zmienia rozmiar przydzielonego wcześniej bloku pamięci wskazywanego przez ptr do size bajtów. Pierwsze n bajtów bloku nie ulegnie zmianie gdzie n jest minimum z rozmiaru starego bloku i size. Jeżeli ptr jest równy zero (tj. NULL), funkcja zachowuje się tak samo jako malloc. free() - funkcja zwalnia blok pamięci wskazywany przez ptr wcześniej przydzielony przez jedną z funkcji malloc, calloc lub realloc. Jeżeli ptr ma wartość NULL funkcja nie robi nic. nmeb - liczba elementów, dla których ma być przydzielona pamięć, size - rozmiar (w bajtach) pamięci do zarezerwowania bądź rozmiar pojedynczego elementu, ptr - wskaźnik zwrócony przez poprzednie wywołanie jednej z funkcji lub NULL void *calloc(size_t nmeb, size_t size); void *malloc(size_t size); void free(void *ptr); void *realloc(void *ptr, size_t size);

93 Napisy - dynamiczne alokowanie pamięci Jeżeli przydzielanie pamięci się powiodło, funkcje calloc, malloc i realloc zwracają wskaźnik do nowo przydzielonego bloku pamięci. W przypadku funkcji realloc może to być wartość inna niż ptr. Jeśli jako size, nmeb podano zero, zwracany jest albo wskaźnik NULL albo prawidłowy wskaźnik, który można podać do funkcji free (zauważmy, że NULL jest też prawidłowym argumentem free). Jeśli działanie funkcji nie powiedzie się, zwracany jest NULL i odpowiedni kod błędu jest wpisywany do zmiennej errno. Dzieje się tak zazwyczaj, gdy nie ma wystarczająco dużo miejsca w pamięci.

94 Napisy - dynamiczne alokowanie pamięci int rozmiar; float *tablica; rozmiar = 3; tablica = malloc(rozmiar * sizeof(*tablica)); tablica[0] = 0.1;

95 Napisy – przepełnienie bufora #include int main(int argc, char **argv) { char haslo_poprawne = 0; char haslo[16]; if (argc!=2) { fprintf(stderr, "uzycie: %s haslo", argv[0]); return EXIT_FAILURE;} strcpy(haslo, argv[1]); /*tutaj następuje przepełnienie bufora */ if (!strcmp(haslo, "poprawne")) { haslo_poprawne = 1;} if (!haslo_poprawne) { fputs("Podales bledne haslo.\n", stderr); return EXIT_FAILURE;} puts("Witaj, wprowadziles poprawne haslo."); return EXIT_SUCCESS;}

96 Napisy – konwersje atol, strtol — zamienia łańcuch na liczbę całkowitą typu long atoi — zamienia łańcuch na liczbę całkowitą typu int atoll, strtoll — zamienia łańcuch na liczbę całkowitą typu long long (64 bity) atof, strtod — przekształca łańcuch na liczbę typu double islower, isupper – sprawdza wielkość znaku tolower, toupper – zamienia znak na mały i odwrotnie

97 typedef typedef stara_nazwa nowa_nazwa; typedef int mojInt; typedef int* WskNaInt;

98 Typ wyliczeniowy Służy do tworzenia zmiennych, które przechowują tylko pewne z góry ustalone wartości. Przechowywane wartości typu wyliczeniowego to liczby całkowite. enum Nazwa {WARTOSC_1, WARTOSC_2, WARTOSC_N }; Przykład enum Kierunek {W_GORE, W_DOL, W_LEWO, W_PRAWO}; enum Kierunek k = W_GORE; switch(k){ case W_GORE: printf("w górę\n"); break; case W_DOL: printf("w dół\n"); break; default: printf("gdzieś w bok\n"); break; }

99 Struktury danych Struktura danych jest to najogólniej mówiąc sposób reprezentacji oraz organizacji informacji. Można powiedzieć, że najprostszą strukturą danych w języku ANSI C (jak i w wielu innych) jest zmienna wybranego przez programistę typu. Dla przykładu, zmienna o nazwie x typu float może nam posłużyć do przechowania rozwiązania równania. Jednak gdybyśmy chcieli np. stworzyć pewien ciąg, który zawiera np. 10 elementów musielibyśmy stworzyć 10 zmiennych – a1, a2,..., a10. Zamiast tego możemy skorzystać z innej struktury, czyli z tablicy np. int tab[10] co da 10 elementową tablicę przechowującą liczby całkowite.

100 Struktury danych Język ANSI C umożliwia nam tworzenie własnych struktur. Taka struktura jest typem zawierającym w sobie zmienne dowolnego typu (tzw. składowe) posiadające swoje własne nazwy. Aby odwołać się do składowej, należy określić jej nazwę, a nie pozycję. Struktura umożliwia zapakowanie powiązanych ze sobą logicznie danych w jednym obszarze pamięci, który może być przesyłany jako jeden argument. struct nazwa_struktury {.... // <- ciało struktury };

101 Struktury danych Definicja struktury: struct magazyn{ int numer_kat; char nazwa[DLUGOSC_NAZW+1]; int liczba_szt; } towar1, towar2 = {111, „Kalosze”, 3} ; Gdzie towar1 i towar2 to nazwy zmiennych typu strukturalnego. Nazewnictwo, ilość i typ składowych definiuje programista według własnego uznania. Zmienną posiadającą strukturę tworzy się podając jako jej typ nazwę struktury. struct magazyn towar; struct magazyn rzecz = {21, „marynarka”, 3455};

102 Struktury danych Inicjalizator desygnowany (C99) {. numer_kat = 111,. nazwa = „Kalosze”,. liczba_szt = 3} Desygnatem wartości w inicjalizatorze jest etykieta składająca się z kropki i nazwy składowej. Zalety inicjalizatorów desygnowanych: są czytelniejsze i łatwiej sprawdzić ich poprawność, bo jawnie pokazują powiązanie składowych struktury i wartości inicjalizatora brak konieczności trzymania się kolejności składowych z deklaracji struktury {. liczba_szt = 3,. nazwa = „Kalosze”,. numer_kat = 111} nie wszystkie wartości wymienione w inicjalizatorze desygnowanym muszą być prefiksowane desygnatem {. numer_kat = 111, „Kalosze”,. liczba_szt = 3} wszelkie składowe pominięte w inicjalizatorze zostaną wyzerowane

103 Struktury danych Żadna nazwa zadeklarowana w zasięgu struktury nie będzie kolidować z podobną nazwą występującą w programie poza strukturą. Każda struktura posiada własną przestrzeń nazw dla swoich składowych. Dostęp do poszczególnych pól uzyskuje się przy pomocy operatora. towar1.numer_kat = 60; /*przypisanie liczby do pola*/ towar1. nazwa = „jakaś nazwa”; /*przypisanie stringa*/ towar1. liczba_szt = 100; lub za pomocą wskaźnika wsk-> liczba_szt ; wsk->nazwa;

104 #include /* deklarujemy prosta strukture */ struct point { float x; float y; }; int main(){ /* zmienna typu strukturalnego: */ struct point p; /* odwolujemy sie do pierwszego pola struktury: */ scanf("%f", &p.x); /*... i do drugiego pola: */ p.y=4.5; /* na koniec wypiszmy zawartosc struktury point: */ printf("x=%f\n",p.x); printf("y=%f\n",p.y); return 0; }

105 #include int main(){ struct Adres{ char Ulica[30]; int Nr_Domu; int Nr_Mieszk; }; struct SCzlowiek{ char Imie[20]; int Wiek; struct Adres Mieszkanie; }; struct SCzlowiek LISTA[50]; LISTA[1].Wiek=34; LISTA[1].Mieszkanie.Nr_Domu=29; printf("%d %d\n", LISTA[1].Wiek, LISTA[1].Mieszkanie.Nr_Domu); return 0; }

106 Struktury danych Właściwości struktur – dozwolone operacje: przypisanie jednej strukturze innej struktury w całości skopiowanie struktury w całości na inną strukturę. pobranie adresu struktury za pomocą operatora & odwołanie do składowych struktury Przez kopiowanie i przypisanie rozumie się także przesyłanie argumentów funkcjom i zwracanie przez funkcje wartości. Struktur nie można porównywać ze sobą!!!

107 Struktury danych typedef int moj_int; struct magazyn{ int numer_kat; char nazwa[100]; int liczba_szt; }; typedef struct magazyn SMagazyn; typedef struct{ int numer_kat; char nazwa[100]; int liczba_szt; } SMagazyn; SMagazyn towar1, towar2, lista_towarow[100];

108 Struktury danych void pokaz_towar(SMagazyn towar1){ printf(„Numer katalogowy: %d\n”, towar1.numer_kat); printf(„Nazwa: %s\n”, towar1.nazwa); printf(„Stan magazynowy: %d\n”, towar1.liczba_szt); } pokaz_towar(jakis_towar); SMagazyn tworz_towar(int id, const char *name, int szt) { SMagazyn towar1; towar1.numer_kat = id; strcpy(towar1.nazwa, name); towar1.liczba_szt = szt; return towar1; } jakis_towar = tworz_towar(1123, „plyta_CD”, 44);

109 Struktury danych typedef struct { int x; int *y; } struktura; // -> operator wyłuskania z dostępem do składowej int main() { struktura *wsk_strukt; struktura strukt; int a = 10, b = 20; strukt.x = a; strukt.y = &b; wsk_strukt = &strukt; printf("Wartosc pola x: %d\n", strukt.x); printf("Wartosc pola x (wskaznik): %d\n", wsk_strukt->x ); //(*wsk_strukt).x printf("Wartosc wskazywana przez pole *y: %d\n", *(strukt.y) ); printf("Wartosc wskazywana przez pole *y w przypadku odwolania sie przez wskaznik do struktury: %d\n", *(wsk_strukt->y) ); // *((*wsk_strukt).y) return 0; }

110 Unie Unie to kolejny sposób prezentacji danych w pamięci. Na pierwszy rzut oka wyglądają bardzo podobnie do struktur union Nazwa{ typ1 nazwa1; typ2 nazwa2; /*... */ }; Przykład: union LiczbaLubZnak{ int calkowita; char znak; double rzeczywista; };

111 Unie Pola w unii nakładają się na siebie w ten sposób, że w danej chwili można w niej przechowywać wartość tylko jednego typu. Unia zajmuje w pamięci tyle miejsca, ile zajmuje największa z jej składowych. W powyższym przypadku unia będzie miała prawdopodobnie rozmiar typu double czyli często 64 bity, a liczba całkowita i znak będą wskazywały odpowiednio na pierwsze cztery bajty lub na pierwszy bajt unii (choć nie musi tak być zawsze). Dlaczego tak? Taka forma często przydaje się np. do konwersji pomiędzy różnymi typami danych. Możemy dzięki unii podzielić zmienną 32-bitową na cztery składowe zmienne o długości 8 bitów każda. Do konkretnych wartości pól unii odwołujemy się przy pomocy operatora wyboru składnika – kropki. Zazwyczaj użycie unii ma na celu zmniejszenie zapotrzebowania na pamięć, gdy naraz będzie wykorzystywane tylko jedno pole i jest często łączone z użyciem struktur.

112 #include /*Deklaracja zmiennej przechowujacej liczbe calkowita lub rzeczywista (ale nie obydwie)*/ union value { long int i_value; /* Liczba rzeczywista */ float f_value; /* Liczba zmiennoprzecinkowa */ } data; int i; /* Losowa liczba calkowita */ float f; /* Losowa liczba zmiennoprzecinkowa */ int main() { data.f_value = 5.0; data.i_value = 3; /* nadpisana wartosc data.f_value */ printf("%li \t %f \n", data.i_value, data.f_value); i = data.i_value; /* prawidlowo */ f = data.f_value; /* nieprawidlowo, wywola nieprzewidywalne wyniki */ printf("%li \t %f \n", i, f); data.f_value = 5.5; /* przypisanie jakiejs wartosci f-value/i-value */ f = data.f_value; i = data.i_value; /* nieprawidlowo, wywola nieprzewidywalne wyniki */ printf("%li \t %f \n", i, f); return(0);}

113 Pola bitowe Struktury mają pewne dodatkowe możliwości w stosunku do zmiennych. Mowa tutaj o rozmiarze elementu struktury. W przeciwieństwie do zmiennej może on mieć nawet 1 bit!. Aby móc zdefiniować taką zmienną musimy użyć tzw. pola bitowego. struct moja{ unsigned inta1:4, /* 4 bity * (0-15)/ a2:8, /* 8 bitów (często 1 bajt) * (0-255)/ a3:1, /* 1 bit * 0 lub 1/ a4:3; /* 3 bity * (0-7)/ }; Wszystkie pola tej struktury mają w sumie rozmiar 16 bitów, jednak możemy odwoływać się do nich w taki sam sposób, jak do innych elementów struktury. W ten sposób efektywniej wykorzystujemy pamięć, jednak istnieją pewne zjawiska, których musimy być świadomi przy stosowaniu pól bitowych. Pola bitowe znalazły zastosowanie głównie w implementacjach protokołów sieciowych.

114 #include int main() { struct info { int Plec : 1; unsigned Wiek : 8; unsigned Dzieci : 4; unsigned Ile_sam : 3; } Kobieta; int bufor; system("clear"); Kobieta.Plec = 0; printf("\n Ile ma lat ? : "); scanf("%d", &bufor); Kobieta.Wiek = bufor; printf("\n Ile ma samochodow ? : "); scanf("%d", &bufor); Kobieta.Ile_sam = bufor; printf("\n Ile dzieci ? : "); scanf("%d", &bufor); Kobieta.Dzieci = bufor; printf("\n\n"); printf(„Kobieta ma %d samochodow", Kobieta.Ile_sam); printf("\nPlec: Dzieci: Wiek (lat): \n\n"); printf("%d\t%d\t%d\n", Kobieta.Plec, Kobieta.Dzieci, Kobieta.Wiek); return 0; }

115 Jak budować proste biblioteki? Biblioteka jest zbiorem funkcji, które zostały wydzielone po to, aby można było z nich korzystać w wielu programach. Ułatwia to programowanie - nie trzeba np. tworzyć funkcji printf. Każda biblioteka posiada swoje pliki nagłówkowe, które zawierają deklaracje funkcji bibliotecznych. Często zamieszczone są w nich komentarze opisujące sposób użycia funkcji. Każda biblioteka składa się co najmniej z dwóch części: pliku nagłówkowego, w którym znajdują się deklaracje funkcji (plik z rozszerzeniem.h) pliku źródłowego, zawierającego definicje zadeklarowanych funkcji (plik z rozszerzeniem.c)

116 Jak budować proste biblioteki? Budowa prostego pliku nagłówkowego: #ifndef PLIK_H #define PLIK_H /* tutaj umieszcza się deklaracje funkcji */ #endif Często zdarza się, że w programie korzysta się z plików nagłówkowych, które dołączają się wzajemnie. Oznacza to, że w kodzie programu kilka razy może pojawić się zawartość tego samego pliku nagłówkowego. Instrukcje #ifndef i #define zapobiegają więc kilkakrotnej kompilacji tego samego kodu. W plikach nagłówkowych często umieszcza się też definicje typów, z których korzysta biblioteka albo np. makr.

117 Jak budować proste biblioteki? Plik nagłówkowy TEKST_NA_EKRAN_H #ifndef TEKST_NA_EKRAN_H #define TEKST_NA_EKRAN_H void tekst_na_ekran(void); #endif Należy pamiętać, o podaniu void w liście argumentów funkcji nie przyjmujących argumentów. O ile przy definicji funkcji (procedury) nie trzeba tego robić (np. w przypadku funkcji main), o tyle w prototypie brak słówka void oznacza, że w prototypie tym nie ma informacji na temat tego jakie argumenty funkcja przyjmuje. Plik nagłówkowy zapisujemy jako “tekst_na_ekran.h”.

118 Jak budować proste biblioteki? Plik źródłowy, zawierający definicje zadeklarowanych funkcji: #include”tekst_na_ekran.h” #include void tekst_na_ekran(void){ printf(„www.mif.pg.gda.pl\n”); } Ważne jest dołączenie na początku pliku nagłówkowego, ponieważ zawiera on deklaracje funkcji. Jeśli popełniony zostanie błąd i deklaracja nie zgadza się z definicją, kompilator od razu o tym powiadomi. Oprócz tego plik nagłówkowy może również zawierać definicje istotnych typów lub makr.

119 Jak budować proste biblioteki? Bibliotekę należy zapisać jako plik “tekst_na_ekran.c” i skompilować w następujący sposób: gcc tekst_na_ekran.c -c -o tekst_na_ekran.o Rozszerzenie “.o” jest domyślnym rozszerzeniem dla bibliotek statycznych, typowych bibliotek łączonych z resztą programu na etapie kompilacji. Program main.c korzystający z biblioteki tekst_na_ekran.h: #include”tekst_na_ekran.h” int main(){ tekst_na_ekran(); return 0; }

120 Jak budować proste biblioteki? Kompilacja: gcc main.c tekst_na_ekran.o -o main Należy zauważyć, że aby uzyskać plik wykonywalny (main), kompilatorowi podaje się zarówno pliki z kodem źródłowym (main.c), jak i pliki ze skompilowanymi bibliotekami (teskt_na_ekran.o). Jeśli nie podane zostaną pliki z bibliotekami, program main.c co prawda skompiluje się, ale błąd zostanie zgłoszony przez linker, który jest częścią kompilatora odpowiedzialną za wstawienie w miejsca wywołań funkcji ich adresów, a takiego adresu linker nie będzie mógł znaleźć.

121 Jak budować proste biblioteki? Extern informuje kompilator, że dana funkcja lub zmienna istnieje, ale w innym miejscu, i zostanie dołączona do kodu programu w czasie łączenia go z biblioteką. Extern przydaje się, gdy zmienna lub funkcja jest zadeklarowana w bibliotece, ale nie jest udostępniona na zewnątrz (nie pojawia się w pliku nagłówkowym). /* biblioteka.h */ extern char zmienna_dzielona[]; /* biblioteka.c */ #include "biblioteka.h" char zmienna_dzielona[] = "Zawartosc"; /* main.c */ #include #include "biblioteka.h" int main(){ printf("%s\n", zmienna_dzielona); } Bez extern, kompilator (nie linker) zaprotestowałby, że nie zna zmiennej zmienna_dzielona. Próba dopisania deklaracji char zmienna_dzielona; stworzyłaby nową zmienną i utracilibyśmy dostęp do interesującej nas zawartości.

122 Jak budować proste biblioteki? Odwrotne działanie ma słowo kluczowe static użyte w tym kontekście (użyte wewnątrz bloku tworzy zmienną statyczną). Może ono odnosić się zarówno do zmiennych jak i do funkcji globalnych. Powoduje, że dana zmienna lub funkcja jest niedostępna na zewnątrz biblioteki. Choć tak naprawdę całe “ukrycie” funkcji polega na zmianie niektórych danych w pliku z kodem binarnym danej biblioteki (pliku.o), przez co linker powoduje wygenerowanie komunikatu o błędzie w czasie łączenia biblioteki z programem. Możemy dzięki temu ukryć np. funkcje, które używane są przez samą bibliotekę, aby nie dało się ich wykorzystać przez extern.

123 Ciekawe opcje kompilatora E – powoduje wygenerowanie kodu programu ze zmianami, wprowadzonymi przez preprocesor. S – zamiana kodu w języku C na kod asemblera (komenda: gcc -S plik.c spowoduje utworzenie pliku o nazwie plik.s, w którym znajdzie się kod asemblera) c – kompilacja bez łączenia z bibliotekami I katalog – ustawienie domyślnego katalogu z plikami nagłówkowymi na katalog l biblioteka – wymusza łączenie programu z podaną biblioteką (np. -lmath)

124 Budowa prostego pliku Makefile Najważniejszym elementem pliku Makefile są zależności oraz reguły przetwarzania. Zależności polegają na tym, że np. jeśli program ma być zbudowany z 4 plików, to najpierw należy skompilować każdy z tych 4 plików, a dopiero później połączyć je w jeden cały program. Zatem zależności określają kolejność wykonywanych czynności. Natomiast reguły określają jak skompilować dany plik.

125 Budowa prostego pliku Makefile Zależności tworzy się tak: co: od_czego reguły... Dzięki temu program make zna kolejność wykonywanych działań oraz czynności, jakie ma wykonać. Aby zbudować “co” należy wykonać polecenie: make co. Pierwsza reguła w pliku Makefile jest regułą domyślną. Jeśli wydamy polecenie make bez parametrów, zostanie zbudowana właśnie reguła domyślna. Tak więc dobrze jest jako pierwszą regułę wstawić regułę budującą końcowy plik wykonywalny; zwyczajowo regułę tą nazywa się all. Należy pamiętać, by sekcji “co” nie wcinać, natomiast “reguły” wcinać dwoma spacjami. Część “od czego” może być pusta.

126 Budowa prostego pliku Makefile Plik Makefile umożliwia definiowanie pewnych zmiennych. Nie trzeba się troszczyć o typ zmiennej, wystarczy napisać: nazwa_zmiennej = wartosc W ten sposób możemy zadeklarować dowolnie dużo zmiennych. Zmienne mogą być różne – nazwa kompilatora, jego parametry i wiele innych. Zmiennej używamy w następujący sposób: $(nazwa zmiennej) Komentarze w pliku Makefile tworzymy zaczynając linie od znaku hash (#).

127 Budowa prostego pliku Makefile # Plik makefile - wpisz ’make all’ aby skompilować cały program CC = gcc all: pierwszy.o drugi.o trzeci.o czwarty.o $(CC) pierwszy.o drugi.o trzeci.o czwarty.o -o test pierwszy.o: pierwszy.c $(CC) pierwszy.c -c -o pierwszy.o drugi.o: drugi.c $(CC) drugi.c -c -o drugi.o trzeci.o: trzeci.c $(CC) trzeci.c -c -o trzeci.o czwarty.o: czwarty.c $(CC) czwarty.c -c -o czwarty.o clean: rm -f *.o test

128 Łączenie C z innymi językami Język C może być z łatwością łączony z innymi językami programowania, które podlegają kompilacji bezpośrednio do kodu maszynowego (Asembler, Fortran czy też C++). Ponadto dzięki specjalnym bibliotekom można go łączyć z językami bardzo wysokiego poziomu (takimi jak np. Python czy Ruby).

129 Język C i asembler Łączenie języka C i asemblera jest dość powszechnym zjawiskiem. Dzięki możliwości połączenia obu tych języków programowania można utworzyć bibliotekę dla języka C, która niskopoziomowo komunikuje się z jądrem systemu operacyjnego komputera. Ponieważ zarówno asembler jak i C są językami tłumaczonymi do poziomu kodu maszynowego, za ich łączenie odpowiada program zwany linkerem (popularny ld). Ponadto niektórzy producenci kompilatorów umożliwiają stosowanie tzw. wstawek asemblerowych, które umieszcza się bezpośrednio w kodzie programu, napisanego w języku C. Kompilator, kompilując taki kod wstawi w miejsce tychże wstawek odpowiedni kod maszynowy, który jest efektem przetłumaczenia kodu asemblera, zawartego w takiej wstawce.

130 Język C i asembler Łączenie na poziomie kodu maszynowego – f1.S.text.globl _f1 _f1: pushl %ebp movl %esp, %ebp movl $4, %eax /* 4 to funkcja systemowa "write" */ movl $1, %ebx /* 1 to stdout */ movl $tekst, %ecx /* adres naszego napisu */ movl $len, %edx /* długość napisu w bajtach */ int $0x80 /* wywołanie przerwania systemowego */ popl %ebp ret.data tekst:.string "Hello world\n" len =. - tekst

131 Język C i asembler Łączenie na poziomie kodu maszynowego – f2.c extern void f1 (void); /*musimy użyć słowa extern*/ int main (){ f1(); return 0; } as f1.S -o f1.o gcc f2.c -c -o f2.o gcc f2.o f1.o -o program

132 Język C i asembler Argumenty Do komunikacji z funkcją język C korzysta ze stosu. Argumenty odkładane są w kolejności od ostatniego do pierwszego. Ponadto na końcu odkładany jest tzw. adres powrotu, dzięki czemu po wykonaniu funkcji program “wie”, w którym miejscu ma kontynuować działanie. Ponadto, początek funkcji w asemblerze wygląda tak: pushl %ebp movl %esp, %ebp Zatem na stosie znajdują się kolejno: zawartość rejestru EBP (Base Pointer, rejestr bazowy - służy do adresowania), adres powrotu a następnie argumenty od pierwszego do n-tego.

133 Język C i asembler Zwracanie wartości Na architekturze i386 do zwracania wyników pracy programu używa się rejestru EAX, bądź jego “mniejszych” odpowiedników, tj. AX i AH/AL. Zatem aby funkcja, napisana w asemblerze zwróciła “1” przed rozkazem ret należy napisać: movl $1, %eax Nazewnictwo Kompilatory języka C/C++ dodają podkreślnik “_” na początku każdej nazwy. Dla przykładu funkcja: void funkcja(); W pliku wyjściowym będzie posiadać nazwę _funkcja. Dlatego, aby korzystać z poziomu języka C z funkcji zakodowanych w asemblerze, muszą one mieć przy definicji w pliku asemblera wspomniany dodatkowy podkreślnik na początku.

134 Język C i asembler.text.globl _funkcja _funkcja: pushl %ebp movl %esp, %ebp movl 8(%esp), %eax /* kopiujemy pierwszy argument do %eax */ addl 12(%esp), %eax /* do pierwszego argumentu w %eax dodajemy drugi argument */ popl %ebp ret /*... i zwracamy wynik dodawania... */

135 Język C i asembler 32-bitowe rejestry ogólnego przeznaczenia to: EAX – Accumulator (akumulator - jego pamięć wykorzystuje arytmometr; używa się go do przechowywania wyników wielu operacji) EBX – Base Register (rejestr bazowy - służy do adresowania) ECX – Counter Register (rejestr licznikowy - służy jako licznik w pętli) EDX – Data Register (rejestr danych - umożliwia przekaz/odbiór danych z portów wejścia/wyjścia) ESP – Stack Pointer (przechowuje wskaźnik wierzchołka stosu) EBP – Base Pointer (rejestr bazowy - służy do adresowania) ESI – Source Index (rejestr źródłowy - trzyma źródło łańcucha danych) EDI – Destination Index (rejestr przeznaczenia - przetrzymuje informacje o miejscu docelowym łańcucha danych)

136 Język C i asembler #include extern int funkcja (int a, int b); int main (){ printf ("2+3=%d\n", funkcja(2,3)); return 0; }

137 Język C i asembler Oprócz możliwości wstępnie skompilowanych modułów można posłużyć się także tzw. wstawkami asemblerowymi. Ich użycie powoduje wstawienie w miejsce wystąpienia wstawki odpowiedniego kodu maszynowego, który powstanie po przetłumaczeniu kodu asemblerowego. Ponieważ jednak wstawki asemblerowe nie są standardowym elementem języka C, każdy kompilator ma całkowicie odmienną filozofię ich stosowania (lub nie ma ich w ogóle). Ze wstawek asemblerowych GNU korzysta się tak: int main (){ asm ("nop"); } W tym wypadku wstawiona zostanie instrukcja “nop” (no operation), która tak naprawdę służy tylko i wyłącznie do konstruowania pętli opóźniających.

138 Język C i C++ f1.c #include extern int f2(int a); int main (){ printf ("%d\n", f2(2)); return 0; } f2.cpp #include using namespace std; extern "C" { int f2 (int a){ cout << "a=" << a << endl; return a*2; }

139 Język C i C++ Kompilacja gcc f1.c -c -o f1.o g++ f2.cpp -c -o f2.o gcc f1.o f2.o -o program -lstdc++ extern "C" { /* funkcje, zmienne i wszystko to, co będziemy łączyć z programem w C */ }

140 stdio.h fclose() – funkcja zamyka otwarty funkcją fopen() plik. Jeśli plik nie zostanie zamknięty, a program zakończy działanie, zmiany nie zostaną zapisane. Funkcja zwraca 0, a w przypadku błędu EOF. int fclose (FILE *plik); feof() – funkcja zwraca wartość niezerową jeżeli napotka koniec pliku tekstowego. int feof(FILE *stream); fflush() – funkcja zapisuje dane znajdujące się w buforach obsługi podanego pliku. Plik pozostaje nadal otwarty. Funkcja fflush zwraca wartość 0 w przypadku wywołania zakończonego sukcesem lub wartość EOF, jeśli wystąpił błąd. int fflush(FILE * stream); fgetpos() – funkcja umieszcza w pos aktualną pozycję wskaźnika do pliku file. Zero gdy funkcja wykonała się pomyślnie, EOF w przypadku wystąpienia błędu, kod błędu umieszczany jest w zmiennej globalnej errno. int fgetpos (FILE* file, fpos_t* pos);

141 stdio.h fgets() – funkcja czyta kolejne znaki ze strumienia stream (usuwa je stamtąd) i umieszcza je w tablicy znakowej wskazywanej przez str. Czytanie przerywa, gdy przeczyta size - 1 znaków, natrafi na koniec pliku lub znak końca linii (znak ten jest zapisywany do str). Na końcu fgets() dopisuje znak '\0'. Wartością funkcji fgets(str, size, stream) jest str w przypadku sukcesu. W przypadku błędu lub natrafienia na koniec pliku przed przeczytaniem jakiegokolwiek znaku wartością funkcji jest NULL. Funkcja fgets() nie odróżnia sytuacji osiągnięcia końca pliku od błędu odczytu pliku. Jeśli potrzebne jest ich rozróżnienie użyj funkcji feof() lub ferror(). char *fgets(char *str, int size, FILE *stream); fread() – funkcja kopiuje nitems elementów z podanego pliku do tablicy. Kopiowanie kończy się w przypadku wystąpienia błędu, końca pliku lub po skopiowaniu podanej liczby elementów. Wskaźnik pliku jest przesuwany, tak by wskazywał pierwszy nieodczytany element. Wartość zwracana to liczba faktycznie wczytanych elementów. ptr – wskaźnik na tablicę, size - rozmiar elementu tablicy, nitems - liczba elementów do odczytania, stream - plik, na którym wykonywana jest operacja size_t fread(void *ptr, size_t size, size_t nitems, FILE *stream);

142 stdio.h fseek() - funkcja ustawia pozycję w pliku file na offset w zależnośći od wartości argumentu mode. Jeśli mode jest równy zero, to offset liczony jest od początku. Jeśli 1, to offset przesuwany od aktualnej pozycji, a 2 przesuwany o offset od końca pliku (wskaźnik pliku jest przesuwany do pozycji będącej sumą rozmiaru pliku i parametru offset). Zwraca zero, a w przypadku błędu wartość niezerową. int fseek(FILE *file, long offset, int mode); fsetpos() - funkcja zmienia aktualną pozycję wskaźnika do pliku file na pos. Zwraca zero gdy funkcja wykonała się pomyślnie, EOF w przypadku wystąpienia błędu, kod błędu umieszczany jest w zmiennej globalnej errno. int fsetpos (FILE* file, fpos_t* pos); ftell() - funkcja zwraca aktualną pozycję wskaźnika pliku. long ftell(FILE *file);

143 stdio.h fwrite() - funkcja kopiuje nitems elementów z poddanej tablicy do pliku. Kopiowanie kończy się w przypadku wystąpienia błędu lub po skopiowaniu podanej liczby elementów. Wskaźnik pliku jest przesuwany, tak by wskazywał pierwszy element po ostatnim zapisanym. Zwraca jest liczba faktycznie zapisanych elementów. ptr – wskaźnik na tablicę, size - rozmiar elementu tablicy, nitems - liczba elementów do odczytania, stream - plik, na którym wykonywana jest operacja size_t fwrite(const void *ptr, size_t size, size_t nitems, FILE *stream); getchar() - funkcja czyta znak ze standardowego wejścia i go stamtąd usuwa. Wywołanie getchar() jest równoważne wywołaniu getc(stdin). Funkcja getchar() zwraca kod pierwszego znaku ze standardowego wejścia traktowany jako unsigned char przekształcony do typu int. W przypadku końca pliku lub błędu funkcja zwraca wartość EOF. int getchar(void); gets() - Funkcja gets() czyta linię ze standardowego wejścia (usuwa ją stamtąd) i umieszcza ją w tablicy znakowej wskazywanej przez str. Ostatni znak linii (znak nowego wiersza - '\n') zastępuje zerem (znakiem '\0'). Wartością funkcji jest str w przypadku sukcesu lub NULL w przypadku błędu lub natrafienia na koniec pliku. Funkcja nie sprawdza, czy jest miejsce do zapisu w tablicy str. char *gets(char *str);

144 stdio.h perror() - Wypisuje zrozumiały komunikat o błędzie. Jeśli argumentem jest NULL, wypisuje na stderr tekstowy opis błędu, opierajac się na wartości zmiennej errno. Jeśli argumentem jest niepusty napis, napisze go najpierw, dodając dwukropek. Po wypisaniu komunikatu perror przechodzi do następnej linii. void perror(const char *string); putchar() - funkcja wysyła znak na standardowe wyjście. Wywołanie putchar (c) jest równoważne wywołaniu putc(c, stdout). Funkcja putchar() zwraca kod znaku traktowany jako unsigned char przekształcony do typu int. W przypadku błędu funkcja zwraca wartość EOF. int putchar(int c); puts() - funkcja wysyła na standardowe wyjście napis s, a następnie znak nowej linii. Funkcja zwraca liczbę nieujemną w przypadku sukcesu. W przypadku błędu zwraca wartość EOF. int puts(const char *s);

145 stdio.h remove() - funkcja usuwa podaną nazwę z systemu plików. Przy usuwaniu plików wywołuje funkcję unlink, natomiast przy usuwaniu katalogu używa funkcję rmdir. Zwraca 0 gdy funkcja zakończyła się poprawnie lub -1 w przypadku błędu. int remove (const char *name); rewind() - funkcja 'przewija' plik do początku. Jest równoważna wywołaniu funkcji fseek(file, 0, 0). void rewind(FILE *file); ungetc() - funkcja zwraca znak c z powrotem do strumienia stream. Przy najbliższej operacji czytania z tego strumienia znak pojawi się ponownie, jako pierwszy. Zwracany znak jest przekształcany do typu unsigned char; nie można zwrócić w ten sposób znacznika końca pliku (EOF). Dla każdego strumienia dopuszczalne jest zwrócenie z powrotem tylko jednego znaku. Wartość zwracana przez funkcję to wycofany znak w razie powodzenia lub EOF w razie błędu. int ungetc( int c, FILE *stream );

146 stdlib.h abort() - powoduje zamknięcie programu z powodu poważnego błędu. W systemach UNIX wywoływana jest funkcja raise(SIGABRT), co powoduje wyjście z programu z kodem -1 i wypisanie stanu rejestrów procesora. void abort(void); abs() - zwraca wartość bezwzględną liczby całkowitej. int abs(int); atexit() - sprawia, że podana jako parametr funkcja będzie wywołana w chwili, gdy program zakończy działanie. Zwraca wartość 0, gdy wywołanie funkcji zakończy sie pomyślnie, w innym przypadku zwraca wartość niezerową. Funkcja nie ustawia wartości errno. int atexit(void (*funkcja)(void)); div() - funkcja zwraca w strukturze div_t wynik dzielenia zmiennej numer przez zmienną denom. Struktura div_t składa się z dwóch pól typu int: quot i rem. Zwracana jest struktura div_t. div_t div (int numer, int denom);

147 stdlib.h atof(), atol() - funkcja jako argument pobiera liczbę w postaci ciągu znaków ASCII, a następnie zwraca jej wartość w formacie double. Liczbę może poprzedzać dowolna ilość białych znaków (spacje, tabulatory, itp.), oraz jej znak (plus (+) lub minus (-)). Funkcja atof() kończy wczytywać znaki w momencie napotkania jakiegokolwiek znaku który nie jest cyfrą, bądź znakiem e, lub E. Zwracana jest przekształcona liczba, a w przypadku gdy ciąg nie zawiera cyfr zwracana jest wartość 0. Znak musi bezpośrednio poprzedzać liczbę, czyli możliwy jest zapis "-2.333", natomiast próba potraktowania funkcją atof ciągu "- 2.333" skutkuje zwracaną wartością 0. double atof ( const char* string ); long atol(const char* string); atoi() - funkcja jako argument pobiera liczbę w postaci ciągu znaków ASCII, a następnie zwraca jej wartość w formacie int. Liczbę może poprzedzać dowolona ilość białych znaków (spacje, tabulatory, itp.), oraz jej znak (plus (+) lub minus (-)). Funkcja atoi() kończy wczytywać znaki w momencie napotkania jakiegokowiek znaku który nie jest cyfrą. W przypadku gdy ciąg nie zawiera cyfr zwracana jest wartość 0. Znak musi bezpośrednio poprzedzać liczbę, czyli możliwy jest zapis "-2", natomiast próba potraktowania funkcją atoi ciągu "- 2" skutkuje zwracaną wartością 0. int atoi ( const char * string );

148 stdlib.h exit() - kończy działanie programu i zwraca kod wyjścia. Wcześniej wypisywana jest zawartość strumieni wyjściowych i zamykane są wszelkie zasoby z jakich korzystał program (np. pamięć). status - kod wyjścia z programu, argument ten może posiadać wartość 0 lub EXIT_SUCCESS w przypadku pozytywnego zakończenia działania programu, w przeciwnym wypadku może zwracać EXIT_FAILURE lub dowolną wartość, z tym że liczy się ostatnie 8 najmniej znaczących bitów. void exit(int status); system() - powoduje wywołanie w powłoce polecenia command. int system (const char* command);

149 stdlib.h calloc() - funkcja przydziela pamięć dla nmeb elementów o rozmiarze size każdy i zeruje przydzieloną pamięć. malloc() - funkcja przydziela pamięć o wielkości size bajtów. realloc() - funkcja zmienia rozmiar przydzielonego wcześniej bloku pamięci wskazywanego przez ptr do size bajtów. Pierwsze n bajtów bloku nie ulegnie zmianie gdzie n jest minimum z rozmiaru starego bloku i size. Jeżeli ptr jest równy zero (tj. NULL), funkcja zachowuje się tak samo jako malloc. free() - funkcja zwalnia blok pamięci wskazywany przez ptr wcześniej przydzielony przez jedną z funkcji malloc, calloc lub realloc. Jeżeli ptr ma wartość NULL funkcja nie robi nic. nmeb - liczba elementów, dla których ma być przydzielona pamięć, size - rozmiar (w bajtach) pamięci do zarezerwowania bądź rozmiar pojedynczego elementu, ptr - wskaźnik zwrócony przez poprzednie wywołanie jednej z funkcji lub NULL void *calloc(size_t nmeb, size_t size); void *malloc(size_t size); void free(void *ptr); void *realloc(void *ptr, size_t size);

150 stdlib.h Jeżeli przydzielanie pamięci się powiodło, funkcje calloc, malloc i realloc zwracają wskaźnik do nowo przydzielonego bloku pamięci. W przypadku funkcji realloc może to być wartość inna niż ptr. Jeśli jako size, nmeb podano zero, zwracany jest albo wskaźnik NULL albo prawidłowy wskaźnik, który można podać do funkcji free (zauważmy, że NULL jest też prawidłowym argumentem free). Jeśli działanie funkcji nie powiedzie się, zwracany jest NULL i odpowiedni kod błędu jest wpisywany do zmiennej errno. Dzieje się tak zazwyczaj, gdy nie ma wystarczająco dużo miejsca w pamięci.

151 stdlib.h rand() - funkcja zwraca kolejną liczbę pseudolosową. Aby ustawić zarodek ciągu liczb pseudolosowych, należy posłużyć się funkcją srand. Aby otrzymać liczbę z mniejszego przedziału, należy posłużyć się operatorem modulo (%) lub operacjami na liczbach rzeczywistych. Liczba pseudolosowa z przedziału od 0 do RAND_MAX (które ma wartość co najmniej 32767). int rand(void); Użycie operacji na liczbach rzeczywistych jest często sugerowane, gdyż w przeciwieństwie do operacji modulo bierze pod uwagę bardziej znaczące bity wygenerowanej liczby, które teoretycznie są bardziej losowe od bitów mniej znaczących. Istotnie może to być prawdą, jednak nie ma to i tak większego znaczenia, gdyż jeżeli zależy nam na dużej losowości generatora powinniśmy w programie użyć innej implementacji generatora. Generalnie generator z biblioteki standardowej nie nadaje się do bardzo poważnych zastosowań. Należy pamiętać, że jeżeli górny zakres do którego losujemy jest bliski wartości RAND_MAX to liczby nie będą miały równomiernego rozkładu prawdopodobieństwa. Jest to prawdą dla każdej granicy, która nie jest dzielnikiem RAND_MAX+1, jednak przy małych wartościach niedokładność jest pomijalna.

152 stdlib.h srand() - funkcja inicjuje generator liczb pseudolosowych podanym w argumencie zarodkiem (ang. seed). Aby sprawić, by za każdym uruchomieniem programu zarodek liczb pseudolosowych był inny, można funkcję srand wywołać z argumentem time(0). void srand(int zarodek); Przykład: #include int main() { srand(0); printf("Pierwsza liczba pseudolosowa dla zarodka równego zero to: %d", rand()); return 0; }

153 stdlib.h Przykład: #include int main() { int i; srand(time(0)); for(i=0; i<10; ++i) { printf("%d. liczba pseudolosowa to %d\n", i, rand()); } printf("Liczba pseudolosowa z przedziału to %d\n", rand()%100); printf("Liczba pseudolosowa z przedziału to %d\n", (int)(rand() / (RAND_MAX + 1.0) * 100.0)); return 0; }

154 string.h memchr(), memrchr() - funkcja memchr() skanuje pierwsze n bajtów obszaru pamięci wskazywanego przez s w poszukiwaniu znaku c. Pierwszy bajt pasujący do c (interpretowany jako typ unsigned char) przerywa szukanie. Funkcja memrchr() jest podobna do funkcji memchr(), z tym wyjątkiem, że poszukuje wstecz od końca n bajtów wskazywanych przez s, zamiast do przodu, od początku. Funkcje memchr() i memrchr() zwracają wskaźnik do pasującego bajtu lub NULL jeżeli znak nie został znaleziony w podanym obszarze pamięci. void *memchr(const void *s, int c, size_t n); void *memrchr(const void *s, int c, size_t n); memcmp() - porównuje obszary pamięci. Funkcja porównuje pierwsze n bajtów obszarów pamięci s1 i s2. Zwraca liczbę całkowitą mniejszą od, równą, lub większą od zera jeżeli s1 jest odpowiednio mniejszy niż, równy, lub większy niż s2. int memcmp(const void *s1, const void *s2, size_t n);

155 string.h memcpy() - funkcja kopiuje size (liczba bajtów do skopiowania) bajtów z obiektu source (wskaźnik do obiektu źródłowego) do obiektu dest (wskaźnik do obiektu docelowego). Funkcja zwraca wskaźnik na dest. Obiekt dest powinien mieć zaalokowane dostatecznie dużo pamięci, aby móc pomieścić obiekt source, ponieważ może dojść do przepełnienia bufora. void *memcpy (void* dest, const void* src, size_t size); Przykład: #include int main (void) { char strTo[30]; char *strFrom = "Ala ma kota"; memcpy (strTo, strFrom, 12); printf("%s", strTo); }

156 string.h memset() - wypełnia kolejne bajty w pamięci ustaloną wartością. buffer - adres początkowy, c - wpisywana wartość (dla napisu - numer znaku), num - ile bajtów zapisać. void * memset ( void * buffer, int c, size_t num ); Przykład: #include int main() { char napis[] = "zamazujemy napis"; memset(napis, '*', 10); printf("%s\n", napis); return 0;}

157 string.h strcat(), strncat() - funkcja dopisuje tekst z tablicy strFrom (wskaźnik do źródłowej tablicy znaków) na koniec tekstu w tablicy strTo (wskaźnik do docelowej tablicy znaków) lub tylko n znaków. Funkcja zwraca wskaźnik na strTo. Tablica strTo powinna być dostatecznie duża, aby pomieścić dodany tekst z strFrom, ponieważ może dojść do przepełnienia bufora. char *strcat (char* strTo, const char* strFrom); Przykład: #include int main (void) { char strTo[30] = "Ala "; char *strFrom = "ma kota"; strcat (strTo, strFrom); printf("%s", strTo); }

158 string.h strchr(), strrchr() - lokalizuje znak w ciągu znaków. Funkcja strchr() zwraca wskaźnik do pierwszego wystąpienia znaku c (poszukiwany znak) w łańcuchu znaków s (przeszukiwany łańcuch). Funkcja strrchr() zwraca wskaźnik do ostatniego wystąpienia znaku c w łańcuchu znaków s. "Znak" oznacza tutaj "bajt" - funkcje te nie działają ze znakami wielobajtowymi. Funkcje strchr() i strrchr() zwracają wskaźnik do pasującego znaku lub NULL jeśli znaku nie znaleziono. char *strchr(const char *s, int c); char *strrchr(const char *s, int c); strcmp(), strncmp() - funkcja porównuje napisy s1 i s2 lub pierwsze n znaków napisów. Funkcja zwraca liczbę mniejszą od zera, gdy s1 s2. int strcmp(const char *s1, const char *s2);

159 string.h strcpy(), strncpy() - funkcja kopiuje tekst z tablicy strFrom (wskaźnik do źródłowej tablicy znaków) do tablicy strTo (wskaźnik do docelowej tablicy znaków) lub tylko n znaków. Funkcja kopiuje znak po znaku od początku, aż do końca tablicy lub znaku '\0', który też kopiuje. Funkcja zwraca wskaźnik na strTo. Tablica strTo powinna być dostatecznie duża, aby pomieścić tekst z strFrom, ponieważ może dojść do przepełnienia bufora. char *strcpy (char* strTo, const char* strFrom); Przykład: #include int main (void) { char strTo[30]; char *strFrom = "Ala ma kota"; /* tekst krótszy niż 30 znaków */ strcpy (strTo, strFrom); printf("%s", strTo); }

160 string.h strcspn() - funkcja zlicza od początku ilość znaków w łańcuchu s (łańcuch znaków) które nie należą do niepasujace (tablica znaków które zliczamy) i zatrzymuje się na pierwszym pasującym którego nie liczy. Ilość zliczonych znaków jest zwracana. size_t strcspn(const char *s, const char *niepasujace); strerror() - funkcja interpretuje wartość errnum (numer błędu) i zwraca wskaźnik do tablicy zawierającej słowny opis tego błędu. char * strerror(int errnum); strlen() - funkcja oblicza długość łańcucha str nie wliczając znaku '\0'. Zwracana jest długość łańcucha str. int strlen (char *str); Przykład: #include int main(){char tab[80]; int dl; printf("Podaj swoje imie: "); scanf("%s", tab); dl=strlen(tab); printf("Twoje imie sklada sie z %d znakow\n", dl); return 0; }

161 string.h strspn() - funkcja zlicza od początku ilość znaków w łańcuchu s które należą do pasujace (tablica znaków które zliczamy) i zatrzymuje się na pierwszym nie pasującym którego nie liczy. Ilość zliczonych znaków jest zwracana. size_t strspn(const char *s, const char *pasujace); strtok() - "Słowo" jest to niepusty ciąg znaków, które nie występują w łańcuchu delim, poprzedzony znakiem \0 lub znakiem występującym w delim. Funkcja strtok() służy do rozkładania ciągu znaków s na słowa. Pierwsze odwołanie do strtok() powinno posiadać s jako pierwszy argument. Następne wywołania powinny mieć jako pierwszy argument NULL. Każde wywołanie zwraca wskaźnik do następnego słowa lub NULL, gdy nie ma już więcej słów. Jeśli słowo kończy się separatorem (delim), to ten kończący znak jest nadpisywany przez \0 i zapamiętywany jest wskaźnik do następnego znaku dla następnego wywołania strtok. Ciąg separatorów delim może być inny dla każdego wywołania. Funkcja strtok_r() działa tak samo, jak strtok(), ale zamiast korzystać ze statycznego bufora, korzysta ze wskaźnika do przydzielonego przez użytkownika wskaźnika char*. Wskaźnik ten, parametr ptrptr, nie może zostać zmieniony podczas rozkładania jednego łańcucha. char *strtok(char *s, const char *delim); char *strtok_r(char *s, const char *delim, char **ptrptr);

162 math.h Aby korzystać ze wszystkich dobrodziejstw funkcji matematycznych należy dołączyc plik math.h: #include W procesie kompilacji należy dodać flagę “-lm”: gcc plik.c -o plik -lm Przy korzystaniu z funkcji trygonometrycznych trzeba wziąć pod uwagę m.in. to, ze biblioteka matematyczna prowadzi kalkulacje w oparciu o radiany, a nie stopnie. Stałe matematyczne: M_E - podstawa logarytmu naturalnego M_LOG2E - logarytm o podstawie 2 z liczby e M_LOG10E - logarytm o podstawie 10 z liczby e M_LN2 - logarytm naturalny z liczby 2 M_LN10 - logarytm naturalny z liczby 10 M_PI – liczba  M_PI_2 - liczba  /2M_PI_4 - liczba  /4 M_1_PI - liczba 1/  M_2_PI - liczba 2/ 

163 math.h Aby korzystać z liczb zespolonych w naszym programie należy w nagłówku programu umieścić następującą linijkę: #include Wiemy, ze liczba zespolona zdeklarowana jest następująco: z = a+b*i, gdzie a, b są liczbami rzeczywistymi, a i*i=(-1) W pliku complex.h liczba i zdefiniowana jest jako I. #include int main (){ float _Complex z = 4+2.5*I; printf ("Liczba z: %f+%fi\n", creal(z), cimag (z)); return 0;} creal - zwraca część rzeczywistą liczby zespolonej cimag - zwraca część urojoną liczby zespolonej

164 math.h Arcusy funkcji trygonometrycznych double acos (double x); float acosf (float x); long double acosl (long double x); double asin (double x); float asinf (float x); long double asinl (long double x); double atan (double x); float atanf (float x); long double atanl (long double x); double atan2 (double a, double b); float atan2f(float a, double b); long double atan2l(long double a, double b);

165 math.h Funkcje trygonometryczne double cos (double x); float cosf (float x); long double cosl (long double x); double sin (double x); float sinf (float x); long double sinl (long double x); double tan (double x); float tanf (float x); long double tanl (long double x);

166 math.h Area funkcji hiperbolicznych double acosh (double x); float acoshf(float x); long double acoshl(long double x); double asinh (double x); float asinhf(float x); long double asinhl(long double x); double atanh (double x); float atanhf(float x); long double atanhl(long double x);

167 math.h Funkcje hiperboliczne double cosh (double x); float coshf(float x); long double coshl(long double x); double sinh (double x); float sinhf(float x); long double sinhl(long double x); double tanh (double x); float tanhf(float x); long double tanhl(long double x);

168 math.h x - argument funkcji matematycznej. Miary kątów podawane są w radianach. a, b - współrzędne punktu na okręgu. acos - arcus cosinus argumentu, asin - arcus sinus argumentu, atan - arcus tangens argumentu, atan2 - arcus tangens ilorazu argumentu, cos - cosinus argumentu, sin - sinus argumentu, tan - tangens argumentu. Funkcje z przyrostkiem "h" obliczają wartości analogicznych funkcji hiperbolicznych i funkcji area. Funkcja atan2 bierze pod uwagę znaki obu argumentów dzięki czemu może dokładnie obliczyć kąt. Funkcja atan nie posiada takich informacji przez co przedział zwracanych przez nią wartości jest dwa razy mniejszy. Jeżeli wartości argumentu x jest spoza przedziału [-1; 1] funkcje acos i asin zwracają nieokreśloną wartość, a zmienna errno ustawiona jest na EDOM. Podobnie, jeżeli argumenty a i b są jednocześnie równe 0 funkcja atan2 ustawia zmienną errno na EDOM.

169 math.h Wartość zwracana Wartości odpowiednich funkcji matematycznych. Dodatkowo, dla arcusów funkcji trygonometrycznych zakres zwracanych wartości jest ograniczony. Dla funkcji: acos - jest to przedział [0; Π], asin - jest to przedział [-Π/2; Π/2], atan - jest to przedział [-Π/2; Π/2], atan2 - jest to przedział [-Π; Π]. Ponadto, jeżeli wystąpi nadmiar funkcja zwraca w wyniku HUGE_VAL z odpowiednim znakiem i ustawia wartość zmiennej errno na ERANGE. Jeśli wystąpi niedomiar funkcja w wyniku zwraca zero, a to czy do zmiennej errno zostanie zapisana wartość ERANGE zależy od implementacji.

170 math.h double pow (double x, double y); float powf(float x, float y); long double powl(long double x, long double y); double sqrt (double x); float sqrtf(float x); long double sqrtl(long double x); double hypot (double x, double y); float hypotf(float x, float y); long double hypotl(long double x, long double y); double cbrt (double x); float cbrtf(float x); long double cbrtl(long double x); double fabs (double x); float fabsf(float x); long double fabsl(long double x);

171 math.h Funkcje obliczają następujące wartości: pow - x do potęgi y sqrt - pierwiastek kwadratowy argumentu hypot - pierwiastek kwadratowy z sumy kwadratów argumentów cbrt - pierwiastek sześcienny argumentu fabs - wartość bezwzględna argumentu Jeżeli x jest liczbą ujemną funkcje sqrt ustawiają zmienną errno na EDOM. Tak samo zachowuje się funkcja pow, jeżeli x jest skończoną ujemną wartością, a y nie jest wartością całkowitą. Błąd dziedziny może także nastąpić, gdy x jest równe zero, a y jest liczbą niedodatnią.

172 math.h ceil(), copysign(), double_t(), erfc(), erf(), exp2(), expm1(), exp(), fdim(), float_t(), floor(), fmax(), fma(), fmin(), fmod(), fpclassify(), frexp(), ilogb(), isfinite(), isgreaterequal(), isgreater(), isinf(), islessequal(), islessgreater(), isless(), isnan(), isnormal(), isunordered(), ldexp(), lgamma(), llrint(), llround(), log1p(), log2(), logb(), log10(), log(), lrint(), lround(), modf(), nan(), nearbyint(), nextafter(), nexttoward(), remainder(), remquo(), rint(), round(), scalbln(), scalbn(), signbit(), tgamma(), trunc()


Pobierz ppt "Języki Programowania Patryk Jasik Katedra Fizyki Teoretycznej i Informatyki Kwantowej Wydział Fizyki Technicznej i Matematyki Stosowanej Politechnika Gdańska."

Podobne prezentacje


Reklamy Google