1.1 Wstęp... 1 1.2 Obiekty stałe... 3 1.3 Obiekty statyczne... 4 1.4 Wskaźniki... 5 1.5 Referencje... 8 1.6 Wskaźniki do wskaźników... 11 1.7 Definiowanie własnych typów danych, polecenie typedef... 17 1.8 Typ wyliczeniowy enum... 18 1.9 Obszary nazw... 20 1.10 Zakres zmiennych... 21 1.11 Zmienne extern... 22 1.12 Obszary pamięci... 23 1.1 Wstęp Środowisko programistyczne Microsoft Visual Studio 2008. Środowisko programistyczne (development environment) jest to zbiór narzędzi niezbędnych do powstania programu komputerowego. W skład środowiska programistycznego wchodzą: urządzenia wykorzystywane do pisania i testowania programów komputerowych, oprogramowanie, programy firmware'owe, standardy i procedury tworzenia oprogramowania, dokumentacja oprogramowania. Etapy tworzenia pliku wykonywalnego 'exe': preprocesor, kompilator, linker. Preprocesor: wykonuje dyrektywy w kodzie zaczynające się od #. usuwa komentarz z kodu. Przykłady dyrektyw dla preprocesora: #include, dołączanie plików nagłówkowych, np. #include <nazwapliku>, #include "nazwapliku", #define, kopiowanie stringów, makr, #pragma, instrukcje dla kompilatora. Kompilator - tłumaczy kod źródłowy napisany np. w języku C++ na kod asemblerowy. MS VS 2008 kompiluje pliki *.cpp, *.h, wyniki kompilacji zapisuje do plików: *.obj. Linker: łączy pliki *.obj, *.lib, *.dll, generuje plik wykonywalny *.exe. Baza linkera znajduje się w pliku *.ilk Tryby kompilacji: debug, release. Kompilacja w trybie 'debug': baza programu generowana przez linker'a zawiera informacje o przebiegu kompilacji w trybie debug. Informacje zawarte są w pliku *.pdb. Podczas kompilacji w trybie 'release' nie są tworzone pliki *.pdb. 1
Definicja: rozwiązanie (solution) - to grupa projektów MS VS 2008. Struktura projektu Microsoft Visual Studio 2008, szablon aplikacji konsolowej. Katalog \projekt1\ projekt1.sln, plik zawiera informacje o konfiguracji i położeniu plików dla rozwiązania, projekt1.suo, plik zawiera konfigurację rozwiązania specyficzne dla danego użytkownika, projekt1.ncb *.cpp, pliki programu, projekt1.vcproj, plik w formacie zawiera xml, zawiera parametry konfiguracyjne projektu, projekt1.vcproj.nazwahosta.nazwauzytkownika.user, plik w formacie zawiera xml, zawiera parametry konfiguracyjne projektu specyficzne dla danego użytkownika, katalog \Debug\ lub \Release\ o Projekt1.obj, o Projekt1.ilk, o Projekt1.pdb. Konwencje zapisu programu w projekcie. Nazwy zmiennych, funkcji typów powinny być opisowe i składać się z minimum dwóch wyrazów. Każdy wyraz powinien zaczynać się do dużej litery. Przykład. int WielkoscBufora = 1024; int OtworzOknoDialogowe() Obiekty stałe powinny być nazywane dużymi literami const int WIELKOSC_BUFORA = 1024; Stałe znakowe #define WIELKOSC_BUFORA 1024 Na ćwiczeniach można stosować do zapisu programu notację węgierską. Zasady notacji węgierskiej. litery typ przykład nazwy zmiennej i int int iliczba; d double double dliczba; c char char clitera; st string string stimie; litery nazwa przykład nazwy obiektu klasy st CString CString stbufor; sz CSize CSize sztext Nazwa wskaźnika zaczyna się od małej litery p. int * pliczba; 2
1.2 Obiekty stałe Stałe dosłowne (literały), czyli stałe które mają wartość ale nie mają nazwy. Każdy literał ma przypisany typ, np. 0 ma typ int, 3.14159 ma typ double. Literał typu int można zapisać w układzie 10-tkowym, 8-mkowym, 16-tkowym, np. 20, // 10-tkowy 024, //8-mkowy 0x14 //16-tkowy Literałom można przypisać typ, np. unsigned, long, unsigned long: 128u unsigned 1L long 102UL, 102Lu unsigned long Stałe zmiennoprzecinkowe mają domyślny typ double, ale można przypisać im typ float 3.14159F // oznacza 3.14159.001f // oznacza 0.001 12.345L // oznacza 12.345 0. // oznacza 0 3.14159E0f // oznacza 3.14159 1E-3F // oznacza 0.001 1.2345E1L // oznacza 12.345 0e0 // oznacza 0 Stałe znakowe reprezentują znaki, np. 'a', '\n', '\?', Stałe tekstowe to stringi, np. "programowanie 2" Modyfikator const. const <typ> <nazwa obiektu> = <wartość obiektu>; Przykład 1. Obiekt const, wskaźnik, referencja, stały wskaźnik, stała referencja do stałego obiektu. const double PI = 3.14; const double * const cppi = &PI; const double * ppi = &PI; const double &rpi = PI; const double const &crpi = PI; double PII = 3.1415; const double * const cppii = &PII; double * const cppii2 = &PII; Własności: 1. Obiektom const nie można zmienić wartości. 2. Obiekty const muszą być zawsze zainicjowane. 3. Obiekty const mogą służyć do określania wielkości tablicy. 4. Obiekty const mają zasięg w pliku w którym są zdefiniowane. 3
1.3 Obiekty statyczne static <typ> <nazwa obiektu> = <wartość>; Przykład 1. Definicja obiektu statycznego. static int is = 100; Własności: 1. Lokalny obiekty typu static zachowuje swoją wartość po zakończeniu funkcji. 2. Obiekty typu static obowiązują w jednym pliku. 3. Do obiektów static nie można stosować modyfikatora extern. 4. Obiekty static nie mogą być argumentami funkcji. 5. Nie można używać w deklaracjach typedef. Przykład 2. Lokalny obiekty typu static zachowuje swoją wartość po zakończeniu funkcji (w01-01-static.cpp). void f() static int si = 0; int i = 0; cout << "si = " << ++si << "\t" << " i = " << ++i << endl; void main() f(); f(); // si = 10; // błąd, zmienna lokalna si = 1 i = 1 si = 2 i = 1 4
1.4 Wskaźniki Programowanie 2. Język C++. Wykład 1. Definicja. Wskaźnik jest zmienną która przechowuje adres innej zmiennej. Definicja wskaźnika na stosie. <typ> *<nazwa_wskaźnika> = &<nazwa_zmiennej>; Przykład 1. Wskaźnik px ma wartość adresu zmiennej x, czyli &x. int x = 0; int *px = &x; Definicja. Sterta (heap) obszar pamięci służący do dynamicznego alokowania pamięci. Dynamiczne alokowanie pamięci odbywa się za pomocą operatorów new, delete. Definicja wskaźnika na stercie. <typ> *<nazwa_wskaźnika> = new <konstruktor typu>; //... delete <nazwa_wskaźnika>; Przykład 2. Wskaźnik na stercie. int *p = new int; *p = 123; //... delete p; Własności: 1. Wskaźnikowi można zmienić wartość, czyli przypisać adres innej zmiennej. 2. Poprzez wskaźnik można zmienić wartość zmiennej na który wskazuje. 3. Referencję można inicjować wskaźnikiem. 4. Wskaźnik można inicjować referencją. Przykład 3. Zmiana wartości i przypisania wskaźnika. int x = 100; int y = 200; int *p = &x; *p = 101; // jaka jest wartość x? p = &y; // jaka jest wartość *p? 5
Odpowiedź. cout << x << endl; cout << *p << endl; 101 200 Przykład 4. Inicjowanie wskaźnika referencją i referencji wskaźnikiem. int x = 100; int *p = &x; int &rx= *p; int *pp = ℞ Przykład 5. Inicjowanie wskaźnika innym wskaźnikiem - placement new, (w01-02-placement-new.cpp). int * p1 = new int(0); void main() int *p2= new (p1)int(200); // placement new // int *p2= new int(200); cout << "*p1 = " << *p1 << endl; cout << "*p2 = " << *p2 << endl; cout << " p1 = " <<(long)p1 << endl; cout << " p2 = " <<(long)p2 << endl; cout << "&p1 = " <<(long)&p1 << endl; cout << "&p2 = " <<(long)&p2 << endl; delete p1; delete p2; *p1 = 200 *p2 = 200 p1 = 15049368 p2 = 15049368 &p1 = 13452336 &p2 = 13237988 Przykład 6. Inicjowanie wskaźnika stałym obiektem o wartości 0. const int cx = 0; int *pp = cx; 6
Przykład 7. Inicjowanie wskaźnika adresem zmiennej o typie wskaźnika. double x = 0; int *p = &x; // błąd unsigned double x = 99; double *p = &x; // błąd double x = 99; const double *p = &x; // ok 7
1.5 Referencje Programowanie 2. Język C++. Wykład 1. Definicja. Referencja jest typem danych który przechowuje adres zmiennej i której nie można zmienić przypisania Referencja jest stałym wskaźnikim do zmiennej, którego adres pokrywa się z adresem zmiennej. Referencja nie zajmuje pamięci. <typ> &<nazwa_referencji> = <nazwa_zmiennej>; Przykład 1. Definicja referencji. int x = 0; int &rx = x; Własności: 1. Referencja musi być przypisana do zmiennej (musi być zainicjowana). 2. Referencji nie można przypisać wartości NULL. 3. Referencji nie można zmienić przypisania. 4. Referencji można przypisać wartość. 5. Referencja musi być zainicjowana takim samym typem jaki jest typ referencji. Referencja typu const może być zainicjowana podobnym typem do typu referencji. Przykład 2. Do pkt.5. const int x = 321; int &r = x; // error C2440: 'initializing' : cannot // convert from 'const int' to 'int &' const int &r = x; // ok int x = 321; const int &r = x; //ok const int &rr = 100; //ok 8
Przykład 3. Użycie referencji (w01-03-referencje.cpp). #include<iostream> void main() int i = 10; int &ri = i; cout <<" i = " << i << endl; cout <<" ri = " << ri << endl; cout <<" &i = " << (long)&i << endl; cout <<"&ri = " << (long)&ri << endl; i = 100; cout <<" ri = " << ri <<endl; ri = 50; cout <<" i = " << i <<endl; const int &cr = 100; cout <<" rc = " << cr <<endl; cout <<"&rc = " << &cr <<endl; cout <<"&rc = " << (long)&cr <<endl; i = 10 ri = 10 &i = 16775472 &ri = 16775472 ri = 100 i = 50 rc = 100 &rc = 00FFF90C &rc = 16775436 9
Przykład 4. Przypisanie zmiennej x wartości zmiennej y. Adres zmiennych x, y nie zmienia się (w01-04- referencje.cpp). void main () int x = 321; int y = 123; int &r = x; r = y; cout << x << endl; cout << &x << endl; cout << &r << endl; cout << &y << endl; 123 00B9FA38 00B9FA38 00B9FA34 10
1.6 Wskaźniki do wskaźników <typ> **<nazwa_wskaźnika_do_wskaźnika> = &< nazwa_wskaźnika>; Przykład 1. Wskaźnik ppx ma wartość adresu wskaźnika px, czyli wartość &px. int x = 0; int *px = &x; int **ppx = &px; Przykład 2. Wskaźnik do wskaźnika na stosie (w01-05-wskaznik2wskaznik-stos.cpp). #include<iostream> int main() int x = 0; int *px = &x; int **ppx = &px; cout << "x = " << x << endl; cout << "**ppx = " << **ppx << endl; cout << "*px = " << *px << endl << endl; cout << "&x = " << &x << endl; cout << "px = " << px << endl; cout << "&**ppx = " << &**ppx << endl; cout << "&*px = " << &*px << endl << endl; cout << "*ppx = " << *ppx << endl; cout << "&px = " << &px << endl; cout << "ppx = " << ppx << endl << endl; cout << "&ppx = " << &ppx << endl; return 0; x = 0 **ppx = 0 *px = 0 &x = 006DFB04 px = 006DFB04 &**ppx = 006DFB04 &*px = 006DFB04 *ppx = 006DFB04 &px ppx &ppx = 006DFAF8 = 006DFAF8 = 006DFAEC 11
Przykład 3. Wskaźnik do wskaźnika na stercie (w01-06-wskaznik2wskaznik-sterta.cpp). void main( ) int ** pp = new int *; *pp = new int; **pp = 100; cout << " **pp = " << **pp << endl; cout << "&**pp = " << &**pp << endl; cout << " *pp = " << *pp << endl; cout << " &*pp = " << &*pp << endl; cout << " pp = " << pp << endl; cout << " &pp = " << &pp << endl; delete *pp; delete pp; **pp = 100 &**pp = 0109A378 *pp = 0109A378 &*pp = 0109A328 pp = 0109A328 &pp = 00C5F804 Przykład 4. Wskaźnik do wskaźnika, funkcja main() (w01-07-main.cpp). Program należy uruchomić z konsoli z kilkoma parametrami, np. \>nazwa_programu.exe aa bb cc dd ee #include<iostream> void main(int argc, char **argv) int i = 0; for(i=0; i<argc; i++) cout << "argv:" << i << "= " << argv[i] << endl; // to samo co wyżej inaczej for(i=0; i<argc; i++, argv++) cout << "argv:" << i << "= " << *argv << endl; 12
Przykład 5. Wskaźnik do referencji (w01-08-wsk-ref.cpp). void main() char *pc = 0; pc = "abcdef"; char &rc = *pc; cout <<"pc= " << pc << endl; cout <<"rc= " << rc << endl; cout <<"rc= " << ++rc << endl; rc+=2; cout <<"rc= " << rc << endl; pc= abcdef rc= a rc= b rc= c Przykład 6. Wskaźnik void nie wskazuje na żaden typ (w01-09-wskaznik-void.cpp). void main() int i = int(10); // konstruktor typu int, inicjuje zmienną nadając wartość 10 int *pi = 0; pi = &i; cout << " i =" << i << endl; cout << "*pi =" << *pi << endl; void *pv = pi; cout << " pv =" << pv << endl; cout << " pi =" << pi << endl; double d = 1.1; double *pd = &d; pv = pd; cout << " pv =" << pv << endl; cout << " pd =" << pd << endl; double **ppd = &pd; 13
Przykład 7. Wskaźnik typu double (w01-10-wsk2wsk-double.cpp). void main () double x = 321; double *px = &x; double &rx = x; double ** ppx = &px; cout << x << endl; cout << rx << endl; cout << *px << endl; cout << **ppx << endl << endl; cout << &x << endl; cout << &rx << endl; cout << px << endl; cout << &px << endl; cout << ppx << endl; cout << &ppx << endl; cout << *ppx << endl<< endl;. 321 321 321 321 005BFEB8 005BFEB8 005BFEB8 005BFEB0 005BFEB0 005BFEB4 005BFEB8 8 8 4 4 endl; cout << sizeof(x) << endl; cout << sizeof(rx) << endl; cout << sizeof(px) << endl; cout << sizeof(ppx) << endl<< 14
Przykład 8. Tablica int, wskaźnik do tablicy (w01-11-wsk2tablicy.cpp). #include<iostream> void main() const int wt = 3; int tab[wt] = 22, 33, 44; cout << "&tab cout << "tab cout << "*tab = " << &tab << endl; = " << tab << endl; = " << *tab << endl; cout << "tab[0] = " << tab[0] << " adres " << &tab[0] << endl; cout << "tab[1] = " << tab[1] << " adres " << &tab[1] << endl; cout << "tab[2] = " << tab[2] << " adres " << &tab[2] << endl << endl; int *ptab = &tab[0]; cout << "&ptab = " << &ptab << endl; cout << "ptab = " << ptab << endl; cout << "*ptab = " << *ptab << endl; cout << "++ptab = " << ++ptab << endl; cout << "*ptab = " << *ptab << endl; cout << "++ptab = " << ++ptab << endl; cout << "*ptab = " << *ptab << endl; &tab = 0030F900 tab = 0030F900 *tab = 22 tab[0] = 22 adres 0030F900 tab[1] = 33 adres 0030F904 tab[2] = 44 adres 0030F908 &ptab = 0030F8FC ptab = 0030F900 *ptab = 22 ++ptab = 0030F904 *ptab = 33 ++ptab = 0030F908 *ptab = 44 15
Przykład 9. Zmienna typu char, referencja, wskaźniki. 16
1.7 Definiowanie własnych typów danych, polecenie typedef Deklaracja nowego typu zmiennej. typedef <typ> <nazwa_typu>; Przykład 1. Definiowanie typu const unsigned int( w01-12-typedef.cpp). typedef const unsigned int cui; void main () cui x = 2; 17
1.8 Typ wyliczeniowy enum enum <nazwatypu> <elementlisty1[=wartość],, <elementlistyn[=wartość]> ; Własności: 1. Domyślna wartość pierwszego elementu jest 0, następnego 1, itd.. 2. Typ wyliczeniowy enum jest typu const. 3. Typ wyliczeniowy enum można zagnieżdżać w klasach. Przykład 1. Deklaracje typu wyliczeniowego. enum DzienTygodnia Ni, Po, Wt, Sr, Czw, Pi, So ; enum boool falsz, prawda ; Przykład 2. Przypiasanie zmiennej wartości typu DzienTygodnia. enum DzienTygodnia Ni, Po, Wt, Sr, Czw, Pi, So ; int x = Czw; DzienTygodnia dzien; dzien = Sr; DzienTygodnia dzien1 = Po; DzienTygodnia dzien2; dzien2 = dzien1; Przykład 3. Typ wyliczeniowy enum (w01-13a-enum.cpp). enum DzienTygodnia Ni, Po, Wt, Sr, Czw, Pi, So ; void main() DzienTygodnia dzien; int x = 0; // dzien = x; //blad dzien = DzienTygodnia(x); if (dzien == Ni dzien == So) cout << "dzien wolny" << endl; else cout << DzienTygodnia(x) << endl; 18
Przykład 4. Numerowanie elementów tablicy za pomocą typy wyliczeniowego enum (w01-13b-enum.cpp). void main() enum Dni Niedziela, Poniedzialek, Wtorek, Sroda, Czwartek, Piatek, Sobota, LiczbaDni ; int Tab[LiczbaDni] = 11, 13, 15, 17, 19, 21, 23; cout << "Tab[Niedziela] = " << Tab[Niedziela] << endl; cout << "Tab[Wtorek] = " << Tab[Wtorek] << endl; cout << "LiczbaDni = " << LiczbaDni << endl; Tab[Niedziela] = 11 Tab[Wtorek] = 15 LiczbaDni = 7 19
1.9 Obszary nazw namespace <nazwa_obszaru> <deklaracje elementów obszaru nazw> Przykład 1.Definicja obszaru nazw. namespace obszn1 Własności: 1 Obszary nazw można zagnieżdżać, 2 Do deklaracji użycia obszaru nazw stosuje się dyrektywę using, np. using namespace <nazwa_obszaru>; 4. Dostęp do elementów obszaru nazw można uzyskać za pomocą operatora zakresu :: 5. Nazwa zadeklarowana poza obszarem nazw, blokiem, klasą ma zakres globalny. 6. Obszar nazw może nie mieć nazwy. Przykład 2. Dostęp do elementów obszaru nazw (w01-14-namespace.cpp). //int iliczba = 9; // zasłania iliczba = 10 namespace int iliczba = 10; namespace ObszN2 int iliczba = 100; namespace ObszN3 int iliczba = 1000; void main( ) int iliczba = 1; cout << iliczba << endl; cout << ::iliczba << endl; cout << ObszN2::iLiczba << endl; cout << ObszN2::ObszN3::iLiczba << endl; 1 10 //gdy int iliczba = 9; odkomentowana to widzimy 9 100 1000 20
1.10 Zakres zmiennych Przykład 1. Zakres zmiennych (w01-15-scope.cpp). int n = 0; void main() int n1 = 1; cout <<"n = " << n <<endl; int n2 = 2; int n3 = 3; cout <<"n = " << n <<endl; cout <<"n1 = " << n1 <<endl; cout <<"n2 = " << n2 <<endl; cout <<"n3 = " << n3 <<endl; // error C2065: 'n3' : undeclared identifier cout <<"n2 = " << n2 <<endl; // error C2065: 'n2' : undeclared identifier n = 0 n = 0 n1 = 1 n2 = 2 21
1.11 Zmienne extern Deklaracja zmiennej typu extern, deklarowana zmienna jest już zdefiniowana w innym pliku. extern <typ> <nazwa_zmiennej>; Przykład 1. Deklaracja extern. extern int ig; Przykład 2. Użycie deklaracji extern (w01-16a-extern.zip). Plik ca.cpp Plik ca1.cpp void f(); void g(); int i = 30; void main() int ii = 50; f(); g(); // int i = 700; // błąd extern int i; int ii = 100; void f() cout << "i = " << i << endl; void g() cout << "ii = " << ii << endl; Zobaczymy: i = 30 ii = 100 Przykład 3. Użycie deklaracji extern (w01-16b-extern.zip). Jaki jest wynik działania programu? Plik ca.cpp Plik ca.h #include "ca.h" int ix = NULL; void main() int ix = 12; cout << ix << endl; cout << ::ix << endl; #ifndef _ca_h_ #define _ca_h_ extern int ix; void f() ix = 47; cout << "f(), ix = " << ix << endl; #endif _ca_h_ f(); cout << ix << endl; cout << ::ix << endl; Odp. 12 0 f(), ix = 47 12 47 22
1.12 Obszary pamięci Stos, sterta, obszar rejestrów, obszar kodu. Obszar zmiennych globalnych jest obszarem nazw, nie obszarem w pamięci ram. Przykład. int g = 100; // zmienne globalna, globalny obszar nazw // blad, zmienna register nie moze byc zmienna globalną // register int reg2 = 99; void main() int s = 300; // zmienna na stosie int * ps = &s; // wskaznik na stosie register int reg = 400; // zmienna w obszarze rejestrow cout << "&g = " << (long)&g << endl; cout << "&s = " << (long)&s << endl; cout << "&ps = " << (long)&ps << endl; cout << "® = " << (long)® << endl; int *p = new int(500); // zmienna na heapie (sterta) cout << "p = " << (long)p << endl; cout << "&p = " << (long)&p << endl; delete p; ps = new int(600); cout << "ps = " << (long)ps << endl; cout << "&ps = " << (long)&ps << endl; delete ps; &g = 2638036 &s = 3734504 &ps = 3734496 ® = 3734508 p = 6726312 &p = 3734500 ps = 6726152 &ps = 3734496 23