Języki i paradygmaty programowania Instytut Teleinformatyki ITI PK Kraków marzec 2012
Spis rzeczy 1 Informacje wstępne Powstanie języka C Czego się spodziewać? Pierwsze programy 2 Przeglad instrukcji strujacych 3 Podstawowe typy wbudowane Typy pochodne Typy: void, enum; instrukcja typedef 4 Zakres lokalny Przestrzenie nazw Przesłanianie nazw
Historia C Powstanie języka C Czego się spodziewać? Pierwsze programy rozwinięty w Bell Laboratories w latach 1969/1972 jako część systemu Unix punkt wyjścia: potrzeba języka systemowego dla UNixa sprzęt: PDP7, brak oprogramowana (tylko asembler) ograniczenia: pamięć 8K 18-bitowych słów, niemożliwość podtrzymywania sprzętowego typów Ken Thomson - język B (bazujacy na BPCL) - mały język beztypowy, nie generował kodu maszynowego PDP-7 1970 - PDP-11 - sprzętowe wsparcie typów, adresacja bytowa, większa pamięć 24 kb 1972 Dennis Ritchie: nowe B - rozszerzenie B poprzez: wprowadzenie typów, usprawnienie obsługi napisów, możliwość kompilacja do kodu maszynowego PDP-11
Historia C -c.d. Powstanie języka C Czego się spodziewać? Pierwsze programy wykorzystanie - język bazowy do rozwoju systemu Unix rok 1973 - jadro systemu Unix przepisane w C, tylko kilkaset linii w asemblerze, większość systemu napisana od razu w C dostarczany jako fragment systemu Unix; szybko zdobył popularność (odbiorcy wielkie firmy, uczelnie) łatwość przenoszenia Unixa na nowy sprzęt rozwój języka, powstanie bibliotek standardowych, potrzeba standaryzacji rok 1978 - pierwszy podręcznik Kernighan i Ritchie - nieformalna specyfikacja języka rok 1983 - powołanie komitetu ANSI; wynik jego prac - standard języka ANSI C przyjęty w 1988
Ogólna charakterystyka Powstanie języka C Czego się spodziewać? Pierwsze programy Cechy języka C język względnie niskiego poziomu posługuje się znakami, liczbami, adresami brak operacji na obiektach złożonych (łańcuchach znaków, tablicach, plikach) słabe wsparcie zarzadzania pamięcia (wiszace referencje, brak mechanizmu garbage collection ) brak range checking brak instrukcji wejścia/wyjścia Zalety języka C mały, zwarty ale mocny język bardzo dobra przenośność oprogramowania duża swoboda programisty, ale też duża odpowiedzialność (zakłada się, że programista wie co robi) składnia C stała się baza dla wielu innych języków i narzędzi (np. C++, Java, C#, języki skryptowe)
Ogólna struktura programu Powstanie języka C Czego się spodziewać? Pierwsze programy program jedna lub więcej funkcji składnia każdej funkcji taka sama: typ NazwaFunkcji(listaParametrow) { tresc (ciało funkcji) funkcje nie moga być zagnieżdżone typ funkcji = typ zwracanej przez funkcję wartości nawiasy ( ) wymagane nawet wtedy, gdy lista deklaracji argumentów jest pusta wyróżniona rola funkcji main: każdy program musi zawierać funkcję main (umieszczona w dowolnym miejscu dowolnego pliku) w programie może być tylko jedna funkcja main start programu wykonanie pierwszej instrukcji funkcji main gdy nie określimy typu funkcji main, to kompilator zachowuje się tak, jakbyśmy napisali int main()
Najprostsze programy Powstanie języka C Czego się spodziewać? Pierwsze programy najkrótszy program zgodny ze składnia: main(){ program typu "Hello World" konieczność użycia funkcji bibliotecznych (I/O) konieczność deklaracji używanych funkcji bibliotecznych - pliki nagłówkowe, dyrektywa include #include <stdio.h> main() { printf("witaj w swiecie C/C++!!! \n"); standardowe biblioteki funkcji (I/O) w C stdio: funkcje printf, scanf, fprintf, fscanf w C++ iostream: obiekty std::cout, std::cin; operatory «,»
Zmienne w programie Powstanie języka C Czego się spodziewać? Pierwsze programy zmienne - pojemniki do przechowywania danych; pozwalaja pisać programy uniwersalne (dla dowolnych wartości danych) sa identyfikowane w programie poprzez nazwy nazwa - dowolny ciag liter, cyfr i znaków podkreślenia który rozpoczyna się od litery lub znaku podkreślenia i jest różny od słowa kluczowgo języka każda zmienna powinna być przed użyciem zdefiniowana; forma definicji: nazwatypu listazmiennnych; przykłady: int a, zmienna; float ulamek; wykonanie instrukcji definicji zmiennych: rezerwuje pamięć dla definiowaych zmiennnych ustala ich adresy możliwa jest inicjalizacja (nadanie wartości w momencie definicji), na przykład int a=4, zmienna; float ulamek=-2.3;
Instrukcje sterujace Przeglad instrukcji strujacych steruja przebiegiem wykonania programu pozwalaja zmienić kolejność wykonywania instrukcji, uzależnić go od stanu maszyny wyrażenia logiczne - często używane w instrukcjach sterujacych wyrażenia logiczne w C w C - nie ma specjalnego typu na przechowywanie wartości wyrażeń logicznych wartości tych wyrażeń moga być reprezentowane przez dowolny typ numeryczny; prawda wartość różna od 0, fałsz wartość równa 0 wyrażenia logiczne w C++ istnieje specjalny typ danych do wyrażania wartości wyrażeń logicznych; typ bool, jego stałe to true oraz false możliwy jest dawny sposób przechowywania informacji logicznej wykaz instrukcji sterujacych: instrukcja warunkowa if, if else, konstrukcja else if pętle: while, do... while, for instrukcja switch instrukcje break, continue, goto, etykieta
Przeglad instrukcji strujacych Wybór wielowariantowy - porównanie konstrukcja else - if --------------------- if(wyrażenie0) instrukcja0; else if(wyrażenie1) instrukcja1;... else if(wyrażenie_k) instrukcja_k; else instrukcja_ostat; instrukcja switch ----------------- switch(selektor) { case wyr_stałe_1 : ciąg_instr_1; case wyr_stałe_2 : ciąg_instr_2;... case wyr_stałe_k : ciąg_instr_k; defaault : ciąg_instr_default; typ obiektu wybierajacego - switch: selektor - musi być typu całkowitego do czego porównujemy - switch: selektor porównujemy z wyrażeniami stałymi rodzaj operacji porównujacej: switch - równość selektora jednego z wyrażeń stałych wnioski: else-if bardziej uniwersalna; switch przejrzyste i eleganckie
Typy Informacje wstępne Podstawowe typy wbudowane Typy pochodne Typy: void, enum; instrukcja typedef C/C++ wspieraja możliwość używania całej gamy typów typ obiektu - określa zbiór jego wartości oraz dozwolone operacje podział typów typy fundamentalne typy pochodne inny możliwy podział typów typy wbudowane typy zdefiniowane przez użytkownika podstawowe typy fundamentalne znaki (kody 0 do 127) i małe liczby: char liczby całkowite: short int, int, long int; modyfikacja signed int, unsigned int liczby zmiennoprzecinkowe: float, double, long double
Tworzenie typów pochodnych Podstawowe typy wbudowane Typy pochodne Typy: void, enum; instrukcja typedef w C/C++ mamy potencjalnie nieskończona ilość typów pochodnych dla każdego istniejacego typu można określić: tablice obiektów tego typu funkcje zwracajace obiekty danego typu wskaźniki do obiektów danego typu struktury: obiekty zawierajace zestawy danych różnych typów zasada tworzenia typów pochodnych: bierzemy obiekt typu podstawowego i jeden z operatorów spośród: tablica [ ] funkcja ( ) wskaźnik do pokazywania na obiekty danego typu: *
Typy pochodne - przykłady Podstawowe typy wbudowane Typy pochodne Typy: void, enum; instrukcja typedef Tablice: int a, b, c; int a[20], b[3], c[100] Wskaźniki: float x; float *wx; wx=&x; *wx=t; // obiekty typu fundamentalnego // tablice //obiekt typu float //wskaźnik do wskazywania obiektów float //ustawianie wskaźnika //użycie operatora wyłuskiwania Funkcje: int x, *wx; int f1(); int *f2(); //definicja obiektu typu int i wskażnika do int //definicja funkcji typu int //definicja funkcji typu wskaźnik do int
Typy void, enum Podstawowe typy wbudowane Typy pochodne Typy: void, enum; instrukcja typedef słowo kluczowe void może pojawiać się w deklaracjach typów złożonych, najczęściej jako określenie typu funkcji, która nie zwraca żadnej wartości: void mojafunkcja(); określenie wskaźnika do pokazywania na obiekt nieznanego typu (wskaźnik generyczny) void *p; void formalnie rzecz biorac rodzaj typu fundamentalnego, ale bez możliwości tworzenia zmiennych tego typu typ wyliczeniowy enum osobny typ dla wybranego przez użytkownika zestawu nazw (stałych całkowitych) umożliwia powiazanie nazw z liczbami, co poprawia przejrzystość programu stosujemy, gdy chcemy zachować nie tyle informację liczbowa, co pewna dodatkowa informację zawarta w nazwach - stałych typu
Typ enum - definicja Podstawowe typy wbudowane Typy pochodne Typy: void, enum; instrukcja typedef składnia definicji enum nazwatypu {listawyliczeniowa; gdzie listawyliczeniowa: nazwa1[=liczba1],...,nazwan[=liczbak] Przykład: enum kolor {czerwone=1, zolte=2, zielone=3; kolor sprawdz(); kolor swiatla; swialta=sprawdz(); switch(swiatla) { case zielone: std::cout << "Przechodz..."; break; case zolte : std::cout << "Uwazaj...:; break; case czerwone : std::cout<< "stoj i czekaj...;
Wyliczenia - kontrola typów Podstawowe typy wbudowane Typy pochodne Typy: void, enum; instrukcja typedef w C definicja stałych wyliczeniowych, brak kontroli typów; w C++ - dokładniejsza kontrola typów gdy kolor - typ wyliczeniowy określony powyżej, zaś lampa - zmienna typu kolor, to: lampa=3; lampa=zolty; //nielegalne //O.K. możliwa definicja typu wyliczeniowego bez nazwy równoważna zdefiniowaniu pewnej ilości stałych całkowitych we właściwym dla miejsca definicji typu zakresie ważności dwa typy wyliczeniowe o tym samym zakresie ważności nie moga zawierać takich samych stałych typu Definicje: enum kolor {czerwony =1, zolty = 3, zielony = 5; enum doswiadczenie {zielony, adept, wyjadacz; są w konflikcie ze względu na stałą: zielony
Instrukcja typedef Podstawowe typy wbudowane Typy pochodne Typy: void, enum; instrukcja typedef pozwala na nadanie dodatkowej nazwy już istniejacemu typowi składnia: typedef typistniejacy typdefiniowany; Przykład Uwagi typedef int Length; Length dlug, *szer; typ, który określamy w instrukcji typedef nie musi być typem fundamentalnym; możemy zdefiniować klka typów na raz, np.: typedef int natural, calk, *wskaz_do_calk; calk a; // równoważne int a; natural b; // równoważne int b; wskaz_do_calk p // równoważne int *p cel łatwa reparametryzacja programu (zmiana typu pewnej grupy zmiennych)
Czas życia, zakres ważności Zakres lokalny Przestrzenie nazw Przesłanianie nazw czas życia - okres od momentu definicji obiektu do czasu gdy przestaje on istnieć zakres ważności - obszar programu, z którego obiekt jest osiagalny poprzez swoja nazwę oba te atrybuty zależa od tego gdzie i jak zdefiniujemy dany obiekt zakres lokalny obiekt zdefiniowany w bloku lokalnym jest dostępny poprzez swoja nazwę tylko w tym bloku zmienna tam zdefiniowana "żyje" tylko do czasu opuszenia przez sterowanie zakresu tego bloku w C - definicje zmiennych musza nastapić na poczatku bloku (przez instrukcjami wykonywalnymi) w C++ - miejsce definicji dowolne, możliwe definicje "w locie" typowy przykład - definicje w pętli for
Zakresy ważności Zakres lokalny Przestrzenie nazw Przesłanianie nazw Przykład: //int k, s1, s2, s3; for (int k=1, s1=0, s2=0, s3=0; k<=10; k=k+1) { s1=s1+k; s2=s2+k*k; s3=s3+k*k*k; //std::cout << Suma liczb = << s1 << std::endl; //std::cout << Suma kwadratow liczb = << s2 << std::endl; //std::cout << Suma szescianow liczb = << s3 << std::endl; zakres bloku funkcji zmienne (obiekty) określone wewnatrz ciała funkcji i nie zawarte w żadnym bloku lokalnym maja zakres ważności bloku funkcji etykieta ma zawsze zakres ważności bloku funkcji
Zakres pliku Zakres lokalny Przestrzenie nazw Przesłanianie nazw większe programy - podział kodu na pliki (jednostki translacji) obiekt którego nazwa jest definiowana na zewnatrz wszystkich bloków - obiekt globalny jego zakres ważności - plik (od miejsca definicji do końca pliku) taka nazwa - na ogół niezenana w innych plikach; możliwosć przedłużenia zakresu ważności (instrukcja extern, deklaracja funkcji) problem dużych projektów wzrost rozmiarów tworzonego kodu, użytych bibliotek, ilości nazw globalnych wzrost prawdopodobieństwa kolizji nazw konieczność zapewnienia unikalności nazw najlepsza metoda uzyskania unikaloności - użycie przestrzeni nazw (C++, rok 1998)
Idea, realizacja Zakres lokalny Przestrzenie nazw Przesłanianie nazw problem - jedna przestrzeń dla wszystkich nazw obiektów globalnych w programie analogia - system plików: komputer przeznaczony dla wielu osób z sytemem plików zawierajacym tylko jedna kartotekę; rozwiazanie problemu: wprowadzenie struktury w systemie plików, podział przestrzeni dyskowej na rozłacznie podobszary dla problemu nazw w C++ - podział przestrzeni nazw obiektów globalnych na podprzestrzenie
Zakres lokalny Przestrzenie nazw Przesłanianie nazw Przestrzeń nazw - tworzenie i użycie składnia definicji: namespace nazwaprzestrzeninazw { definicje lub deklaracje obiektów globalnych określanych przez programistę #include <iostream> namespace tajne{ int klucz = 4401; int PIN; int main() { bool OK=true; do { std::cout «Podaj PIN: ; std::cin» tajne::pin; if(tajne::pin!=tajne::klucz) std::cout «Błędny PIN. Sprobuj jeszcze raz. «std::endl; else OK=false; while(ok); std::cout «PIN poprawny. ) «std::endl;
Zakres lokalny Przestrzenie nazw Przesłanianie nazw pełna nazwa obiektu z przestrzeni nazw: nazwa_przestrzeni::nazwa_obiektu mamy pewność, że nazwy z pewnej przestrzeni nazw będa jednoznaczne nawet bez podania nazwy przestrzeni problem: czy można odnosić się do obiektu z przestrzeni nazw poprzez jego nazwę właściwa? tak - dwa mechanizmy: deklaracja using postaci using nazwa_przestrzeni::nazwa_obiektu taka deklaracja udostępnia lokalnie pojedyncza nazwę z przestrzeni nazw dyrektywa using postaci using namespace nazwa_przestrzeni taka dyrektywa udostępnia lokalnie wszystkie nazwy z przestrzeni nazw
Zakres lokalny Przestrzenie nazw Przesłanianie nazw #include <iostream> namespace tajne{ int klucz = 4401; int PIN; using namespace std; int main() { //int PIN; bool OK=true; using tajne::pin; do { cout «"Podaj PIN: "; cin» PIN; if(pin!=tajne::klucz) cout «"Błędny PIN. Sprobuj jeszcze raz." «endl; else OK=false; while(ok); cout «"PIN poprawny."«endl;
Zakres lokalny Przestrzenie nazw Przesłanianie nazw #include <iostream> namespace moja{ int k=5; using namespace std; int k=7; //using moja::k; //trwale zamaskuje globalne k int main() { int k=3; cout «"W main - k = " «k «endl; //using moja::k; //konflikt z deklaracja lokalnego k cout «"ale tez - moja::k - k = " «moja::k «endl; cout «"jak rowniez - globalne k = " «::k «endl; cout «"-----------------------\n"; { int k=1; cout «"W bloku - k = " «k «endl; cout «"ale tez - moja::k = " «moja::k «endl; cout «"jak rownez - globalne k = " «::k «endl; cout «"-----------------------\n"; cout «"Po bloku - k = " «k «endl; dwie identyczne nazwy w tym samym zakresie ważności ==> bład kompilacji gdy zakresy różne - nazwy moga być być niezależnie używane; n.p. takie same nazwy w różnych blokach (funkcjach) gdy zakresy ważności się przekrywaja zasłanianie: nazwa o mniejszym zasięgu zasłania nazwę o większym zasięgu pomimo przesłonięcia, możemy odnosić się do zasłaniajacego obiektu używajac operatora zakresu ::