kurs c/c++ wykład 4
Transkrypt
kurs c/c++ wykład 4
KURS C/C++ WYKŁAD 4 1. Typy zmiennych 2. Typy podstawowe 3. Stałe 3.1. Stałe całkowite 3.2. Stałe zmiennoprzecinkowe 3.2. Stałe znakowe 3.4. Stałe tekstowe 4. Typy pochodne 4.2. Typ void 4.2. Tablice 4.2.1. Jednowymiarowe 4.2.2. Wielowymiarowe 4.2.3. Znakowe 1. Typy zmiennych. Język C++ dostarcza dwa rodzaje typów: a) typy podstawowe, b) typy pochodne, wyprowadzane z typów podstawowych za pomocą operatorów deklaracji i mechanizmu deklarownia struktury, Z każdą nazwą (identyfiaktorem) jest związany typ. Typ zmiennej określa jakie operacje mogą być wykonywane na zmiennej danego typu i jak te operacje są intrerpretowane. Np; float zmienna; Oznacza to że jest to zmienna numeryczna i może być ona używana np. w wyrażeniach arytmetycznych, można jej nadawać wartości itp. 2. Typy podstawowe: Typami podstawowymi są: char short int int long int do reprezentownia zmiennych całkowitych różnych rozmiarów, float double long double do reprezentownia zmiennych zmiennopozycyjnych unsigned char unsigned short int unsigned int unsigned long int do reprezentownia liczb całkowitych bez znaku, wartości logicznych, tablic bitowych itp. signed char signed short int signed int signed long int do jawnego reprezentowania zmiennych ze znakiem. Typ int i char są typami całkowitymi. Zmienne całkowite mogą występować w dwóch wariantach: ze znakiem (signed) lub bez znaku(unsigned). Np.: signed int lub unsigned int. ˇ Wyposażenie typu w znak: signed int, sprawia, że może on reprezentować liczbę ujemną i dodatnią. * Typ bez znaku unsigned int reprezentuje liczbę niejemną. * Przez domniemanie przyjmuje się, że zapis int a; oznacza typ signed int, czyli liczbę ze znakiem. * Dla typu char sprawa nie jest taka prosta. * Od sprzętu zależy ilość bajtów pamięci przeznaczonej na przechowanie danego typu podstawowego, czyli długość typu. Standart języka zapewnia, iż typ char jest dostatecznie duży, aby pomieścić dowolny elemnt podstawowego zbioru znaków implementacji. Jest to zwykle ośmiobitowy bajt. Typy signed char , unsigned char i char są traktowane jako oddzielne typy, choć zajmują tyle samo miejsca w pamięci. Standard gwarantuje że: 1 ≡ sizeof(char)<=sizeof(short) <=sizeof(int)<=sizeof(long) sizeof(float)<=sizeof(double) <=sizeof(long double) sizeof(I) ≡ sizeof(signed I) ≡ sizeof(unsigned I) gdzie I: może byc typem: char, short, int lub long Liczby całkowite mają rozmiar naturalny dla architektury danej maszyny. Poniżej przedstawione wartości dotyczą implementacji dostępnej w laboratorium: (dla pakietu Borland 3.1): char (ze znakiem) - 1 bajty -128 do 127 unsigned char - 1 bajt 0 do 255 unsigned short - 2 bajt 0 do 65535 signed short - 2 bajty -32768 do 32767 unsigned int - 2 bajty 0 do 65535 signed int - 2 bajty -32768 do 32767 unsigned long int - 4 bajty 0 do 4294967295 signed long int - 4 bajty 2147483648 do 2147483647 float - 4 bajty 3.4 E- 38 do 3.4e+38 double - 8 bajtów 1.7 E-308 do 1.7 E+308 long double - 10 bajtów 3.4 E-4932 do1.1E+4932 Największe i najmniejsze wartości każdego typu dla danej implementacji są podane w standardowym pliku nagłówkowym limits.h. Przy wyborze typu należy pamiętać, że za lepszą dokładność płaci się dłuższym czasem obliczeń. 3. Literały (inaczej zwane stałymi) W programach często posługujemy się stałymi, możemy używać: - stałych całkowitych - stałych zmiennopozycyjnych - stałych znakowych - literałów napisowych Przykład: x = 10.53; i = i+5; if(m>12) 3.1. Stałe całkowite - dziesiętne np. 12, -100 (bez znakow wiodących) - ósemkowe: jeśli zapis stałej zaczniemy od cyfry 0 np: 014 014 dziesiętnie: 4*80 + 1*81= 12 - szesnastkowe: jeśli zapis stałej zaczniemy od cyfry 0x lub 0X np: 0x10 lub 0X10 Do reprezentowania 10, 11, 12, 13, 14, 15, 16 używa się liter a,b,c,d,e,f lub ich dużych odpowiedników. 0xA1 dziesiętnie: 1*160 + 10*161= 161 Stałe całkowite traktowane są jako int chyba, że ze względu na ich rozmiar trzeba je oznaczyć jako long. Wtedy dopisujemy na końcu literę L (lub l) np: 0L, 200L. Jeśli chcemy, żeby stała była typu unsigned dopisujemy na końcu literę u np:277u. Może ona występować razem z L np: 50 uL, czyli stała ma być traktowana jako unsigned long. #include<stdio.h> main(){ int i, j, k, n, m; i = 5; k = i + 010; printf ("k=%d",k); //5+8 m = 100; //100 n = 0x100; //256 j = 0100; //64 j=23; printf ("%#x %o %d", j, j, j); } 3.2. Stałe zmiennoprzecinkowe //0x17 27 23 Można je zapisać na dwa sposoby: - normalny zapis liczby z kropką np: 17.89 - 12. - tzw. notacja naukowa. W zapisie tym występuje litera e, po której występuje wykładnik potęgi o podstawie 10, a więc: 8 e 2 = 8 * 102 10.4 e 8 = 10.4 * 108 10.4e -8 = 10.4 * 108 Stałe te traktowane są jako double, chyba że typ określimy przy pomocy przyrostka: f, F - float, l, L - long double. Część całkowitą i ułamkową można pominąć, ale nie obie naraz. Kropkę lub część wykladniczą z literą e(E) można pominąć, ale nie obie naraz. double x = 1.2e2; x= 12e2; x=.22e2 printf (″%e″, x); // ekran: 1.200000e+02 3.3. Stałe znakowe Są to stałe reprezentujące np. znaki alfanumeryczne. Zapisuje się je ujmując dany znak w znaki apostrofu. Pojedyncze stałe znakowe są typu char: 'a' - oznacza literę a '7' - oznacza cyfre 7 (CYFRĘ a NIE LICZBĘ) przykład deklaracji i użycie stałej znakowej: char znak; znak = 'n'; Wartością stałej znakowej jest wartość kodu znaku !!! Są jednak znaki, których nie można bezpośrednio umiecić między apostrofami. Służą one do sterowania wypisywanym tekstem. Są to znaki nie mające reprezentacji graficznej. '\b' - cofacz(Bsp ) '\v' - tabulator pionowy '\f' - nowa strona '\a' - sygnał alarmowy '\n' - nowa linia -NL(LF) '\r' - powrót karetki '\t' - tabulator poziomy '\\' � bekslesz '\' ' - apostrof '\" ' - cudzysłów '\?' = pytajnik. '\0' - NULL specjalny znak o kodzie 0 \ooo - liczba ósemkowa (oktalnia) \xhh - liczba szesnastkowa (heksadecymalna) Mimo, że widzimy kilka znaków zapisy te reprezentują tylko jeden znak. Typem stałej znakowej jest znak. Stałe znakowe można podawać bezpośrednio w znakach apostrofu lub podając kod stałej też w znakach apostrofu w postaci liczby ósemkowej lub heksadecymalnej. oktalnie: heksadecymalnie: 'k' -> '\153' char znak='k'; 'k' -> '\x6B� lub znak= '\153'; lub znak= '\x6B'; printf (″%c″, znak); Ekran: k Używanie notacji numerycznej do reprezentacji stałych znakowych ogranicza przenośność oprogramowania, że względu na możliwość innego kodowania znaków niż ASCII. 3.4. Stałe tekstowe (stringi) Jest to ciąg znaków ujęty w cudzysłów. Stringi w pamięci przechowywane są jako ciąg znaków zakończonych znakiem NULL czyli znakiem o wartości!!! 0. "arbuz" sizeof ( "arbuz") = 6 znaków w napisie: napis "arbuz" ma 6 znaków, czyli o jeden znak więcej niż ilość znaki z tekstu "arbuz" + miejsce na NULL. "" sizeof ("") = 1 napis pusty zawiera 1 znak jest to znak NULL Stała 'x' nie oznacza tego samego co "x" - pierwsza reprezentuje tylko jeden znak x, druga jednoznakowy łańcuch zawierający znak x oraz znak końca stringu �\0� !!! Wewnątrz napisu można reprezentować cudzysłów ,bekslash, apostrof. należy te znaki poprzedzić znakiem bckslash�a. cout<<"witamy w \"C++\"); printf ("witamy w \"C++\"); Wewnątrz napisu można również umieszczać znak �\0�, lecz większość funkcji nie spodziewa się tego znaku wewnątrz. printf("Pomarańczowa \0 Alternatywa"); na ekranie będzie wyświetlone tylko słowo Pomarańczowa Jeśli string nie mieści się w jednej linii, wtedy możemy postąpić nastepująco: cout<<"To jest" << " przykład dobrego łamania stringu"; cout<<"To jest" " przykład dobrego łamania stringu" kompilator łączy dwa sąsiadujące ze sobą stringi. cout<<"To jest przykład błędnego łamania stringu" lub: printf ("To jest" " przykład dobrego łamania stringu"; printf ("To jest przykład błędnego łamania stringu"); 4. Typy pochodne Istnieje nieskończenie wiele typów pochodnych konstruowanych z typów podstawowych mogą to być: tablice, funkcje, wskaźniki, referencje, stałe (const), klasy, struktury, unie. Typy pochodne można wyprowadzać z typów podstawowych za pomocą operatorów deklaracji: [] - operator indeksowania pozwala tworzyć tablicę obiektów danego typu. * - wskaźnik do pokazywania na obiekt danego typu. którym można umieścić adres jakiegoś obiektu. () - operator do deklarowania funkcji zwracającej wartość danego typu. & - referencja obiektu danego typu. Oznacza inną nazwę obiektu. Dzięki referencji na tę samą zmienną można mówić używając jego drugiej nazwy. oraz mechanizmu definiowania struktury. * & - operatory przedrostkowe [] () - operatory przyrostkowe Przykłady typów pochodnych: int tab[10]; float *p; int *wsk char func (); Wskaźnik jest obiektem, w int i; int &refer=i; 4.1. Typ void Typ void specyfikuje pusty zbiór wartości. Typu void można jedynie użyć jako części typu pochodnego. Nie ma obiektów typu void. Stosuje się go jako typ wartości funkcji, która nie przekazuje na zewnątrz żadnej wartości, oraz jako wskaźnika do obiektu nieznanego typu. void f (); - funkcja f () nie przekazuje żadnej wartości void *wsk; - wskaźnik do obiektu nieznanego typu. Można dokonać jawnej konwersji każdego wyrażenia do typu void. 4.2. Tablice 4.2.1. Tablice jednowymiarowe Jest to ciąg obiektów tego samego typu, zajmujących ciągły obszar w pamięci. Zamiast odnosić się do każdej zmiennej z osobna, odnosimy się do n-tego elementu tablicy. Rozmiar tablicy musi być wartością stałą, znaną już na etapie kompilacji. Jeśli jednak zachodzi konieczność pracy z tablicą o rozmiarze ustalanym w czasie działania programu , to wtedy realizujemy proces dynamicznej alokacji pamięci, który zostanie pokazany przy omawianiu wskaźników. Tablice można tworzyć z: - typów podstawowych - typów wyliczeniowych - wskaźników - innych tablic - z klas - ze wskaźników do pokazywania na składniki klasy. Tablice nie mogą być ciągiem referencji. Jeśli zadeklarowaliśmy tablicę: float tablica[rozmiar]; to elementy tablicy numerowane są od 0 do rozmiar �1 Deklarujemy tablicę: float tablica[3]; to jest to zbiór trzech elementów typu float: tablica[0], tablica[1], tablica[2]. Uwaga: Instrukcja tablica[3]=90 nie będzie sygnalizowana jako błąd, gdyż język C/C++ nie sprawdza przekroczenia zakresu tablic. Zapis taki powoduje zniszczenie obiektu zapisanego bezpośrednio za tablicą i w konsekwencji błędy wykonania programu. Nadanie wartości elementom tablicy może odbywać się: - w trakcie działania programu: Przykład 1 #include <stdio.h> void main (){ int tab [10], i; for( i = 0;i<10;i++) tab[i]= 2*i -1; for( i = 0;i<10;i++) printf (″\ntab[%d] = %d″, i, tab[i]) ; } Przykład 2 #include <stdlib.h> #include <iostream.h> void main (){ const int r =10; int tab[r], los; randomize(); for( i = 0; i<r; i++){ tab[i] = random(70); cout<<″\ntab[″<<i<<″] =″<<tab[i] ; } } - podczas deklaracji tablicy: należy inicjalizować każdy element z osobna np.: int tab[3] = {1,2,3}; umieszczenie w nawiasie większej ilości liczb niż wynika to z deklaracji int tab[3] = {1,2,3,4}; generuje błąd, kompilator sprawdza czy podczas inicjalizacji nie został przekroczony zakres. Jeżeli elementów jest mniej, brakujące elementy uzupełniane są zerami: int tab[4] = {1,2} => int tab[4] = {1,2,0,0} lub nie podając rozmiaru tablicy: int tab[] = {1,2,3}; 4.2.2. Tablice wielowymiarowe int tab[3][2]; Tablice wielowymiarowe są reprezentowane jako tablice tablic. Odwołania do tablicy tab przy pomocy indeksowania: tab[0][0], tab[0][1], tab[1][0], tab[1][1], tab[2][0], tab[2][1]. Używanie notacji z przecinkiem takiej jaka jest stosowana w innych językach (Pascal), powoduje błędy kompilacji: int tab[5,2]; int tab[5][2]; // błąd // poprawnie int zle = tab[5,2]; // błąd int dobrze = tab[4][1]; Podobnie jak dla tablic jednowymiarowych można je inicjalizować: tab[3][2] = {10, 11, 20, 21, 30, 31}; tab[0][0] tab[1][0] tab[2][0] tab[0][1] tab[1][1] tab[2][1] Przy inicjalizacji tablic dwuwymiarowych konieczne jest podanie co najmniej jednego wymiaru, jest nim ilość elementów w wierszu: tab[][] = {0,1, 10,11, 20,21, 30,31}; //źle tab[3][] = {0,1, 10,11, 20,21, 30,31}; //źle tab[][2] = {0,1, 10,11, 20,21, 30,31}; //dobrze, potrafi ustalić rozmiar {0,1, 10,11, 20,21, 30,31, 40};//dobrze, potrafi ustalić rozmiar tab[][2] = Uzupełnianie brakujących elementów tablic odbywa się wg. zasad dla tablic jednowymiarowych Przykład zastosowania obu typów tablic przedstawia program obliczania sumy elementów wierszy macierzy 5x5. #include <stdio.h> #include <conio.h> void main (){ const int r=5; int A[r][r], B[r]; int i,j; clrscr(); for (i = 0; i<r;i++) for(j = 0; j<r; j++) A[i][j]=1; /* sumowanie wierszy */ for (i = 0; i<r;i++) { B[i]=0; for(j = 0; j<r; j++) B[i] += A[i][j]; } /* wyświetlanie wyników zapisanych w tablicy jednowymiarowej */ for(i = 0; i<r; i++) cout << B[i]<< "\n"; getch(); } 4.2.3. Tablice znakowe Specjalnym rodzajem tablic są tablice znakowe. Deklaracja takiej tablicy: char zdanie[50]; W tablicach takich można przechowywać tekst. Nazwa tablicy jest adresem jej pierwszego elementu w pamięci. Podobnie jak inne tablice, tablica znakowa może być w trakcie deklaracji inicjalizowana: (a) char zdanie [12] = "ala ma kota"; lub char zdanie [] = "ala ma kota"; Teksty w tablicach przechowywane są tak, że po ciągu znaków następuje znak o kodzie 0 tzw. NULL. Znak NULL został zapisany automatycznie, ponieważ przy inicjalizacji tablicy ciąg znaków ograniczyliśmy znakami cudzysłowia. Ciąg znaków zakończony NULL'em nazywamy łańcuchem (stringiem). W przypadku gdy nie określono rozmiaru tablicy, rozmiar wynosi: sizeof (zdanie)= ilość znaków + NULL. deklarujemy: char zdanie[20]; Iinicjalizacja tablicy w następujący sposób jest błędna: zdanie = ″ala ma kota″; Tak poprawnie powinna być inicjalizowana tablica: char zdanie[] = ″ala ma kota″; lub z ustalonym rozmiarem co najmniej 12, takim żeby można było umiścić wszystkie znaki tekstu + NULL. Np.: char zdanie[15] = ″ala ma kota″; Przykłady: char t1[1] = "a"; //błąd char t1[2] = "a"; //poprawnie char t2[1] = {'a'}; //poprawnie Która z deklaracji jest poprawna?. Pierwsza nie, bo tablica ma być jednoelementowa, a string "a" to string o długości 2 (litera a + NULL). Powstała specjalna biblioteka <string.h> zawierająca podstawowe funkcje obsługujące łańcuchy: kopiowanie , wycinanie, poszukiwanie znaku itd. char s[]=″Politechnika″; //ze znakiem NULL int dl= strlen (s); //strlen () � funkcja obliczana długość łańcucha, w oparciu o // o znak NULL, a przy takiej inicjalizacji brak tego znaku dl=12, długość łańcucha jest liczona bez znaku NULL char nazwa[30]; strcpy (nazwa, ″Informatyka″); funkcja strcpy() skopiuje do tablicy nazwa[] łańcuch Informatyka, kopiowanie zostaje zakończone po napotkaniu znaku NULL. Można inicjalizować: (1) char s[]={�P�,�o�,�l�,�i�,�t�,�e�,�c�,�h�,�n�,�i�,�k�,�a�,�\0�} (2) char s[]={�P�,�o�,�l�,�i�,�t�,�e�,�c�,�h�,�n�,�i�,�k�,�a�}; znaku NULL s [0]=�P�, s [1]=�o�, s[2]=�l� ,...., dla (1) i (2) można sprawdzić czy: if (s [0] = = �X�) if (isalpha (s[0]) czylitera=1; else czylitera=0; if (czylitera) printf (″znak jest literą″); chcemy przejrzeć cały tekst: (1) dl=strlen (s); //dl=12, tablica 0..11 for (i=0; i<dl; i++) if (!isalpha (s[i]) break; if (i = = dl) printf (″wszystkie znaki to litery″); else printf (na %d pozycji tablicy znak nie jest literą″, i); (2) dl=strlen (s); //błąd, brak znaku NULL for (i=0; i<?; i++) const r=5; char zestaw[r]={�a�, �e�,�i�,�o�,�u�}; znak=getch(); for (i=0; i<r; i++) if (znak= =zestaw[i]) {printf (″litera jest samogloska″); break;} //bez