Programowanie komputerów Jacek Lach Zakład Oprogramowania Instytut Informatyki Politechnika Śląska
Plan printf Typy danych
Temperatury wersja 2 #include <stdio.h> /* print Fahrenheit-Celsius table for fahr = 0, 20,..., 300; floating-point version, acc. to K&R */ int main() { float fahr, celsius; float lower, upper, step; lower = 0; /* lower limit of temperature scale */ upper = 300; /* upper limit */ step = 20; /* step size */ fahr = lower; while (fahr <= upper) { celsius = (5.0/9.0) * (fahr-32.0); printf("%3.0f %6.1f\n", fahr, celsius); fahr = fahr + step; } return 0; }
Definiowanie nazw krótko #define nazwa tekst_zastępujący Wszystkie wystąpienia nazwy (za wyjątkiem stałych tekstowych i części innych nazw) zostaną zastąpione tekstem_zastępującym nazwa ma postać identyfikatora tekst_zastępujący może być dowolnym ciągiem znaków, niekoniecznie liczb
Wersja 3 pętla for #include <stdio.h> #define LOWER 0 /* dolny zakres tabeli */ #define UPPER 300 /* górny zakres */ #define STEP 20 /* krok */ /* Wypisywanie tabeli Fahrenheit-Celsius, według K&R */ int main() { int fahr; } for (fahr = LOWER; fahr <= UPPER; fahr = fahr + STEP) printf("%3d %6.1f\n", fahr, (5.0/9.0)*(fahr-32) ); return 0;
Instrukcja for Instrukcja for organizuje pętlę, jest uogólnieniem while Wewnątrz nawiasów umieszczane są trzy części oddzielane średnikami: inicjalizacja warunek modyfikacja Przykład: for(i=0;i<10;i++) {...}
* jako specyfikator precyzji Szerokość lub precyzja mogą być zdefiniowane przez gwiazdkę (*). Wtedy określa je następny argument, koniecznie typu int. Przykład: printf("%.*f", max, x);
printf konwersje d,i int; decimal number o int; unsigned octal number (without a leading zero) x,x int; unsigned hexadecimal number (without a leading 0x or 0X), using abcdef or ABCDEF for 10,...,15. u int; unsigned decimal number c int; single character s char *; print characters from the string until a '\0' or the number of characters given by the precision. f double; [ ]m.dddddd, where the number of d's is given by the precision (def: 6) e,e double; [ ]m.dddddde+/ xx or [ ]m.dddddde+/ xx, where the number of d's is given by the precision (default 6). g, G double; use %e or %E if the exponent is less than 4 or greater than or equal to the precision; otherwise use %f. Trailing zeros and a trailing decimal point are not printed. p void *; pointer (implementation dependent representation). % no argument is converted; print a %
printf sterowanie formatem %[flagi] [szerokość] [precyzja] [modyfikator_wielkości] typ_parametru Flagi: wyrównuje liczbę do lewej (normalnie byłaby wyrównana do prawej) + liczba zawsze zaczyna się od znaku "+" (dla dodatnich) lub " " (dla ujemnych), normalnie znak jest wyświetlany tylko dla liczb ujemnych
printf sterowanie formatem %[flagi] [szerokość] [precyzja] [modyfikator_wielkości] typ_parametru Szerokość: n określa, ile znaków zostanie wyświetlonych. Jeśli n jest większe od szerokości liczby, to zostanie ona uzupełniona spacjami. Jeśli jest mniejsze to liczba nie zostanie ucięta. 0n określa, ile znaków zostanie wyświetlonych. Jeśli n jest większe od szerokości liczby, to zostanie ona uzupełniona zerami. Jeśli jest mniejsze to liczba nie zostanie ucięta.
printf sterowanie formatem %[flagi] [szerokość] [precyzja] [modyfikator_wielkości] typ_parametru Modyfikator wielkości: l określa, że parametr jest typu long (np. long int to %ld) h określa, że parametr jest typu short (np. short int to %hd)
printf przykłady 0.521 "[%6.3f]" wyświetli się "[ 0.521]". Liczba 6 oznacza szerokość całej liczby, a nie tylko części przed przecinkiem. Ponieważ szerokość liczby jest równa pięć, to została dodana jedna spacja przed liczbą. "[% 6.3f]" wyświetli się "[0.521 ]". Jak wyżej, tylko spacja została dodana po liczbie (wyrównanie do lewej). "[%06.3f]" wyświetli się "[00.521]". Zamiast spacji, zostało dodane zero. "[%+6.3f]" wyświetli się "[+0.521]". Oczywiście dla wartości 0.521 wyświetli się znak minus, nie plus
printf przykłady short s = 1235; int i = 12345; long l = 12345678; unsigned u = 65535; format wyjście %d\%d 12345-12345 %8d %8d 12345-12345 %-8d %-8d 12345-12345 %+8d %+8d +12345-12345 %-+8d %-+8d +12345-12345 %08d %08d 00012345-0012345 % 08d % 08d 0012345-0012345 %+08d %+08d +0012345-0012345 %10.8d %10.8d 00012345-00012345
printf przykłady short s = 1235; int i = 12345; long l = 12345678; unsigned u = 65535; format wyjście wid = 14;..." %*d \n", wid, i ); 12345 wid = 9;..." %*d \n", wid, i ); 12345 " %14ld %14ld ", l, -l 12345678... " %10hd,%10hd", s, -s 1235... " %u %d ", u, u 65535-1 %3ld 12345678
printf przykłady short hex = 0x0b52; format wyjście %x b52 %X B52 %.4x 0b52 %04x 0b52 %10.8X 00000B52 %#x 0xb52
printf przykłady short oct = 0571; format wyjście %o 571 %5o 571 %8.5o 00571
printf przykłady long l = 12345678; format wyjście %#lo 057060516 %#lx 0xbc614e %#ld 12345678
printf przykłady float f = 5.555555; format wyjście %f 5.555555 %.2f 5.56 %10f 5.555555 %10.2f 5.56 %010.2f 0000005.56 %-10f 5.555555 %+.2f +5.56
printf przykłady double ff = 1234000000.0; format wyjście %.3e 1.234e+09 %E 1.234000E+09 %.3E 1.234E+09 %12.5e 1.234000e+09 %g 1.234e+09 %g 1234 %12g 1.234e+09 1.1 %.4g 1.1 %#.4g 1.100 %.10g 3.141592654
printf przykłady char c = 'A"; format wyjście %c A %4c A %-4c A %04c A
Łańcuchy precyzja hello, world (12 znaków) z dwukropkami :%s: :hello, world: :%10s: :hello, world: :%.10s: :hello, wor: :%-10s: :hello, world: :%.15s: :hello, world: :%-15s: :hello, world : :%15.10s: : hello, wor: :%-15.10s: :hello, wor :
printf przykłady char str[] = "This is all there is."; format wyjście %s This is all there is. %30s This is all there is. %5s This is all there is. %.5s This %-30s This is all there is. %10.2s Th
Strumienie we/wy Standardowe wejście: stdin Standardowe wyjście: stdout Stand. strumień błędów: stderr Szczegóły zostaną omówione później Normalnie strumienie te są związane z konsolą: stdin z klawiaturą stdout i stderr z terminalem tekstowym Strumienie mogą być przekierowywane testapp.exe <dane.txt >wyniki.txt
Wejście znakowe Odczytanie pojedynczego znaku lub EOF: int getc(file *stream); powyższa deklaracja nazywana jest prototypem Przykład: int c; c = getc(stdin); Dlaczego używamy int dla c zamiast char? Czytanie jednego znaku z stdin: int getchar(void); getchar() działa jak getc(stdin)
Wyjście znakowe Zapis pojedynczego znaku do strumienia: int putc(int c, FILE *stream); Przykład: putc( A,stdout); Zapis pojedynczego znaku do stdout: int putchar(int c); putchar( A ) działa jak putc( A,stdout) Zwracana wartość: wypisany znak
Kopiowanie #include <stdio.h> /* kopiowanie wejścia na wyjście: pierwsza wersja */ main() { int c; c = getchar(); while (c!= EOF) { putchar(c); c = getchar(); } return 0;
Kopiowanie #include <stdio.h> /* copy input to output; 2nd version */ main() { int c; } while ((c = getchar())!= EOF) putchar(c); return 0;
Zliczanie znaków #include <stdio.h> /* count characters in input; */ main() { long nc; } nc = 0; while (getchar()!= EOF) ++nc; printf("%ld\n", nc); return 0;
Zliczanie znaków #include <stdio.h> /* count characters in input; 2nd version */ main() { double nc; for (nc = 0; gechar()!= EOF; ++nc) ; printf("%.0f\n", nc); return 0; }
Zliczanie wierszy #include <stdio.h> /* count lines in input */ main() { int c, nl; } nl = 0; /* the number of lines so far */ while ((c = getchar())!= EOF) if (c == '\n') /* new line found */ ++nl; printf("%d\n", nl); return 0;
Zmienne w pamięci Każda zmienna pamiętana jest w pewnym miejscu pamięci... lub w rejestrze procesora Adres zmiennej jest indeksem tej komórki pamięci, w której znajduje się pierwszy bajt zmiennej Liczba bajtów zajmowanych przez zmienną zależy od jej typu; operator sizeof informuje o tym rozmiarze: sizeof(type)
Wskaźniki Wskaźnik jest adresem, pod którym znajduje się zwykle coś użytecznego Definiowanie zmiennej przechowującej adres: <type> *<variable> Przykład: int *pti; Pobieranie adresu, pod którym przechowywana jest zmienna: operator & int x; pti = &x;
Wskaźniki int x; pti = &x; x = 5; *pti = *pti + 1; /* teraz x = 6 */ x++; printf( %d,x); /* 7 */
Wskaźniki dlaczego teraz?...aby przeczytać z wejścia coś bardziej złożonego niż pojedynczy znak scanf jest odpowiednikiem printf dla strumienia wejściowego udostępnia wiele podobnych konwersji jak printf, ale w przeciwnym kierunku Prototyp: int scanf(char *format,...) scanf wymaga wskaźników do zmiennych
scanf przykład #include <stdio.h> main() { int x; printf( Podaj liczbę: ); scanf( %d, &x); printf( Kwadrat %d wynosi %d\n, x, x*x); return 0; }
scanf błędne dane #include <stdio.h> main() { int a,b; scanf("%d",&a); /* abc */ printf("a=%d\n",a); /* przypadkowa wart. */ scanf("%d",&b); /* nie czeka! */ printf("b=%d\n",b); /* znowu przyp. wart. */ return 0; }
Typy danych Jest tylko kilka podstawowych typów danych w C: char pojedynczy bajt, mogący przechowywać jeden znak z lokalnego zestawu znaków int liczba całkowita, zwykle odzwierciedlająca rozmiar słowa maszynowego na danym komputerze float liczba zmiennoprzecinkowa pojedynczej precyzji double liczba zmiennoprzecinkowa podwójnej precyzji
Typy danych Dodatkowo mogą być stosowane kwalifikatory opisujące dodatkowe typy (modyfikacje): short i long stosowane do liczb całkowitych Słowo int może być opuszczone w takich deklaracjach i zwykle tak się robi signed lub unsigned mogą byś stosowane do char i innych typów całkowitych
Typy danych short i long s powinny być różnej długości int ma zwykle rozmiar wynikający z długości słowa maszyny short ma zwykle 16 bitów, int ma 16 bitów lub 32 bity Każdy kompilator może obsługiwać inne rozmiary typów całkowitych, zależnie od możliwości sprzętu. Zastrzega się, że short i int mają co najmniej 16 bitów, long co najmniej 32 bity, short jest nie dłuższy niż int, który jest nie dłuższy niż long.
Typy danych Kwalifikator signed lub unsigned może być zastosowany do char lub innych typów całkowitych Liczby unsigned są zawsze nieujemne, podlegają zasadom arytmetyki modulo 2 n, gdzie n jest długością zmiennej danego typu w bitach Na przykład, jeśli char ma 8 bitów: unsigned char mają wartości od 0 do 255, signed chars mają wartości od 128 do 127 (w arytmetyce dopełnień do 2) Typ char może być signed lub unsigned zależy od maszyny. Znaki drukowalne są zawsze dodatnie.
Typy danych Typ long double określa liczby zmiennoprzecinkowe o podwyższonej precyzji Tak jak w przypadku liczb całkowitych, rozmiary typów zmiennoprzecinkowych są zależne od implementacji float, double i long double mogą reprezentować jeden, dwa lub trzy różne rozmiary Standardowe nagłówki <limits.h> i <float.h> zawierają stałe symboliczne dla wszystkich tych rozmiarów, wraz z innymi własnościami zależnymi od kompilatora i maszyny
Typy danych limits.h # define CHAR_BIT 8 /* Minimum and maximum values a `signed char' can hold. */ # define SCHAR_MIN (-128) # define SCHAR_MAX 127 /* Maximum value an `unsigned char' can hold. (Minimum is 0.) */ # define UCHAR_MAX 255 /* Maximum value an `unsigned int' can hold.(minimum is 0.) */ # define UINT_MAX 4294967295U /* Minimum and maximum values a `signed long int' can hold. */ # if WORDSIZE == 64 # define LONG_MAX 9223372036854775807L # else # define LONG_MAX 2147483647L # endif
Typy danych konwersje Kiedy operator ma działać na operandach różnych typów, są one konwertowane przy zastosowaniu niewielkiego zbioru reguł. double x, y; int k; // Różne typy! y = x + k; Ogólnie, jedynymi automatycznymi konwersjami są takie, które przekształcają typ mniejszy w większy bez utraty informacji.
Konwersje Wyrażenia, które nie mają sensu, są zakazane Przykład: użycie float jako indeksu Wyrażenia, w których można utracić informację, powodują zgłoszenie ostrzeżenia, ale są dozwolone; przykłady: kopiowanie większego typu całkowitego do mniejszego przypisanie wartości zmiennoprzecinkowej do zmiennej całkowitej
Konwersje Jeśli nie występują operandy unsigned, wystarczający jest następujący zestaw reguł: Jeśli jeden z operandów jest typu long double, przekształć drugi na long double. W przeciwnym wypadku, jeśli jeden operand jest double, przekształć drugi na double. W przeciwnym wypadku, jeśli operand jest float, przekształć drugi na float. W przeciwnym wypadku, przekształć char i short na int. Potem, jeśli jeden z operandów jest long, przekształć drugi na long.
Wymuszanie konwersji Jawna konwersja typu może być wymuszona w wyrażenie przez zastosowanie operatora rzutowania (ang. cast) W następującej konstrukcji: (nazwa_typu) wyrażenie wyrażenie jest przekształcane na wyrażenie o podanym typie cels = ((double)5/9) * (fahr 32); Konwersje są również wykonywane w wywołaniach funkcji
Stałe Stała całkowita taka jak 1234 jest typu int Stałe long są zapisywane z przyrostkiem l lub L: 123456789L Stałe całkowite bez znaku (unsigned) są zapisywane z przyrostkiem u lub U (ul oznacza unsigned long)
Stałe Poprzedzające stałą 0 (zero) oznacza stałą ósemkową System ósemkowy: np. 31 oct = 25 dec Poprzedzające stałą 0x lub OX oznacza stałą szesnastkową Przykład: 31 = 037 = 0x1f Inny przykład: 0XFUL to stała typu unsigned long o wartości dziesiętnej 15
Stałe Stała znakowa (character constant) to wartość całkowita, zapisywana jako pojedynczy znak w apostrofach, np. 'x' Wartość stałej odpowiada kodowi znaku w lokalnej tablicy kodów Przykład: '0' ma wartość 48 w kodzie ASCII
Stałe Stałe znakowe biorą udział w operacjach arytmetycznych tak jak inne stałe całkowite Niektóre znaki mogą być reprezentowane przez specjalne, dwuznakowe sekwencje przykład: '\n' (znak nowego wiersza) sekwencja, chociaż składa się z dwóch znaków, reprezentuje jeden znak
Stałe Dowolna stała o rozmiarze bajtu może być zapisana jako '\ooo' gdzie ooo to jedna to trzech cyfr ósemkowych (0...7) lub jako: '\xhh' gdzie hh jest jedną lub więcej cyfr szesnastkowych (0...9, a...f, A...F)
Sekwencje specjalne \a alert \' single quote \b backspace \f form feed \n newline \r carriage return \t horizontal tab \v vertical tab \\ backslash \? question mark \" double quote \ooo \xhh
Przykład char b[]="abc"; b[0]: 'a' b[1]: 'b' b[2]: 'c' b[3]: '\x0' (int)b[0]: 97 (int)b[1]: 98 (int)b[2]: 99 (int)b[3]: 0
Przykład char b[]="a0\n"; b[0]: 'a' b[1]: '0' b[2]: '\n' b[3]: '\x0' (int)b[0]: 97 (int)b[1]: 48 (int)b[2]: 10 (int)b[3]: 0
Przykład char b[]="\nn\n"; b[0]: '\n' b[1]: 'n' b[2]: '\n' b[3]: '\x0' (int)b[0]: 10 (int)b[1]: 110 (int)b[2]: 10 (int)b[3]: 0
Przykład char b[]="a\066b"; b[0]: 'a' b[1]: '6' b[2]: 'b' b[3]: '\x0' (int)b[0]: 97 (int)b[1]: 54 (int)b[2]: 98 (int)b[3]: 0
Przykład char b[]="\123c"; b[0]: 'S' b[1]: 'c' b[2]: '\x0' (int)b[0]: 83 (int)b[1]: 99 (int)b[2]: 0
Przykład char b[]="\567c"; Błąd! Kod > 255
Przykład char b[]="\xefgh"; b[0]: ' ' b[1]: 'g' b[2]: 'h' b[3]: '\x0' (int)b[0]: 239 (int)b[1]: 103 (int)b[2]: 104 (int)b[3]: 0
Przykład char b[]="\x12x\n"; b[0]: '\x12' b[1]: 'x' b[2]: '\n' b[3]: '\x0' (int)b[0]: 18 (int)b[1]: 120 (int)b[2]: 10 (int)b[3]: 0
Stałe i wyrażenia Stałe wyrażenie jest wyrażeniem, w którym występują tylko stałe Stałe wyrażenie może być obliczone podczas kompilacji, a nie wykonania programu.
Stałe łańcuchowe Stała łańcuchowa albo stała tekstowa (lub znakowa) jest ciągiem zera lub więcej znaków otoczonych przez znaki " Przykład: "Jestem łańcuchem znakowym" Stałe znakowe mogą być łączone (konkatenowane) podczas kompilacji: "hello, " "world" jest równoważne: "hello, world"
Stałe wyliczeniowe Wyliczenie jest listą stałych o wartościach całkowitych, np. enum boolean { NO, YES }; Pierwsza nazwa w enum ma wartość 0, następna 1 itd., chyba, że zostaną określone inne wartości. Jeśli nie wszystkie wartości zostaną podane, to dla nieokreślonych przyjęte zostaną wartości będące kontynuacją ostatniej podanej wartości, jak np.: enum months {JAN = 1, FEB, MAR, APR, MAY, JUN, JUL, AUG, SEP, OCT, NOV, DEC }; /* FEB = 2, MAR = 3, itd. */ Nazwy w tym samym i różnych wyliczeniach nie mogą się powtarzać. Wartości mogą się powtarzać.