Upload
percy
View
50
Download
5
Embed Size (px)
DESCRIPTION
Programowanie strukturalne i obiektowe – język C. Programowanie. Język C B. W. Kernighan, Dennis M. Ritchie – „Język ANSI C” kompilator GCC http://gcc.gnu.org system operacyjny Microsoft Windows XP środowisko programistyczne DEV-C++ http:// www.bloodshed.net/devcpp.html. - PowerPoint PPT Presentation
Citation preview
Programowanie strukturalne i obiektowe – język C
Programowanie
Język C B. W. Kernighan, Dennis M. Ritchie – „Język ANSI C”
kompilator GCC http://gcc.gnu.org
system operacyjny Microsoft Windows XP środowisko programistyczne DEV-C++
http://www.bloodshed.net/devcpp.html
Odrobina historii języka C
1969 – Martin Richards – język BCPL 1970 – Ken Thompson – język B
adaptacja dla UNIX na maszynie DEC PDP-7 1972 – Dennis Ritchie – język C
DEC PDP-11 1983-1989 – standaryzacja ANSI
American National Standards Institute 1990 – standard ISO
International Organization for Standardization
Podstawowe cechy języka C
język ogólnego stosowania względnie „niski poziom” język imperatywny (proceduralny) podstawowe konstrukcje sterujące
programowania strukturalnego: grupowanie instrukcji, podejmowanie decyzji, wybór
przypadku, powtarzanie ze sprawdzaniem warunku zatrzymania na początku i końcu pętli, przerwanie pętli
Podstawowe cechy języka C – c.d.
język typowy typy podstawowe: znaki, liczby całkowite i zmiennopozycyjne w
różnych zakresach typy pochodne: wskaźniki, tablice, struktury, unie
wyrażenia buduje się z operatorów i ich argumentów wyrażenie może pełnić rolę instrukcji
włącznie z przypisaniem i wywołaniem funkcji input/output jest częścią biblioteki a nie języka przenośny - niezależny od architektury system UNIX jest rozwijany w języku C
Elementarz
„Nie można się nauczyć programowania bez samodzielnego pisania programów!”
proces kompilacji edycja kodu źródłowego (*.c) asemblacja (*.o) konsolidacja (plik uruchamialny, np. *.exe)
pierwszy program Wypisanie na konsoli napisu „Hello World!”
Podstawowe typy danych
int -32768 ... +32767 (przynajmniej 2 bajty)
float 1.E-36 … 1.E+36 (4 bajty, precyzja do 7 miejsc)
double 1.E-303 … 1.E+303 (8 bajtów, precyzja do 13 miejsc)
char 0 … 255 (1 bajt)
Deklaracje zmiennych i stałych
typ nazwa-zmiennej; int a; int x,y,z; int count=7;
const typ nazwa-stałej = wartość;const int NUM=123;const float PI=3.14;
Dodatkowe deklaratory typu
shortshort int = short
long long int = long
signed unsigned
Stałe znakowe i liczbowe
"string" ‘A’, ‘a’, …
‘A’ = 65 ‘\ooo’ ‘\xhh’ \n, \t, \r, … 1234 123456789L (123456789l) 123U (123u) 31 = 037 = 0x1f = 0X1F
Formatowany input/output
printf(…); int res=7; printf("Result: %d",res); int a=2; int res; res=a+7; printf("In:%d, Result:
%d",a,res); scanf(…);
int dat; scanf("%d",&dat);char str[20]; scanf("%s",str);
Formatowany input/output – c.d.
Usual variable type Display
%c char single character%d (%i) int signed integer%e (%E) float or double exponential format%f float or double signed decimal%g (%G) float or double use %f or %e as required%o int unsigned octal value
%p pointer address stored in pointer%s array of char sequence of characters%u int unsigned decimal%x (%X) int unsigned hex value
Formatowany input/output – c.d.
flag width.precision
flag meaning - left justify + always display sign space display space if there is no sign 0 pad with leading zeros # use alternate form of specifier
Formatowany input/output – c.d.
%#o adds a leading 0 to the octal value
%#x adds a leading 0x to the hex value
%#f or
%#e ensures decimal point is printed
%#g displays trailing zeros
Operacje i operatory Arytmetyczne:
+, -, *, /, % (modulo) Inkrementacja:
++, -- (++x, x++) Przypisanie:
=, +=, -=, *=, /=, … Relacyjne:
==, !=, <, >, <=, >= Logiczne
&&, ||, ! Bitowe:
& (AND), | (OR), ^ (XOR), <<, >>, ~ Koercja/rzutowanie (ang. cast)
(typ) wyrażenie (float)2/5
Wyrażenia warunkowe
wyr1 ? wyr2 : wyr3 Np. z=(a>b) ? a : b; /*z=max(a,b)*/
Instrukcja warunkowa
if (wyrażenie) instrukcja{…} – instrukcja złożona
if (wyrażenie) instrukcja1 else instrukcja2 if (wyrażenie1) instrukcja1
else if (wyrażenie2) instrukcja2…else if (wyrażenieX) instrukcjaXelse instrukcja
Instrukcja wyboru
switch (wyrażenie) { case wyrażenie-stałe1: instrukcje1 case wyrażenie-stałe2: instrukcje2 default: instrukcje}
break
Instrukcje skoku bezwarunkowe przekazanie sterowania do
innego miejscareturn wyrażenieopc;break;continue;goto etykieta;
etykieta: - poprawny identyfikator skok w obrębie tej samej funkcji nie należy nadużywać! uzasadnione w przypadku wyskoku z kilku pętli
jednocześnie
Instrukcje iteracyjne (pętli) while (wyrażenie) instrukcja do instrukcja while (wyrażenie); for (wyr1opc; wyr2opc; wyr3opc) instrukcja
można opuścić wyrażenia for (;;) instrukcja for (;;) ;
ominięcie wyr2 jest równoważne wystąpieniu stałej różnej od zera
Instrukcje pętli - przerywanie break;
przerwanie "najciaśniej otaczającej" pętli continue;
przekazanie sterowania do miejsca wznowienia "najciaśniej otaczającej" pętli
Przerywanie pętli - c.d.
while (...){ do { for(...){... ... ...} } while (...); }br: ; br: ; br: ;
break; jest równoważne goto br; jeśli w ciele pętli nie występuje "ciaśniejsza" pętla
Przerywanie pętli - c.d.
while (...){ do { for(...){... ... ...contin: ; contin: ; contin: ;} } while (...); }
continue; jest równoważne goto contin; jeśli w ciele pętli nie występuje "ciaśniejsza" pętla
Instrukcje pętli - równoważność for (wyr1; wyr2; wyr3) instrukcja
wyr1; while (wyr2){ instrukcjawyr3 ;}
Uwaga: pętle są równoważne gdy instrukcja nie zawiera continue;
Instrukcje pętli - porównanie while (wyrażenie) instrukcja
wyrażenie sprawdzane na początku - instrukcja nie musi być wykonana ani razu
np. odczyt z pliku do instrukcja while (wyrażenie);
wyrażenie sprawdzane na końcu - instrukcja musi być wykonana przynajmniej raz
for (wyr1; wyr2; wyr3) instrukcjazwykle wyr1 to inicjalizacja a wyr3 to inkrementacjanp. ustalona liczba iteracji
Tablice deklaracja:
typ identyfikator [rozmiar];np. int array[7]; char tab[256];Wniosek: elementy jednolitego typu
indeksowanie:x=array[6]; tab[i]='Z';Uwaga: indeksowanie od 0 !
Tablice -c.d. deklaracja tablicy wielowymiarowej:
typ identyfikator [rozmiar1][rozmiar1]...;np. int array[7][2]; char tab[256][10][100];
inicjowanie int arr[4] = {2,3,4,5}; int arr[] = {2,3,4,5}; float f[2][3]={
{1,2,3}, {4,5,6} };
Uwaga: najłatwiej przetwarzać pętlą for
Tablice i wskaźniki wskaźnik to zmienna zawierająca adres innej zmiennejpostać deklaracji: typ * zmienna;np. char * c;
korzyści z użycia wskaźnikówbardziej zwarty i efektywny kodczasami jedyny sposób implementacji pewnych
algorytmów w języku Cmożliwość dynamicznego tworzenia zmiennych (w trakcie
pracy programu) Uwaga: łatwo można napisać zupełnie niezrozumiały lub błędny kod gdy nie
zachowuje sie ostrożności!
Tablice i wskaźniki -c.d. pamięć komputera można przedstawić jako tablicę
kolejno numerowanych (adresowanych) komórek pamięcikomórkami pamięci można manipulować indywidualnie
lub całymi grupamibajtowi odpowiada typ chardwóm bajtom odpowiada typ short intczterem bajtom odpowiada typ long intwskaźnik jest grupą komórek, która może pomieścić
adres 2 bajty (komputery 16-bitowe) 4 bajty (komputery 32-bitowe)
Tablice i wskaźniki -c.d. operator adresowy (&) i wskaźnikowy (*)& - referencja, * - dereferencja 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]
Tablice i wskaźniki -c.d. równoważność tablic i wskaźnikówprzykład
int a[10]; int *pa; pa=&a[0];
x=*pa; x=a[0]; pa=&a[0]; pa=a; x=a[3]; x=pa[3];
arytmetyka na adresachadresy można inkrementować (++) i dekrementować (--)
pa+1 - wskazuje na następny obiekt pa+i &a[i] *(pa+i) a[i]
Specjalne typy wskaźnikowewskaźniki na znak (tablice znakowe) można
inicjować poprzez stałe łańcuchowe char str[ ] = "blah, blah, blah";
nie można zmienić wartości str
char * ptr = " blah, blah, blah"; można przypisać zmiennej ptr inne wskaźniki
wskaźnik na typ void może przechowywać wskaźniki na dowolny typ
void * v; char *p; int *q; ... v=p; ... v=q; ... v nie może być użyty do adresowania pośredniego
(wymaga rzutowania - cast) p=(*char)v;
Funkcje w języku Cprogram w C jest "zbiorem funkcji"
musi zawierać przynajmniej jedna funkcję (main)
funkcja to podprogram realizujący określone zadanie mogący zwracać do miejsca wywołania wartość określonego typu
można stosować wielokrotnie może przyjmować parametry ukrywa szczegóły implementacji przed innymi częściami
programu można pisać funkcje w różnych plikach kompilowanych
oddzielnie można łączyć funkcje w biblioteki
Uwaga: raczej kilka małych niż jedna duża funkcja!
Funkcje w C - ograniczenia funkcji nie można "zagnieżdżać"cała funkcja musi być umieszczona w jednym
pliku funkcje zawsze przekazują parametry "przez
wartość" konieczne jest używanie wskaźników
używana funkcja musi być uprzednio zadeklarowana
Funkcje w C - c.d.deklaracja:
typ-wyniku nazwa (parametryopc);
definicja:typ-wyniku nazwa (parametryopc); {... instrukcje ...return wyrażenieopc;}
standaryzacja w ANSI C
Funkcje w C - c.d.domyślny typ wyniku to intgdy funkcja nie zwraca wartości stosuje się typ
voidparametry mają postać:
typ1 zmienna1[, typ2 zmienna2]...
w deklaracji mogą występować tylko nazwy typów listę parametrów można pominąć w deklaracji -
nie będą wtedy sprawdzane przez kompilator!dla funkcji bez parametrów stosuje się zapis
(void) jako listę parametrów
Funkcje w C - c.d.zmienne globalne - zewnętrzne (extern)
deklaracja poza ciałem funkcji dostępne z każdej funkcji
nie należy nadużywać - komplikuje to większe programy
inicjalizacja na 0 gdy brak przypisania extern int x;
extern jest domyślne poza funkcją
zmienne statyczne (static) przechowują wartość we wszystkich wywołaniach tej
samej funkcji static int x; inicjalizacja na 0 gdy brak przypisania
Funkcje rekurencyjne w CFunkcja może wywoływać samą siebie
bezpośrednio pośrednio (również rekurencja wzajemna!)
konieczny jest mechanizm stosowy każde wznowienie funkcji otrzymuje na stosie swój komplet
wszystkich zmiennych automatycznych (niezależny od poprzedniego)
zalety rekurencji bardziej zwarta postać bardzo łatwo implementować struktury danych zdefiniowane w
sposób rekurencyjny, np. drzewa
wady rekurencji zwykle dłuższy czas działania zwykle większe zużycie pamięci
Rekurencja - przykładypotęga
xn=x * x *...* x (n razy) x0=1 , xn+1=x * xn
silnia n!=1*2*...*n 0!=1, n!=(n-1)!*n
liczby Fibonacciego realizacja iteracyjna realizacja rekurencyjna realizacja poprzez formułę Bineta
Rekurencja - przykłady c.d.klasyczny problem "Wieże Hanoi"szybkie sortowanie (QuickSort)
Strukturystruktura w C to obiekt złożony z jednej lub
kilku zmiennych, być może różnych typów, zgrupowanych "dla wygody" pod jedną nazwą
ułatwiają zorganizowanie skomplikowanych danych umożliwiają traktowanie związanych ze sobą
danych jako jeden obiekt
przykład - pozycja listy płac pracownik ma imię, nazwisko, adres,
wynagrodzenie, itp. adres to miejscowość, ulica, numer domu, itp. -
elementem struktury może być inna struktura
Struktury - c.d.standard ANSI C daje możliwość
przypisywania struktur przypisywania wartości początkowych przekazywania jako parametrów do funkcji zwracania jako wartość funkcji
Uwaga: przekazywanie do funkcji przez wartość może być kosztowne - czasami lepiej użyć wskaźnika
Struktury - c.d.definicja
struct nazwaopc {typ1 składowa1;typ2 składowa2;
...} zmienneopc;
nazwa jest etykietą - może być wykorzystana przy późniejszych deklaracjach
bez zmiennych mamy tylko opis kształtu struktury - nie jest rezerwowana żadna pamięć
odwołania nazwa-struktury . składowa wskaźnik-do-struktury -> składowa (np. p->s zamiast (*p).s)
inicjalizacja jak dla tablicy (poprzez stałe)
Struktury - przykładystruct point { int x; int y;};
struct point pt; struct point maxpoint={640,480};
struct point { int x; int y;} p,q,r;struct rect {
struct point pt1;struct point pt2;};
struct rect r; r.pt1.y=100;
Struktury - przykłady - c.d.struct rect r, *rp = &r;równoważne wyrażenia:
r.pt1.x rp->pt1.x (r.pt1).x (rp->pt1).x
Uwaga: operatory strukturowe (.), (->), podobnie jak nawiasy otaczające parametry funkcji (()) i indeksowanie tablic ([ ]) znajdują się na szczycie hierarchii priorytetów - wiążą najmocniej
Deklaracja typedef mechanizm typedef umożliwia tworzenie nowych nazw dla typów
danych, np. typedef int Length; Length len, maxlen; typedef char *String;
typ deklarowany w typedef pojawia się składniowo w miejscu zmiennej
konstruowanie struktur "rekurencyjnie" - np. drzew typedef struct tnode *Treeptr;
typedef struct tnode {char *word;int count;Treeptr left;Treeptr right;
} Treenode;
Unieunia w C to zmienna, która może zawierać
obiekty różnych typów i rozmiarówprzykład definicji
union u_tag {int ival;floaf fval;char *sval;
} u;
odwołania nazwa-unii . składowa wskaźnik-do-unii -> składowa
Unie - c.d.kompilator "troszczy" się o zgodność położenia i
pamięciunia jest wystarczająco obszerna żeby pomieścić
wartość największego z zadeklarowanych w niej typów
składnia zbliżona do struktury unia de facto jest strukturą o składowych nie
przesuniętych względem jej początku inicjalizacja tylko wartością pierwszego typu
programista musi się troszczyć o to, aby pobierane dane były zgodne z typem ostatnio zapisanym
Unie - przykład unii w strukturze struct{
char *name;int flags;int utype;union {
int ival;float fval;char *sval;
} u;} symtab[NSYM];
odwołanie do składowej ival symtab[i].u.ival
odwołanie do pierwszego znaku tekstu w sval *symtab[i].u.sval symtab[i].u.sval[0]
Pola bitowemogą reprezentować znaczniki jednobitoweumożliwiają upakowanie wielu znaczników w
jedno słowo maszyny (zależne od implementacji)
oszczędność narzucone fomaty danych, np. łącza z urządzeniami
zewnętrznymi mogą wymagać dostępu do kawałków słów
Pola bitowe - c.d.Przykład
maski odpowiadające właściwym pozycjom bitów mogą być zdefiniowane jako stałe
#define KEYWORD 01 #define EXTERNAL 02 #define STATIC 04
lub poprzez typ wyliczeniowy enum {KEYWORD=01, EXTERNAL=02,STATIC=04};
(liczby w maskach muszą być potęgami dwójki)
Pola bitowe - c.d.flags |= EXTERNAL | STATIC;
ustawia bity EXTERNAL i STATIC w zmiennej flags
flags &= ~(EXTERNAL | STATIC); kasuje powyższe bity
if (flags & (EXTERNAL | STATIC)==0)... warunek jest prawdziwy gdy oba bity są skasowane
Pola bitowe - c.d.definicja poprzez pola bitowe:
struct {unsigned int is_keyword : 1;unsigned int is_extern : 1;unsigned int is_static : 1;} flags;
flags.is_extern=flags.is_static=1;flags.is_extern=flags.is_static=0; if (flags.is_extern==0 && flags.is_static==0)
...
Pola bitowe - c.d.podobna definicja poprzez pola bitowe:
struct {unsigned int is_keyword : 1;unsigned int is_extern : 1;unsigned int is_static : 1;} flags;
unsigned int gwarantuje wartość bez znaku nazwaopc : rozmiar
brak nazwy można wykorzystać do "załatania dziury" rozmiar 0 wymusza przesunięcie kolejnych pól do następnego
słowa maszyny wypełniają pola od lewej do prawej lub odwrotnie
Uwaga: pola nie mają adresów - nie można stosować operatora &
Obsługa plikówstandardowe wejście (stdin), wyjście (stdout) i
wyjście błędów (stderr) typ plikowy FILE
struktura zdefiniowana w pliku nagłówkowym stdio.h
w programach wykorzystuje się wskaźnik do struktury FILE jako stałą umożliwiającą
otwarcie odczyt zapis
Obsługa plików - c.d.podstawowe operacje
FILE *fopen(char *name, char *mode) "r" - read "w" - write "a" - append "b" - dla plików binarnych "r+", "w+", "a+" - aktualizacja = czytanie i pisanie
między czytaniem należy wywołać funkcję pozycjonującą int fclose(FILE *fp)
wywoływana automatycznie na końcu programu int fflush(FILE *fp) FILE *reopen(char *name, char *mode, FILE *fp)
zwykle używa się do zmiany plików związanych z stdin, stdout, stderr
Obsługa plików - c.d. int ferror(FILE *fp)
wartość niezerowa gdy wystąpił jakiś błąd int feof(FILE *fp)
wartość niezerowa po napotkaniu końca pliku int getc(FILE *fp)
zwraca wczytany znak lub EOF gdy wystąpi błąd getchar
int putc(int c, FILE *fp) zwraca wypisany znak lub EOF gdy wystąpi błąd putchar
int ungetc(int c, FILE *fp) "oddaje" znak do pliku
Obsługa plików - c.d.pliki tekstowe
char *fgets(char *line, int maxline, FILE *fp) czyta co najwyżej maxline-1 znaków wraz ze wskaźnikiem
nowej linii do tablicy line i dodaje znak '\0' normalnie wartością jest wskaźnik do tablicy line po napotkaniu błędu lub końca pliku wartością jest NULL
int fputs(char *line, FILE *fp) wpisuje tekst do pliku (nie musi zawierać znaku nowej linii) wartością jest 0 lub EOF w przypadku błędu
gets używa stdin, kasuje znak nowej linii
puts używa stdout, dodaje znak nowej linii
Obsługa plików - c.d.
formatowane wejście/wyjście int *fprintf(FILE *fp, char *format, ...) int *fscanf(FILE *fp, char *format, ...) int *sprintf(char *s, char *format, ...) int *sscanf(char *s, char *format, ...)
Obsługa plików - c.d.
bezpośrednie wejście/wyjście size_t fread(void *p, size_t s, size_t n, FILE *fp) size_t fwrite(void *p, size_t s, size_t n, FILE *fp)
dostęp bezpośredni (nie sekwencyjny) int fseek(FILE *fp, long offset, int origin)
SEEK_SET, SEEK_CUR, SEEK_END
long ftell(FILE *fp) void rewind(FILE *fp) int fgetpos(FILE *fp, fpos_t *ptr) int fsetpos(FILE *fp, fpos_t *ptr)
Biblioteka standardowa: <ctype.h> funkcje do klasyfikacji znaków - wynik jest typu
int - różny od 0 gdy argument spełnia dany warunek int isalpha(int c) int isdigit(int c) int isalnum(int c) int islower(int c) int tolower(int c) int isupper(int c) int toupper(int c) int isspace(int c) int isxdigit(int c) int iscntrl(int c)
Biblioteka standardowa: <string.h> funkcje operujące na tekstach - argumenty s i t
są typu char*, cs i ct są typu const char*, n jest typu size_t, a c ma typ int przekształcony do charchar *strcpy(s,ct)char *strncpy(s,ct,n)char *strcat(s,ct)char *strncat(s,ct,n) int strcmp(cs,ct) int strncmp(cs,ct,n)size_t strlen(cs)
<string.h> - c.d.char *strchr(cs,c)char *strrchr(cs,c)size_t strspn(cs,ct)size_t strcspn(cs,ct)char *strpbrk(cs,ct)char *strstr(cs,ct)char *strtok(s,ct)
za pierwszym razem wywołuje się funkcję z argumentem s różnym od NULL
kolejne wywołania z s równym NULL ct może być różny za każdym razem na końcu zwraca NULL
<string.h> - c.d. funkcje operujące na obiektach traktowanych
jako tablice znakowe - argumenty s i t są typu void*, cs i ct są typu const void*, n jest typu size_t, a c ma typ int przekształcony do unsigned charvoid *memcpy(s,ct,n)void *memmove(s,ct,n)
działa, gdy obiekty "zachodzą" na siebie
int memcmp(cs,ct,n)void *memchr(cs,c,n)void *memset(s,c,n)
Biblioteka standardowa: <math.h> funkcje matematyczne - argumenty x i y są typu
double, n jest typu int, zwracają wynik typu doublesin(x) cos(x) tan(x)asin(x) acos(x) atan(x)exp(x) log(x) log10(x)pow(x,y) sqrt(x)ceil(x) floor(x)fabs(x)fmod(x,y)modf(x, double *ip)
Biblioteka standardowa: <stdlib.h> funkcje narzędziowe - konwersja liczb,
przydzielanie pamięci, interakcja z systemem operacyjnymdouble atof(const char *s) int atoi(const char *s) long atol(const char *s)double strtod(const char *s, char **endp)
jeśli endp jest różny od NULL - nieprzetworzona część napisu
long strtol(const char *s,char **endp, int b) b - podstawa (2-36)
<stdlib.h> - c.d. int rand(void)
wartość pseudolosowa z przedziału od 0 do RAND_MAX (co najmniej 32767)
void srand(unsigned int seed) początkowy zarodek jest równy 1
<stdlib.h> - c.d. void *bsearch(
const void *key, const void *base, size_t n, size_t size,int (*cmp)(const void *keyval, const void *datum) )
Uwaga: tablica musi być uporzadkowana rosnąco! funkcja cmp musi zwracać -1, 0 lub 1 gdy keyval*
poprzedza, jest równy lub następuje po datum* void qsort(void *base, size_t n, size_t size,
int (*cmp)(const void *, const void *) ) porządkuje w kolejności rosnącej funkcja porównująca taka sama jak dla bsearch
Dynamiczny przydział pamięci <stdlib.h>
void *malloc (size_t size) zwraca wskaźnik do obiektu o rozmiarze size utworzonego
dynamicznie zwraca NULL gdy nie może zrealizować polecenia
void *calloc(size_t n, size_t size) tworzy tablicę n elementów o rozmiarach size obszar jest inicjalizowany zerami
void *realloc(void *p, size_t size) zmienia rozmiar obiektu wskazywanego przez p na size
void free(void *p) zwalnia pamięć przydzieloną uprzednio przez malloc, calloc, lub
realloc nie robi nic gdy p jest równy NULL
Lista jednokierunkowa typedef struct intelem *list;
struct intelem { int val; list next;
}; funkcje:
read / print insert reverse join empty
Drzewa binarne typedef struct tnode *TreePtr;
struct tnode { char *word;int count; TreePtr left;TreePtr right;
}; przykład zastosowania:
szybkie zliczanie wystąpień słów w tekście
Preprocesor kompilatora C Pierwsza faza (przebieg) kompilacji
przed tłumaczeniem, asemblacją i konsolidacjągcc -E ...dyrektywy preprocesora to wiersze
rozpoczynające sie od znaku # gramatyka niezależna od języka C mogą wystąpić w dowolnym miejscu programu działanie kończy się wraz z końcem jednostki
tłumaczenia
Preprocesor - c.d. Kolejne (logiczne) fazy preprocesora
zastępowanie tzw. sekwencji trzyznakowych sklejanie wierszy
wystąpienia \<new line> są zamieniane na pojedyncze odstępy przekształcenie kodu programu na ciąg leksemów oddzielonych
odstępami komentarze zamieniane są na pojedynczy odstęp wykonanie dyrektyw (każda w osobnej linii) rozwinięcie makr
zastępowanie sekwencji specjalnych w stałych znakowych i napisach oraz łączenie sąsiadujących napisów
np. \n, \123 itp. tłumaczenie wyniku i łączenie z innymi programami/bibliotekami
zapewnienie powiązania zewnetrznych funkcji i obiektów z ich definicjami
Sekwencje trzyznakowe ??= # ??/ \ ??' ^ ??( [ ??) ] ??! | ??< { ??> } ??- ~
zgodność ze zbiorem znaków ISO 646-1983 - niezależność od zestawu znaków
gcc -trigraphs ...
Definicje stałych i makr # define identyfikator ciąg-leksemów
zlecenie zastępowania wszystkich wystąpień identyfikator przez ciąg-leksemów opuszczając odstępy otaczające ciąg-leksemów
ponowne użycie #define jest błędne gdy definiowane ciągi się różnią
# define identyfikator( lista-identyfikatorów ) ciąg-leksemów Uwaga: konieczny jest brak odstępu pomiędzy identyfikatorem
a otwierającym nawiasem opuszczanie odstępów i ponowne definiowanie - j/w
# undef identyfikator "zapomnienie" (być może niezdefiniowanej) definicji
Rozwijanie makr wystąpienie
identyfikator( lista-identyfikatorów ) jest wywołaniem makra po nawiasie może wystąpić odstęp lista jest separowana przecinkami
przecinki są "chronione" przez apostrofy, cudzysłów i zagnieżdzone nawiasy
ilość argumentów musi odpowiadać definicji argumenty są izolowane poprzez usunięcie otaczających
odstępów - makra występujące w argumentach nie są na razie rozwijane
specjalna obróbka znaku # i operatora ## rozwijanie makrowywołań występujących w leksemach
argumentów tuż przez zastąpieniem
Rozwijanie makr - c.d. jeśli w definicji makra w zastępującym ciągu
znaków wystąpienie argumentu jest poprzedzone znakiem # to parametr jest zastępowany ciągiem otoczonym przez cudzysłów znaki " i \ występujące w argumencie w stałych
znakowych napisach są poprzedzane przez znak \
jeśli w definicji makra wystąpi infiksowy operator ## to po zastąpieniu parametrów jest on usuwany powodując "sklejenie" sąsiadujących leksemów operator ## nie może wystąpić na początku ani końcu
zastępującego ciągu leksemów
Rozwijanie makr - przykłady #define ABSDIFF(a,b) ((a)>(b) ? (a) - (b) : (b) - (a))
w przeciwieństwie do podobnej funkcji, argumenty i wynik mogą być dowolnego typu arytmetycznego lub nawet moga być wskaźnikami
Uwaga: argumenty są tu obliczane dwa razy - różnica gdy mają "efekty uboczne"
#define tempfile(dir) #dir "/%s" wywołanie tempfile(/usr/tmp) produkuje
"/usr/tmp" "/%s" co jest następnie sklejane w jeden napis
Rozwijanie makr - przykłady - c.d. #define cat(x,y) x ## y
cat(1,2) produkuje 12 cat(cat(1,2),3) produkuje niepoprawny cat ( 1, 2 )3
#define xcat(x,y) cat(x,y) makro "drugiego poziomu" xcat(xcat(1,2),3) produkuje 123
Preprocesor - włączanie plików # include "nazwa-pliku"
lub# include <nazwa-pliku> przeszukiwanie bieżącej kartoteki lub listy lokalizacji
zależnej od implementacji włączanie plików można zagnieżdżać można użyć konstrukcji
# include ciąg-leksemów aby wywołać rozwinięcie makra do jednej z postaci j/w
Preprocesor - kompilacja warunkowa # if wyrażenie-stałe # ifdef identyfikator # ifndef identyfikator # else # elif wyrażenie-stałe # endif defined (identyfikator) lub defined identyfikator Uwagi:
wyrażenia są przedmiotem makrorozwinięć wyrażenia są podstawiane przez 0L lub 1L
Preprocesor - symbole specjalne zawsze zdefiniowane - nie można ich zmienić (podobnie jak
operatora defined) _ _LINE_ _
dziesiętna stała o numerze bieżącego wiersza programu _ _FILE_ _
nazwa tłumaczonego pliku _ _DATE_ _
napis w postaci "Mmm dd rrrr" - data kompilacji _ _TIME_ _
napis w postaci "gg:mm:ss" - czas kompilacji _ _STDC_ _
1 dla kompilatorów dostosowanych do standardu
Preprocesor - pozostałe dyrektywy # error ciąg-leksemów
wypisanie komunikatu diagnostycznego podczas kompilacji
# line stała lub# line stała "nazwa-pliku" przypisanie numeru linii - może być wykorzystywane do
celów diagnostycznych
# pragma ciąg-leksemów zależne od implementacji kompilatora ignorowane jeśli kompilator nie obsługuje
# pusta dyrektywa preprocesora
Zmienne listy argumentów przykład z biblioteki standardowej
int printf(const char *format, ...) plik nagłówkowy <stdarg.h>
typ va_list - reprezentuje wskaźnik na kolejny argument makra
void va_start(va_list ap, ostatni_nazwany_parametr) typ va_arg(va_list ap, typ) void va_end(va_list ap)
przykład typer myfunc(type1 par1,..., typek park , ...)
{ ... va_list ap; va_start(ap, park); ... typek+1 x = va_arg(ap, typek+1) ...
va_end(ap); ... }
Skoki odległe (long jumps) zastosowanie - ominięcie ciągu wywołań funkcji i
powrotów (wyskok z zagnieżdżonych funkcji) plik nagłówkowy <setjmp.h>
jmp_buf - typ do przechowania stanu programu w chwili wywołania funkcji setjmp
int setjmp(jmp_buf env)void longjmp(jmp_buf env, int val)
przykład if (setjmp(env)==0)
... /* kod wykonywany bezpośrednio po wejściu*/else ... /* kod wykonywany po wołaniu longjmp */
Interakcja z systemem operacyjnym plik nagłówkowy <stdlib.h>
char *getenv(const char *name) zwraca wartość zmiennej środowiskowej name
int system(const char *prog) uruchamia program prog i zwraca jego kod wyjścia
void exit (int status) kończy program z kodem status stałe EXIT_SUCCESS, EXIT_FAILURE
int atexit(void (*func)(void)) rejestruje funkcję func do wykonania przy wyjściu z programu -
powstaje łańcuch funkcji
void abort(void) bezwzględne zakończenie programu
Obsługa daty i czasu plik nagłówkowy <time.h>
clock_t, time_t - typy arytmetycznestruct tm - składniki czasu kalendarzowego
int tm_sec int tm_min int tm_hour int tm_mday int tm_mon int tm_year - lata, które upłyneły od 1900 r. int tm_wday int tm_yday int tm_isdst - znacznik czasu letniego
Obsługa daty i czasu - c.d. wybrane funkcje do obsługi czasu
time_t time(time_t *tp) aktualny czas kalendarzowy (tp może być NULL)
double difftime(time_t t1, time_t t2) różnica czasów wyrażona w sekundach
struct tm *gmtime(const time_t *tp)struct tm *localtime(const time_t *tp)char *asctime(const struct tm *tp)size_t strftime(char *s, size_t max,
const char *fmt, const struct tm *tp) format podobny do printf, np. %d - dzień miesiąca,
%H - godzina, itp.
Obsługa sygnałów plik nagłówkowy <signal.h>
void (*signal(int sig, void (*handler)(int)))(int) SIG_DFL, SIG_IGN - predefiniowane handlery na początku obsługi przywracana jest domyślna wartość
handlera - należy ustawić go na nowo wewnątrz handlera
int raise(int sig)wybrane sygnały
SIGFPE SIGINT SIGSEGV SIGABRT SIGTERM
Obsługa diagnostyczna plik nagłówkowy <assert.h>
makro assert(int wyrażenie) Assertion failed: wyrażenie, file ..., line ...
_ _FILE_ __ _LINE_ _wywołanie funkcji abort
zdefiniowanie NDEBUG przed włączeniem pliku nagłówkowego assert.h sprawia, że makro assert jest ignorowane
Microsoft Windows API
API (Application Programming Interface)kernel32.dll, user32.dll, gdi32.dll, shell32.dll, ...C/C++#include <windows.h>
Dokumentacja:http://msdn2.microsoft.com/en-us/library/http://www.winprog.org/tutorial/
Microsoft Windows API - c.d.
WinMain#include <windows.h> int WINAPI WinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
{ MessageBox(NULL,"Hello world!",
"Note", MB_OK); return 0;
}
Microsoft Windows API - c.d.
Funkcja "Call-back"LRESULT CALLBACK WindowProcedure( HWND hwnd,
UINT message, WPARAM wParam, LPARAM lParam
){ ... switch (message)
{ ... default: return DefWindowProc (hwnd, message, wParam, lParam); } ... }
Microsoft Windows API
Wstęp do technik tworzenia parserów
Co to jest parser? program przetwarzający dane zapisane w
sformalizowanym języku zgodnie z jego gramatyką
liczne przykłady zastosowania: kompilatory języków programowania interpretatory poleceń obsługa interfejsów użytkownika przetwarzanie formalnych dokumentów
HTML, TEX, XML, Mizar, ...
Techniki tworzenia parserów - c.d.
Formalny opis językagramatyka
gramatyka bezkontekstowa (context-free grammar) zbiór rodzajów syntaktycznych wyrażeń zestaw reguł ich tworzenia z podwyrażeń
notacja BNF (Backus-Naur Form) stworzona do specyfikacji języka Algol 60
gramatyki (LA)LR możliwość przetworzenia dowolnych danych wejściowych z
pojedynczym wyprzedzeniem (look-ahead)
tokenizacja (scanning) symbole terminalne i nieterminalne
Techniki tworzenia parserów - c.d.
Przykład tokenizacji int /* keyword `int' */
square (int x) /* identifier,open-paren,keyword `int',identifier,close-paren */
{ /* open-brace */
return x * x; /* keyword `return', identifier, asterisk, identifier, semicolon */
} /* close-brace */
jest to definicja funkcjizawiera ona jedną deklarację i jedną instrukcjęw instrukcji każdy 'x' jest wyrażeniem a także
'x * x' jest wyrażeniem
Tworzenie skanerów LEX
GNU Flex (fast scanner generator)wyrażenia regularne w pliku wejściowymoutput w postaci kodu C (lex.yy.c) funkcja int yylex()
konsolidacja z biblioteką fl (gcc -l fl ...)
format pliku wejściowego definitions
%% rules %% user code
Tworzenie parserów Yacc
GNU Bison (general-purpose LALR generator)wyrażenia regularne w pliku wejściowymoutput w postaci kodu C (plik.tab.c) - parser funkcja int yyparse()
wymaga funkcji yylex kompatybilny z Lex
format pliku wejściowego %{ Prologue %}
Bison declarations %% Grammar rules %% Epilogue