background image

 
 
 
 
prof. Jan Bielecki 

 

 
 
 
 
 
 

Visual C++ 6.0 
Podstawy programowania 

 
 
 

1. 

Pierwsze kroki 

2. 

Ś rodowisko Visual C++ 

3. 

Wskaźniki i odnośniki 

4. 

Przetwarzanie łań cuchów 

5. 

Posługiwanie się  funkcjami 

6. 

Zarzą dzanie pamię cią  

7. 

Widoczność deklaracji 

8. 

Studia programowe 

 
 

Dodatki 

 

 

Priorytety operatorów 

 

Opracowywanie wyrażeń  

 

Konwersje standardowe 

 

Operatory bitowe i warunkowe 

 

Operacje wejścia-wyjścia 

 

 

 

background image

 

1

Pierwsze kroki 

 
 
 
 
Program  jest  zbiorem  modułów  źródłowych.  Każ dy  moduł  składa  się  z  deklaracji typów,  zmiennych  funkcji
Napis  od  znakó w  //  do  końca  wiersza  jest  komentarzem.  Jako  taki  nie  ma  wpływu  na  przebieg  wykonania 
programu. 
 
Dokładnie jeden moduł, nazywany  głównym, zawiera deklarację funkcji main. Wykonanie programu polega na 
opracowaniu  wszystkich  jego  globalnych  deklaracji,  a  następnie  przystą pieniu  do  wykonywania  instrukcji 
zawartych  w  funkcji  main.  Zakończenie  wykonywania  programu  następuje  po  wykonaniu  w  funkcji  głó wnej 
instrukcji return, albo tuż  po powrocie z funkcji exit. Moż e to nastą pić  jeszcze przed podjęciem wykonywania 
funkcji głó wnej. 
 

int main(void)      // deklaracja funkcji g

łównej 


    return 0;       // instrukcja return 

 
void exit(int);     // deklaracja funkcji exit 
 
struct Empty {      // deklaracja typu Empty 
    Empty(void) 
    { 
        exit(0);    // wywo

łanie funkcji exit 

    } 
}; 
 
Empty obj;          // deklaracja zmiennej 

 
 
Program  napisano  w  taki  sposó b,  aby  jego  wykonanie  zakończył

o  się przed  podjęciem  wykonywania  funkcji 

ó wnej. 

 
 

Komunikacja z otoczeniem 

 
W  najprostszym  przypadku,  program  pobiera  dane  z  klawiatury  i  wyprowadza  je  na  monitor.  Operacje 
wprowadzania  danych  odbywają   się  za  pomocą   operatora  >>,  a  operacje  wyprowadzania  danych  za  pomocą  
operatora <<. Klawiatura jest reprezentowana przez zmienną  cin, a monitor przez zmienną  cout. Posłuż enie się 
nimi wymaga uż ycia dyrektywy #include wyszczegó lniają cej nazwę iostream.h
 
Daną   wprowadzoną   z  klawiatury  kończy  odstę p,  uzyskany  przez  naciśnięcie  klawisza  Space,  Tab  albo  Enter
Analiza  danych  wejściowych  następuje  wierszami,  to  jest  dopiero  po  naciśnięciu  klawisza  Enter.  W 
szczegó lności,  jeśli  program  oczekuje  3  danych,  to  każ dą   moż na  podać   w  osobnym  wierszu,  albo  wszystkie 
podać  w jednym wierszu. Przed wprowadzeniem kolejnej danej pomija się poprzedzają ce ją  odstępy. 
 
Uwaga: Wygodnym sposobem wyprowadzenia odstępu Enter jest uż ycie symbolu endl, a wygodnym sposobem 
wyprowadzenia znaku o kodzie 0 jest uż ycie symbolu ends
 
Ponieważ  operacja wejścia-wyjścia dostarcza w miejscu jej uż ycia jej lewy argument, więc zapis pary instrukcji 
 
cin >> one; 
cin >> two; 
 
moż na uprościć  do 
 

background image

 

2

cin >> one >> two; 
 
Właściwość  tę, nazywaną  łą czeniem operacji, moż na zastosować  takż e do wyprowadzania danych. 
 
 

#include <iostream.h> 
 
int main(void) 

    int one, two; 
    cout << "Enter 2 numbers:" << endl; 
    cin >> one >> two; 
    cout << "Sum = " << one + two << endl; 
 
    return 0; 

 
Program wyprowadza zachętę do wprowadzenia 2 liczb, a następnie wyznacza i wyprowadza ich sumę. 
 
 

Wykonywanie operacji 

 
Wykonanie programu sprowadza się do wykonania operacji na danych. W Dodatku A zamieszczono kompletny 
wykaz operacji, a w  Dodatku B omó wiono zasady opracowywania wyraż eń. Podane tam opisy stanowią  istotny 
element niniejszego opracowania. 
 
 

Operacje przypisania 

 
Prosta operacja przypisania ma postać   
 
 

a = b 

 
w któ rej a i b są  wyraż eniami, ale ponadto a jest l-nazwą  zmiennej (por. Dodatek B). 
 
Wykonanie operacji polega na przypisaniu zmiennej a wartości wyraż enia b
 
 
Złoż ona operacja przypisania ma postać  
 

 

a 

@

b  

 
w któ rej @= jest jednym z operatoró w wymienionych w Dodatku A (np.  +=-=*= , /=). 
 
Operację a @= b (np. a += b) wykonuje tak, jak operację 
 

 

a = a + b 

 
ale wymaga się, aby opracowanie a i b było jednokrotne
 
 

Operacja połą czenia 

 
Operacja połą czenia ma postać  
 

 

a , b 

 
Jej wykonanie składa się z opracowania wyraż enia a (wyłą cznie dla jego skutkó w ubocznych) oraz z niejawnego 
zastą pienia całej operacji nazwą  zmiennej reprezentowanej przez wyraż enie b

background image

 

3

 
Uwaga: Nie jest operatorem połą czenia przecinek oddzielają cy parametry i argumenty funkcji. 
 
W szczegó lności wykonanie instrukcji 
 

return a = 10, cout <<  a, b = 20; 

 
jest ró wnoważ ne wykonaniu instrukcji 
 

a = 10; 
cout << a; 
b = 20; 
return b; 

 
 

Operacje arytmetyczne 

 
Operacje arytmetyczne wykonuje się za pomocą  operatoró w wymienionych w tabeli Operacje arytmetyczne
 
Tabela Operacje arytmetyczne 

### 

 

+ 

(dodawanie)   

- 

(odejmowanie

 

 

* 

(mnożenie

/ 

(dzielenie

(reszta

 
 

++  (zwiększenie o 1

-- 

(zmniejszenie o 1

 
 

+=  (dodanie

-= 

(odjęcie

 

*=  (pomnożenie

/= 

(podzielenie

### 

 
Sposó b  wykonania  podstawowych  działań  arytmetycznych  nie  wymaga  opisu.  Należ y  jedynie  zauważ yć ,  ż e 
rezultat  dzielenia  całkowitego  jest  całkowity,  a argumenty wyznaczania reszty  muszą   być   całkowite  (np.  11 / 4 
ma wartość  2, a 11 % 4 ma wartość  3). 
 
 

Operacje przedrostkowe 

 
Wykonanie operacji  ++num powoduje zwiększenie wartości zmiennej num 1. W miejsce wykonania operacji 
jest dostarczana nowa wartość  num
 
Wykonanie operacji  --num powoduje zmniejszenie wartości zmiennej num o 1. W miejsce wykonania operacji 
jest dostarczana nowa wartość  num
 

int fix = 10; 
cout << ++fix;    // 11 
cout << fix;      // 11 

 
 

Operacje przyrostkowe 

 
Wykonanie operacji  num++ powoduje zwiększenie wartości zmiennej num o 1. W miejsce wykonania operacji 
jest dostarczana pierwotna wartość  num
 
Wykonanie  operacji  num--  powoduje  zmniejszenie  wartości zmiennej num o  1  W miejsce wykonania operacji 
jest dostarczana pierwotną  wartość  num
 

int fix = 10; 
cout << fix--;    // 10 
cout << fix;      // 9 

 

background image

 

4

 

 

Operacje porównania 

 
Operacje poró wnania wykonuje się za pomocą  operatoró w wymienionych w tabeli Operacje porównania
 
Tabela Operacje porównania 

### 

 

== 

(ró wne

!= 

(nie ró wne), 

 

< 

(mniejsze

> 

(większe), 

 

<= 

(mniejsze lub ró wne

>= 

(większe lub ró wne

### 

 
Jeśli poró wnanie wyraż a orzeczenie prawdziwe, to jego rezultat ma wartość  true (prawda). W przeciwnym razie 
ma wartość  false (fał

sz). 

 
Uwaga: Poró wnanie na ró wność  wykonuje się za pomocą  operacji ==, a nie za pomocą  operacji =. Zaniedbanie 
tego faktu jest źró dłem trudnych do wykrycia błędó w semantycznych. 
 

#include <iostream.h> 
 
int main(void) 
{     
    int num = 0; 
    while(num == 0) 
        cin >> num; 
    cout << num << endl; 
 
    return 0; 

 
Program  wyprowadza  liczbę  0  albo  pierwszą   niezerową   liczbę  wprowadzoną   z  klawiatury.  Gdyby  operator 
poró wnania zastą piono operatorem przypisania, to zawsze wyprowadzał

by liczbę 0

 
 

Operacje orzecznikowe 

 
Operacje orzecznikowe wykonuje się za pomocą  operatoró w wymienionych w tabeli Operacje orzecznikowe
 
Tabela Operacje orzecznikowe 

### 

 

!

  (zaprzeczenie

&&

  (koniunkcja

||

  (dysjunkcja

 
### 

 
Argumenty i rezultaty operacji orzecznikowych są  typu bool i mają  wartości true albo false
 
Rezultat zaprzeczenia ma wartość  true tylko wó wczas, gdy argument ma wartość  false
 
Rezultat koniunkcji ma wartość  true tylko wó wczas, gdy oba argumenty mają  wartość  true
 
Rezultat dysjunkcji ma wartość  false tylko wó wczas, gdy oba argumenty mają  wartość  false
 
Uwaga:  Operacja  koniunkcji  i  dysjunkcji jest  wykonywana w  taki  sposó b, ż e  jeśli  po opracowaniu pierwszego 
argumentu  jest  znana  wartość   rezultatu  całej operacji (bo dla koniunkcji ma  wartość   false, a dla dysjunkcji ma 
wartość  true), to rezygnuje się z opracowania drugiego argumentu. 
 

#include <iostream.h> 
 
int vec[] = { 10, 20, 30, 40, 50 }; 
 
int main(void)  

background image

 

5

 
    int pos; 
    cin >> pos; 
 

pos >= 0 && pos < 5 && (cout << vec[pos]); 

 
    return 0; 

 
Program wyprowadza wartoś ć tego elementu tablicy, któ rego indeks wprowadzono z klawiatury.  
 
Jeś li wprowadzi się indeks, któ ry nie ma wartoś ci z przedział

u [0 ; 4], to program nie wyprowadzi nic. 

 
Dzięki użyciu operatora &&, nigdy nie dojdzie do opracowania wyrażenia vec[pos] z niedozwolonym indeksem. 
 
 

Operacje konwersji 

 
Wykonanie konwersji ma na celu przekształcenie zmiennej pewnego typu w zmienną  typu docelowego.  
 
Operacja konwersji wyraż enia e do typu Type ma postać  
 

 

(Type)e 

 
Jeśli  nazwę  typu  docelowego  Type  moż na  wyrazić   za  pomocą   identyfikatora  (np. int),  to  operację  konwersji 
moż na zapisać  jako 
 

 

Type(e)         

 
 
W szczegó lności, jeśli w programie występuje instrukcja 
 

int num = 4.8; 

 
w któ rej zmienna num jest typu int, a wyraż enie 4.8 jest typu double, to ponieważ  danej typu double (zazwyczaj 
8-bajtowej)  nie  moż na  pomieścić   w  zmiennej  typu  int  (zazwyczaj  4-bajtowej),  więc  najprościej  byłoby  taką  
instrukcje uznać  za błędną . 
 
Ponieważ   w  C++  przekształcenie  zmiennej  typu  double  w  zmienną   typu  int  zdefiniowano  jako  konwersję 
standardową   (polega  ona  na  odrzuceniu  części  ułamkowej),  więc  rozpatrywana  instrukcja  zostanie  niejawnie 
zmieniona w poprawną  instrukcję 
 

int num = int(4.8); 

 
ró wnoważ ną   
 

int num = 4; 

 
w któ rej wyraż enie inicjują ce jest już  typu int
 
Uwaga: Waż ne informacje na temat konwersji zamieszczono w Dodatku C
 
 

Operacje warunkowe 

 
Operacje warunkowe wykonuje się za pomocą  tró jargumentowego operatora ?: (pytajnikdwukropek). 
 
Rezultatem operacji 
 

 

e ? eT : eL 

 

background image

 

6

jest  zmienna  o  wartości  eT    jeśli  orzeczenie  wyraż one  przez  e  jest  prawdziwe,  albo  zmienna  o  wartości  eF  w 
przeciwnym razie..  
 
Uwaga: Po opracowaniu wyraż enia e, opracowuje się tylko jedno z wyraż eń eT i eF
 

num =  fix > 0 ? fix1 : fix2;   
num < 0 ? fix1 : fix2 = 30;    

 
 

Operatory :: i Name:: 

 
Jeśli  id  jest  identyfikatorem,  to  ::id  jest  nazwą   globalną ,  a  Name::id  jest  nazwą   składnika  typu  strukturowego 
Name
 

int num = 0; 
 
struct Fix { 
    int num;                   
    void set(int num =::num)   
    { 
        Fix::num = num; 
    } 
}; 

 
Napis ::num jest nazwą  zmiennej globalnej, a napis Fix::num jest nazwą  skł

adnika num

 
Prawy argument przypisania Fix::num = num jest nazwą  parametru. 
 
 

Wykonywanie instrukcji 

 
Do  napisania  dowolnego  programu  wystarczy  zaledwie  kilka  instrukcji.  Najważ niejszymi  z  nich  są :  instrukcja 
pusta,  grupują ca,  warunkowa  (if),  iteracyjna  (while)  i  powrotu  (return).  Opis  pozostałych  ograniczono  do 
przykładó w. 
 
 

Instrukcja pusta 

 
Instrukcja pusta składa się ze średnika.  
 

 

 
Jej wykonanie nie ma ż adnych skutkó w.  
 
 

Instrukcja grupują ca 

 
Instrukcja grupują ca składa się z nawiasó w klamrowych zawierają cych dowolną  sekwencję instrukcji.  
 
Jeśli w miejscu, w któ rym składnia wymaga uż ycia dokładnie jednej instrukcji, chce się umieścić  ich więcej, to 
wystarczy ują ć  je w nawiasy klamrowe i powstanie jedna instrukcja. 
 

{ int a; cin >> a; a++; cout << a; } 

 
 

Instrukcja warunkowa 

 
Instrukcja warunkowa ma postać  

background image

 

7

 

 

if(c

 

    s   

albo 
 

 

if(c

 

    s1 

 

else 

 

    s2 

 
w  któ rej  c  jest  wyraż eniem  orzecznikowym  o  wartości  true  albo  false,  a  s1  oraz  s2  jest  pojedynczą   instrukcją  
(np. instrukcją  grupują cą ). 
 
Wykonanie instrukcji warunkowej zaczyna się od opracowania wyraż enia c (np. a > 2). Jeśli wyraż one przez nie 
orzeczenie  jest prawdziwe, to w pierwszym przypadku jest wykonywana instrukcja s, a w drugim instrukcja s1
W przeciwnym razie, w pierwszym przypadku nie robi się nic, a w drugim wykonuje instrukcję s2
 

if(a > 2)  
    { a++; cout << a; } 
else 
    { cout << a; a-- } 

 
albo ró wnoważ nie 
 

if(a > 2) { 
    a++; 
    cout << a; 
} else { 
    cout << a; 
    a--; 

 
Jeśli  podczas  opracowywania  instrukcji  warunkowej  napotka  się  słowo  kluczowe  else,  to  przyjmuje  się,  ż e 
dotyczy ono najbliż szego z lewej słowa if, nie połą czonego jeszcze z else
 
W szczegó lności instrukcja 
 

if(fix1 > fix2) if(fix1) fix1++; else fix2++; 

 
jest wykonywana jak instrukcja 
 

if(fix1 > fix2) { 
    if(fix1)  
        fix1++;  
    else fix2++; 

 
a nie jak instrukcja 
 

if(fix1 > fix2) { 
    if(fix1)  
        fix1++;  
} else  
    fix2++; 

 
 

Instrukcje iteracyjne 

 
Instrukcja iteracyjna while ma postać  
 

 

while(c

 

    s 

 

background image

 

8

w któ rej c jest wyraż eniem orzecznikowym, a jest pojedynczą  instrukcją . 
 
Wykonanie instrukcji iteracyjnej while polega na cyklicznym badaniu orzeczenia wyraż onego przez wyraż enie c 
i  wykonywaniu instrukcji s
 
Iteracja kończy się w chwili stwierdzenia, ż e orzeczenie jest nieprawdziwe. Jeśli okaż e się to już  na wstępie, to 
instrukcja  s nie będzie wykonana wcale
 

int i = 3; 
while(i > 0) { 
    int t = i * i; 
    cout << t << endl;    //  9 4 1 
    i--; 

 
Często uż ywa się instrukcji iteracyjnej for 
 

 

for(d c ; e) { 

 

    s s ... s 

 

 
w któ rej d jest instrukcją  deklaracyjną , a c i e są  wyraż eniami. 
 
Tak zapisana instrukcja for jest ró wnoważ na instrukcji 
 

 

d 

 

while(c) { 

 

    s s ... s 

 

    e

 

 

 

Instrukcja for dobrze opisuje czynności o znanej liczbie powtó rzeń. 
 

int tab[5] = { 10, 20, 30, 40, 50 }, sum = 0; 
for(int i = 0; i < 5 ; i++) 
    sum += tab[i]; 
cout << "Sum = " << sum << endl; 

 
 

Instrukcja zaniechania 

 
Instrukcja zaniechania ma postać  
 

 

 

break; 

 
Wykonanie  instrukcji  zaniechania  powoduje  zakończenie  wykonywania  najwę ższej  obejmują cej  ją   instrukcji 
iteracyjnej albo decyzyjnej. 
 

int sum = 0; 
while(true) { 
    int tmp = 0; 
    cin >> tmp;         // wprowad

ź daną 

    if(tmp == 0)        // zbadaj czy 0 
        break;          // zako

ńcz iterację 

    sum += tmp;         // dosumuj 

cout << "Sum = " << sum << endl; 

 
albo 
 

int tmp = 0, sum = 0; 
while(cin >> tmp, tmp)  // wprowad

ź i zbadaj 

    sum += tmp;         // dosumuj           
cout << "Sum = " << sum << endl; 

background image

 

9

 
lub 
 

for(int tmp = 0, sum = 0; cin >> tmp, tmp ; sum += tmp); 
cout << "Sum = " << sum << endl; 

 
 

Instrukcja powrotu 

 
Instrukcja powrotu ma postać  
 

 

return e

 
w któ rej e jest wyraż eniem. 
 
Wykonanie  instrukcji  powrotu  powoduje  zakończenie  wykonywania  funkcji  i  dostarczenie  rezultatu  o  wartości 
określonej przez e.  
 

int sum(int one, int two) 

    return one + two; 

 
Jeśli  typem  funkcji  jest  void,  to  uż yta  w  niej  instrukcja  powrotu  nie  moż e  zawierać   wyraż enia.  Uż ycie  takiej 
instrukcji jest zazwyczaj zbyteczne, ponieważ  domniemywa się ją  tuż  przed klamrą  zamykajacą  ciało funkcji. 
 

void sum(int one, int two) 

    cout << one + two << endl; 
 
    return;      // zb

ędne 

 
 

Instrukcja decyzyjna 

 
Instrukcja  decyzyjna  uogó lnia  instrukcję  warunkową   i  jest  przydatna  wó wczas,  gdy  w  programie  występują  
więcej niż  dwie gałęzie decyzyjne. W szczegó lności instrukcję warunkową  
 

if(a == 2) 
    b = 3; 
else if(a == 1) 
    b = 5; 
else if(a == 4) 
    b = -1; 
else 
    b = 0; 

 
moż na zapisać  w postaci 
 

switch(a) { 
    case 2:         // je

śli a == 2 

        b = 3; 
        break; 
    case 1:         // je

śli a == 1 

        b = 5; 
        break; 
    case 4:         // je

śli a == 4 

        b = -1; 
        break; 
    default:        // w pozosta

łych przypadkach 

        b = 0; 

 

background image

 

10

 

Deklarowanie zmiennych i typów 

 
Każ dy moduł  programu  jest kompilowany  niezależnie od  pozostałych. Analiza  składniowa  modułu  odbywa się 
od-gó ry-do-doł

u  i  od-lewej-do-prawej  i  polega  na  rozpoznawaniu  jednostek  leksykalnych:  identyfikatorów 

(np. exit), literałów (np. 0), operatorów (np. +) i ograniczników (np. ;). 
 
 

Identyfikatory  

 
Identyfikatorem jest spójna sekwencja liter i cyfr, zaczynają ca się od litery. Identyfikator nie moż e mieć  postaci 
słowa kluczowego (np. return). Za jego literę uznaje się ró wnież  znak podkreślenia (_). 
 
Litery  małe  uznaje  się  za  ró ż ne  od  duż ych.  Zaleca  się,  aby  w  wielosłowowych  nazwach  zmiennych  i  funkcji, 
wszystkie słowa, z wyją tkiem pierwszego, były zapisane za pomocą  duż ych liter. 
 
np. 

 

forSale  speedLimit  veryLongName 

 
 

Literały 

 
Literałami  są   liczby  (np. 12,  0xff  i  2.e-3),  znaki  (np. ’a’)  i  łań cuchy  (np.  "Hello").  Każ dy  literał  jest  nazwą  
zmiennej ustalonej. Jej typ wynika z zapisu literału. 
 
Uwaga: Jeśli łańcuch ma zawierać  znak \ (ukoś nik), to należ y go zapisać  jako \\ (np. "C:\\Data.txt). 
 
np. 

 

’a’ 

’\n’ 

’\0’    // nazwy zmiennych typu char 

 

12 

-12 

  // nazwy zmiennych typu int 

 

-2.4 

2.e4 

.2     // nazwy zmiennych typu double 

 

"a" 

"N" 

"\n"   // nazwy zmiennych typu char [2] 

 
 

Deklaracje 

 
Każ de uż ycie  identyfikatora musi być  poprzedzone jego  deklaracją . Deklaracja kompletnie opisują ca zmienną  
(określają ca  jej  wartość   począ tkową ),  typ  (wyszczegó lniają ca  strukturę  jego  obiektó w)  i  funkcję   (podają ca  jej 
ciało) jest nazywana definicją .  
 
W skład deklaracji wchodzą  specyfikatorydeklaratory inicjatory
 
np. 

 

const int tab[3] = { -1, 0, +1 }; 

 
Specyfikatorami są  const int, deklaratorem jest tab[3], a inicjatorem jest = { -1, 0, +1 }
 
 

Nagłówki 

 
Deklaracje  typó w  i  funkcji  są   ujmowane  w  nagłówki.  Każ dy  nagłó wek  jest  zapisany  w  odrę bnym  pliku. 
Włą czenie nagłó wka odbywa się w miejscu wystą pienia wyszczegó lniają cej go dyrektywy #include
 
Do  najczęściej  uż ywanych  nagłó wkó w  należ ą :  iostream.h  i  iomanip.h,  math.h,  string.h  i  stdlib.h.  Dwa 
pierwsze  włą czają   do  modułu  deklaracje  zmiennych  i  operatoró w  wejścia-wyjścia  (cin,  cout,  >>,  <<),  dwa 
następne  włą czają   deklaracje  funkcji  matematycznych  (sqrt,  sin,  cos)  i  łańcuchowych  (strlen,  strcpy,  strcat
strcmp), a ostatni włą cza m.in. deklarację funkcji exit
 

background image

 

11

#include <iostream.h> 
#include <math.h> 
 
int main(void) 

    double number;          // deklaracja zmiennej 
    cin >> number;          // wprowadzenie liczby 
    cout << sqrt(number);   // wyprowadzenie pierwiastka 
 
    return 0;               // zako

ńczenie wykonywania 

 
 

Zmienne 

 
Zmienną   jest  obszar  pamięci  do  przechowywania  danych  określonego  typu:  skalarnych,  tablicowych  
strukturowych. Każ de odwołanie do zmiennej musi być  poprzedzone deklaracją  jej typu. 
 

int fix;           // zmienna ca

łkowita 

char chr;          // zmienna znakowa 
double num;        // zmienna rzeczywista 

 
Zmienna fix jest typu int, zmienna chr jest typu char, zmienna num jest typu double
 
 

Rozmiar zmiennej 

 
Rozmiar  zmiennej  w  bajtach  określa  się  za  pomocą   operatora  sizeof.  Argumentem  operatora sizeof  moż e  być  
nazwa zmiennej albo nazwa typu. 
 
Uwaga: Rozmiar  zmiennej zależ y od implementacji. W  Visual C++ zmienne typu char są  1-bajtowe, zmienne 
typu int są  2-bajtowe, a zmienne typu double są  8-bajtowe. 
 

int age = 24;         
cout << sizeof(age);    // 4 
cout << sizeof(int);    // 4 
int tab[3];              
cout << sizeof(tab);    // 12 

 
 

Zmienne ustalone 

 
Zmienna  zadeklarowana  ze  specyfikatorem  const  jest  zmienną   ustaloną .  Zmienna  ustalona  musi  być  
zainicjowana, ale nadana jej wartość  nie moż e ulec zmianie. 
 
Uwaga:  Zmiennymi  ustalonymi  są   takż e  zmienne  reprezentowane  przez  literały.  W  szczegó lności  liczba  12e2 
jest nazwą  zmiennej ustalonej o wartości 1200
 

const int size = 100; 
const double width = -2e-7, height = 2e2; 
const int tab[2] = { 10, 20 }; 

 
 

Zmienne skalarne 

 
Deklaracja zmiennej skalarnej określa jej identyfikator oraz wyszczegó lnia typ danych jakie moż na przypisywać  
zmiennej (np. intdoublechar). 
 

int number; 
double speedLimit; 
char separator; 

 

background image

 

12

Wartość   począ tkową   zmiennej  określa  się  za  pomocą   inicjatora.  Jeśli  deklaracja  zmiennej  zawiera  jawny  albo 
domniemany inicjator, to jest jej definicją 
 

int minValue = 10, maxValue = 90; 
double width = 2.4, height = 4.5e+2, area; 
char lastChar = ’.’; 

 
 

Składnia inicjatora 

 
Inicjatory  dzielą   się  na  wyrażeniowe,  klamrowe  i  nawiasowe.  Inicjator  zmiennej  ustalonej  musi  mieć   postać  
wyrażenia  stałego.  W  jego  skład  wchodzą   odwołania  do  literałó w  i  zmiennych  ustalonych,  ale  nie  mogą  
wchodzić  odwołania do zmiennych nie-ustalonych. 
 

int base = 100;             // inicjator wyra

żeniowy 

int min =  { base + 20 };   // inicjator klamrowy 
int max(base + 40);         // inicjator nawiasowy 
 
const int size = max - min; // b

łąd 

 
 

Punkt zadeklarowania 

 
Identyfikator  zmiennej  uważ a  się  za  zadeklarowany  w  punkcie  tuż   przed  inicjatorem  wyraż eniowym  i 
klamrowym, ale tuż  po inicjatorze nawiasowym. Ta subtelna ró ż nica ma niekiedy wpływ na poprawność  i skutek 
wykonania programu. 
 

#include <iostream.h> 
 
const int val = 10;  // definicja zmiennej globalnej 
 
int main(void) 

    int val(val);    // definicja zmiennej lokalnej 
    cout << val;     // 10 
 
    return 0; 

 
Punkt  zadeklarowania  zmiennej  lokalnej  występuje  tuż  po  inicjatorze  (val).  Gdyby  inicjator  nawiasowy 
zastą piono  jednym  z  pozostał

ych  inicjatoró w,  to  program  stał

by  się bł

ędny, ponieważ zmienna lokalna byłaby 

wó wczas inicjowana nie wartoś cią  zmiennej globalnej, ale nieokreś loną  jeszcze wartoś cią  zmiennej lokalnej. 
 
 

Operacje wejścia-wyjścia 

 
Zmienne  typu  int,  double  i  char  są   zmiennymi  arytmetycznymi,  przystosowanymi  odpowiednio  do 
przechowywania liczb całkowitych, zmiennopozycyjnych i kodó w znakó w.  
 
Podczas  wykonywania  operacji  wejścia,  do  zmiennych  typu  int  i  double  wprowadza  się  dane  liczbowe,  a  do 
zmiennych typu char wprowadza się kody znakó w. A zatem, jeśli z klawiatury wprowadzi się na przykład napis 
20e3,  to  liczba  pobranych  znakó w  i  otrzymana  wartość   będzie  zależ eć   od  typu  zmiennej,  zgodnie  z  tabelą  
Wprowadzanie danych
 
Tabela Wprowadzanie danych 
 
 

Typ zmiennej 

Pobrano znaków 

Wprowadzono wartość   

 
 

int 

20 

 

double 

20000 

 

char 

49 

 

background image

 

13

Podczas  wykonywania  operacji  wyjścia,  wyprowadza  się  liczby  o  wartości  zmiennych  typu  int  i  double  oraz 
znaki o kodach określonych przez wartości zmiennych typu char
 

#include <iostream.h> 
 
int main(void) 

    int mant, exp; 
    char sep; 
    cin >> mant >> sep >> exp; 
    int value = mant; 
    while(exp > 0) { 
        value = value * 10; 
        exp--; 
    } 
    cout << mant << sep << exp << 
         " == " << value << endl; 
 
    return 0; 

 
Jeś li wprowadzi się napis 2e3, to program wyprowadzi ten napis oraz liczbę 2000
 
 

Zmienne tablicowe 

 
Zmienną  tablicową , w skró cie tablicą , jest zestaw są siadują cych ze sobą  elementów tablicy. Każ dy element jest 
zmienną  takiego samego typu: skalarną tablicową strukturową 
 

int tab[20]; 

 
Zmienna tab jest tablicą  o 20-elementach typu int
 
Z każ dym elementem tablicy jest zwią zany indeks, określają cy położ enie elementu w obrębie tablicy. Elementy 
tablicy  są   indeksowane  od  0.  W  deklaracji  tablicy  podaje  się  liczbę  jej  elementó w,  a  nie  indeks  jej  ostatniego 
elementu. Jeśli deklarator nie podaje liczby elementó w, ale deklaracja zawiera inicjator, to za liczbę elementó w 
uznaje się liczbę fraz inicjują cych. 
 
Uwaga: Liczba fraz inicjują cych nie moż e przekraczać  liczby elementó w tablicy. Jesli jest od niej mniejsza, to 
jest niejawnie dopełniana frazami 0
 

int tab[100] = { 4, 4 }; 

 
Zerowy i pierwszy element tablicy tab ma wartoś ć 4. Wszystkie pozostał

e mają  wartoś ć 0

 
 
Liczba  elementó w  tablicy  musi  być   wyraż ona  za  pomocą   wyrażenia  stałego.  Wyraż enie  stałe  moż e  zawierać  
literały i identyfikatory zmiennych ustalonych, ale nie moż e zawierać  operatora połą czenia. 
 

const int Count = 3; 
double sizes[Count] = { 2.4, 3.8, 5.2 }; 
int values[] = { 10, 20, 30, 40, 50 }; 
int Size = 4; 
double reals[Size];      // b

łąd 

 
Tablica sizes skł

ada się z 3 zmiennych, z któ rych każda jest typu double

 
Tablica values skł

ada się z 5 zmiennych, z któ rych każda jest typu int.  

 
 

Identyfikowanie elementów tablicy 

 

background image

 

14

Jeśli nazwą  tablicy jest vec, to nazwą  jej elementu o indeksie ind jest vec[ind]. Jest to prawdziwe tylko wó wczas, 
gdy wyraż enie ind ma wartość  większą -lub-ró wną  0 i jednocześnie mniejszą  od liczby elementó w tablicy. 
 
Uwaga: Jeśli tablica vec ma n elementó w, to zezwala się, aby wyraż enie ind miało wartość  -1 oraz n, ale tylko 
wó wczas,  gdy  opracowanie  wyraż enia  vec[ind]  nie  ma  na  celu  dokonania  zmiany  albo  dostarczenia  wartości 
elementu. 
 

#include <iostream.h> 
 
int values[5] = { 10, 20, 30, 40, 50 }; 
 
int main(void) 

    int index; 
    cin >> index; 
    if(index >= 0 && index < 5) 
        cout << values[index] << endl; 
    else 
        cout << "Wrong index" << endl; 
 
    return 0; 

 
Program  wyprowadza  wartoś ć  elementu  o  podanym  indeksie.  Jeś li  indeks  nie  mieś ci  się  w  domkniętym 
przedziale [0 ; 4], to program wyprowadza napis Wrong index
 
 

Tablice znakowe 

 
Tablicą  znakową  jest tablica o elementach typu  char. Przechowuje się w niej zazwyczaj małe liczby oraz kody 
znakó w.  
 
Ponieważ   Visual C++ uż ywa kodu  ASCII, w któ rym kodem cyfry jest 48, więc zainicjowanie 4-elementowej 
tablicy znakowej kodami cyfr 02 oraz kodem znaku ’\0’ moż na wykonać  na wiele sposobó w, w tym m.in. 
 

char digits[] = { ’0’, ’1’, ’2’, ’\0’ }; 
char digits[] = { 48, 49, 50, 0 }; 
char digits[4] = { ’0’, ’0’+1, ’3’-1 }; 
char digits[4] = "012"; 

 
Z klawiatury moż na wprowadzać  tylko spójne cią gi znakó w. Za ostatnim wprowadzonym znakiem umieszcza się 
wó wczas specjalny znak o kodzie 0.   
 
Jeśli  chce  się  wyprowadzić   cią g  znakó w  utworzony  w  tablicy  programowo,  to  należ y  zakończyć   go  znakiem  o 
kodzie 0 (jego rozpoznanie spowoduje zakończenie wyprowadzania znakó w). 
 

#include <iostream.h> 
 
char name[100]; 
 
int main(void) 

    cin >> name; 
    name[1] = 0; 
    cout << "Your initial is: " << name << endl; 
 
    return 0; 

 
Program wprowadza imię, a następnie wyprowadza jego inicjał

. 

 
 

Literały łań cuchowe 

 

background image

 

15

Literał łańcuchowy, na przykład "Hello", ma postać  cią gu znakó w ujętego w cudzysłowy. Znaki specjalne są  w 
tym cią gu reprezentowane przez nastepują ce symbole 
 
 

\\ (ukoś nik

\n (nowy wiersz

\t (tabulator),  

 

\’ (apostrof

\" (cudzysł

ó w

\0 (znak o kodzie 0). 

 
Każ dy literał łańcuchowy, jest nazwą  tablicy o elementach typu char, zainicjowanych kodami kolejnych znakó w 
literału oraz kodem znaku \0. W szczegó lności (w kodzie ASCII) literał "No" jest nazwą  3-elementowej tablicy 
zainicjowanej liczbami 78111 i 0
 

#include <iostream.h> 
 
int main(void) 

    int i = 0; 
    while("Hello"[i] != 0) { 
        cout << "Hello"[i] << ’ ’; 
        i++; 
    } 
    cout << endl; 
 
    return 0; 

 
Program wyprowadza kolejne znaki napisu Hello, po każdym znaku dodają c spację . Zakończenie wykonywania 
następuje po rozpoznaniu elementu zainicjowanego liczbą  0
 
Literały  łańcuchowe  mogą   być   uż yte  do  inicjowania  tablic  znakowych.  Tak  zainicjowana  tablica  musi  mieć   co 
najmniej  
tyle  elementó w  ile  ma  tablica  reprezentowana  przez  literał.  Jeśli  jest  dłuż sza,  to  jej  nadmiarowe 
elementy są  inicjowane liczbami 0
 

char name1[10] = { ’I’, ’s’, ’a’, 0 };  
char name2[10] = "Isa";   
char name3[]   = "Isa"; 
char name4[3]  = "Isa";        // b

łąd 

 
 

Operacje wejścia-wyjścia 

 
Tablice  znakowe  mogą   być   wykorzystane  do  wprowadzania  z  klawiatury  spó jnych  cią gó w  znakó w.  W  takim 
przypadku argumentem  operacji  jest zazwyczaj  nazwa  tablicy, a wykonanie operacji powoduje  umieszczenie w 
tablicy kodó w znakó w łańcucha oraz kodu o wartości 0
 
Ponieważ  moż e wó wczas dojść  do przepełnienia tablicy, zaleca się uż ycie manipulatora setw, zadeklarowanego 
w nagłó wku iomanip.h, ograniczają cego liczbę wprowadzonych znakó w. 
 
Uwaga: Manipulator setw moż e być  uż yty takż e podczas wyprowadzania danych. W takim wypadku określa on 
szerokość  pola zewnętrznego, w któ rym umieszcza się dane wyjściowe. 
 

#include <iostream.h> 
#include <iomanip.h> 
 
char name[20]; 
 
int main(void) 

    cin >> setw(20) >> name; 
    name[1] = 0; 
    cout << "Your initial is: " << name << endl; 
 
    return 0; 

 

background image

 

16

Program  wprowadza  imię,  a  następnie  wyprowadza  jego  inicjał

.  Aby  zabezpieczyć  się  przed  wpisaniem  do 

tablicy name więcej niż 20 znakó w, użyto manipulatora setw(20) zadeklarowanego w nagł

ó wku iomanip.h. 

 
 

Zmienne strukturowe 

 
Zmienną   strukturową ,  w  skró cie  strukturą ,  jest  zestaw  są siadują cych  ze  sobą   elementów  struktury.  Każ dy 
element struktury moż e być  zmienną  innego typu: skalarną tablicową strukturową 
 
Przed zadeklarowaniem zmiennej strukturowej należ y zdefiniować  jej typ. Deklaracja typu strukturowego składa 
się z deklaracji pól struktury. Deklaracja pola struktury ma postać  deklaracji zmiennej. 
 

struct Child { 
    char name[20]; 
    int age; 
}; 
 
Child isa = { "Isabel", 15 }; 

 
Struktura  isa  skł

ada  się  z  2  zmiennych,  opisanych  przez  pola  name  i  age.  Wartoś ci  począ tkowe  elementó w 

struktury okreś lono za pomocą  inicjatora klamrowego. Użycie innych inicjatoró w jest zabronione. 
 
 

Identyfikowanie elementów 

 
Jeśli  nazwą   struktury  jest  str,  a  w  opisie  jej  typu  występuje  pole  fld,  to  nazwą   zmiennej  odpowiadają cej  temu 
polu jest str.fld
 

#include <iostream.h> 
#include <iomanip.h> 
 
struct Child { 
    char name[20]; 
    int age; 
}; 
 
Child child; 
 
int main(void) 

    cin >> setw(20) >> child.name >> child.age; 
    cout << child.name << " is "  
         << child.age << " now" << endl; 
 
    return 0; 

 
Zmienna  child  skł

ada  się z  tablicy  o elementach  typu char i zmiennej  skalarnej typu int.  Program wprowadza 

imię i wiek dziecka, a następnie wyprowadza je.  
 
 

Kopiowanie struktur 

 
W odró ż nieniu od tablic, któ re moż na kopiować  tylko element-po-elemencie, kopiowanie struktur moż e dotyczyć  
pełnego zestawu jej elementó w i to nawet wó wczas, gdy struktura zawiera tablice. 
 

#include <iostream.h> 
 
struct Child { 
    char name[20]; 
    int age; 
}; 
 
Child girl; 

background image

 

17

 
int main(void) 

    Child isa = { "Isabel", 15 }; 
    girl = isa; 
    cout << girl.name << " is " << girl.age << endl; 
 
    return 0; 

 
Program wyprowadza te same dane, któ rymi zainicjowano strukturę isa
 
 

Unia elementów 

 
Struktura, któ rej elementy są  rozmieszczone w pamięci nie jeden-za-drugim, ale zawsze od tego samego miejsca, 
jest nazywana unią . W celu zadeklarowania unii należ y zamiast słowa kluczowego struct uż yć  słowa union
 
Definicja  unii,  w  któ rej  pominięto  nazwę  typu,  jest  definicją   unii  anonimowej.  Pola  unii  anonimowej  są  
zadeklarowane w miejscu zdefiniowania unii. 
 

struct Number { 
    bool isFixed; 
    union {            // unia anonimowa 
        int fixed; 
        double real; 
    }; 
}; 
 
Number num = { true, 12 }; 
if(num.isFixed) 
    cout << num.fixed << endl;   // 12 
else 
    cout << num.real << endl; 
cout << num.real << endl;        // b

łąd 

 
W każdej chwili struktura num skł

ada się ze zmiennych typu bool int, albo ze zmiennych typu bool double

 

ą d polega na tym, że w chwili gdy zmienna num składa się ze zmiennych typu bool i int, następuje odwoł

anie 

do zmiennej typu double
  
 

Przetwarzanie plików 

 
Przetwarzanie  plikó w  odbywa  się  za  pośrednictwem  zmiennych  strumieniowych  klas  ifstream  i  ofstream
zadeklarowanych w nagłó wku fstream.h.  Po  utworzeniu  zmiennej strumieniowej należ y otworzyć  skojarzony z 
nią  plik, a następnie upewnić  się, ż e otwarcie było pomyślne.  
 
Po pomyślnym otwarciu pliku, pochodzą cy z niego strumień danych moż na przetwarzać  w taki sam sposó b, jak 
strumień danych zwią zany z klawiaturą  albo z monitorem. 
 
 

Stany strumienia 

 
Począ tkowo  strumień  znajduje  się  w  stanie  dobrym,  ale  na  skutek  błędu  operacji  wejścia-wyjścia  albo  pró by 
wprowadzenia nieistnieją cej danej, moż e znaleźć  się w stanie nie-dobrym (fail).  
 
W  stanie  nie-dobrym  wszystkie  operacje  na  strumieniu  są   ignorowane.  Jeśli  dane  przygotowano  właściwie,  a 
jakość  pamięci zewnętrznej jest zadowalają ca, to stan nie-dobry oznacza, ż e napotkano koniec strumienia.  
 

background image

 

18

Uwaga:  W  programach  przykładowych  nie  bę dzie  rozpatrywany  przypadek  wystą pienia  błę du  przesyłania 
danych
.  
 
Szczegó lnym przypadkiem stanu nie-dobrego jest stan zły (bad). Powstaje on w przypadku rozpoznania danych o 
złym formacie. Niestety, na skutek niefortunnych domniemań, wprowadzenie takiej "danej" jak 3e, zamiast 3e0 
(w kontekście 3ex) nie zmienia stanu strumienia na zły. 
 
Uwaga: Do sprawdzenia czy stan strumienia jest zły, służ y funkcja bad, a do sprawdzenia, czy strumień znajduje 
się w pozycji za końcem pliku, służ y funkcja eof. Funkcji tych uż ywa się bardzo rzadko. 
 
 

Zmienna plikowa 

 
Jeśli  w  miejscu  wystą pienia  operacji  wejścia-wyjścia  odbywa  się  takie  badanie  zmiennej  plikowej,  jakby 
dotyczyło wyraż enia o wartości orzecznikowej, na przykład 
 

 

while(cin >> num) ... 

albo 

 

if(cin) ... 

 
to w stanie dobrym jest dostarczana wartość  true, a w stanie nie-dobrym wartość  false
 
 

Wprowadzanie danych 

 
Zmienna strumieniowa uż yta do wprowadzania danych z pliku jest typu ifstream. Otwarcie pliku odbywa się za 
pomocą   funkcji  open,  któ rej  pierwszym  argumentem  jest  nazwa,  a  drugim  tryb  otwarcia  pliku:  ios::in.  Jeśli 
otwierany plik nie istnieje, to zostanie utworzony jako pusty. Aby tego unikną ć , plik należ y otworzyć  w trybie 
ios::in | ios::nocreate
 
Do  zbadania,  czy  otwarcie  pliku  się  powiodło,  służ y  funkcja  is_open.  Jej  rezultat  ma  wartość   nie-zero  tylko 
wó wczas, gdy otwarcie było pomyślne. 
 

#include <iostream.h> 
#include <fstream.h> 
#include <assert.h> 
 
int sum = 0; 
 
int main(void) 

    ifstream inp;         // zmienna plikowa 
    inp.open("Data.txt", ios::in | ios::nocreate); 
    if(!inp.is_open()) { 
        cout << "File does not exist" << endl; 
        return -1; 
    } 
    int val; 
    while(inp >> val)     // wprowad

ź i sprawdź stan 

        sum += val;       // dosumuj 
    assert(!inp.bad());   // raczej zb

ędne 

    cout << "Sum = " << sum << endl; 
 
    return 0; 

 
Wykonanie programu powoduje wyprowadzenie sumy liczb cał

kowitych zawartych w pliku Data.txt. 

 
Wywoł

anie funkcji assert ma na celu upewnienie się, że strumień nie znajduje się w zł

ym stanie. Gdyby tak był

o, 

to wykonanie programu został

oby zaniechane. 

 
 

background image

 

19

Wyprowadzanie danych 

 
Zmienna strumieniowa uż yta do wyprowadzania danych do pliku jest typu ofstream. Otwarcie pliku odbywa się 
za pomocą  funkcji open, któ rej pierwszym argumentem jest nazwa, a drugim tryb otwarcia pliku: ios::out.  
 
Jeśli  otwierany  plik  nie  istnieje,  to  zostanie  utworzony  i  otworzony  jako  pusty.  Jeśli  już   istnieje,  to  zostanie 
otworzony jako pusty. 
 
Do  zbadania,  czy  otwarcie  pliku  się  powiodło,  służ y  funkcja  is_open.  Jej  rezultat  ma  wartość   nie-zero  tylko 
wó wczas, gdy otwarcie było pomyślne. 
 
 

#include <iostream.h> 
#include <fstream.h> 
 
int main(void) 

    ifstream inp; 
    inp.open("Data.txt", ios::in | ios::nocreate); 
    if(!inp.is_open()) { 
        cout << "Source does not exist" << endl; 
        return -1; 
    } 
 
    ofstream out; 
    out.open("Data2.txt", ios::out); 
    if(!out.is_open()) { 
        cout << "Target not opened" << endl; 
        return -1; 
    } 
 
    int val; 
    while(inp >> val) 
        out << val << endl; 
    cout << "Done!" << endl; 
 
    return 0; 

 
Program  kopiuje  liczby  cał

kowite  z  pliku  Data.txt  do  pliku  Data2.txt.  Każdą   kopiowaną   liczbę umieszcza  w 

nowym wierszu. 
 
 

Użycie klawiatury 

 
Jeśli  dane  wprowadza  się  z  klawiatury,  to  koniec  strumienia  określa  się  za  pomocą   znaku  koń ca:  Ctrl-Z  (na 
polskiej klawiaturze Ctrl-Y). W Visual C++ nastą pi wó wczas pominięcie pierwszego znaku wyprowadzonego na 
konsolę. 
 
Uwaga: Zaleca się, aby znak końca został wprowadzony na począ tku nowego wiersza (po Enter). 
 

#include <iostream.h> 
 
int main(void) 
{     
    int count = 0; 
    double tmp; 
    while(cin >> tmp) 
        count++; 
 
    cout << endl;     // na po

żarcie 

    cout << "Count = " << count << endl; 
 
    return 0; 

background image

 

20

 
Program zlicza dane liczbowe wprowadzone z klawiatury. 
 

background image

 

21

 

Ś rodowisko Visual C++  

 
 
 
 
Program  źró dłowy  składa  się  z  modułów  źródłowych.  Każ dy  moduł  jest  umieszczony  w  odrębnym  pliku  z 
rozszerzeniem .cpp. Dodatkowo, w skład programu mogą  wchodzić  moduły skompilowane (*.obj) i biblioteczne 
(*.lib). 
 
W  celu  przekształcenia  zestawu  modułó w  w  program  wykonalny,  należ y  utworzyć   projekt,  umieścić   go  w 
przestrzeni roboczej, włą czyć  do projektu nazwy plikó w z rozszerzeniami .cpp.obj i .lib, a następnie zbudować  
program. Zostanie on umieszczony w pliku z rozszerzeniem .exe
 
 

Katalog 

 
Zaleca  się,  aby  pliki  programu  znajdowały  się  we  własnym  katalogu.  Jeśli  dysponuje  się  wolnym  miejscem  na 
przykład w katalogu głó wnym dysku D:, to należ y wywołać  Eksplorator Windows, klikną ć  na nazwie katalogu 
głó wnego  i  wydać   polecenie  Plik  /  Nowy  obiekt  /  Folder,  a  następnie  określić   nazwę  swojego  katalogu,  na 
przykład jbVisual
 
 

Przestrzeń  

 
W  celu  utworzenia  przestrzeni  roboczej  należ y  wydać   polecenie  File  /  New,  a  następnie  (w  zakładce 
Workspaces)  podać   nazwę  przestrzeni,  np.  Workspace:  jbSpace  oraz  określić   jej  położ enie,  np.  Location: 
D:\jbVisual\jbSpace, po czym nacisną ć  przycisk OK
 
Jeśli  przestrzeń  już   istnieje,  to  aby  ją   otworzyć ,  należ y  wydać   polecenie  File  /  Open  Workspace,  wejść   do 
katalogu przestrzeni (np. jbSpace), a następnie dwu-klikną ć  na nazwie jbSpace.dsw
 
 

Projekt 

 
W celu utworzenia projektu należ y wydać  polecenie File / New, a w zakładce Projects podać  typ projektu: Win 
32  Console  Application
  i  jego  nazwę,  np.  Project  name:  jbTests.  Po  upewnieniu  się,  ż e  projekt  zostanie 
włą czony 

do 

bież ą cej 

przestrzeni 

(Add 

to 

current 

workspace

czym 

zaświadczy 

Location: D:\jbVisual\jbSpace\jbTests, należ y nacisną ć  przycisk OK
 
 

Pliki 

 
W celu utworzenia plikó w projektu należ y wydać  polecenie File / New, a następnie (w zakładce Files), określić  
rodzaj pliku 
 
 

C/C++ Source File 

dla pliku z rozszerzeniem .cpp 

 

C++ Header File 

dla pliku z rozszerzeniem .h 

 

Text File 

dla pliku z rozszerzeniem .txt 

 
nie zapominają c o podaniu jego nazwy (bez rozszerzenia), np. File name: Sum
 
Po  wykonaniu  tych  czynności,  w  katalogu  D:\jbVisual\jbSpace\jbTests  powstanie  plik  Sum.cpp,  a  jego 
(począ tkowo pusta) zawartość  ujawni się odrębnym oknie edycyjnym. 

background image

 

22

 
Jeśli  program  wymaga  utworzenia  plikó w  z  danymi,  to  zaleca  się  je  umieścić   w  tym  samym  katalogu  co  pliki 
źró dłowe. Dla wygody moż na je dołą czyć  do plikó w projektu. 
 
 

Budowanie projektu 

 
W celu zbudowania projektu, to jest skompilowania jego wszystkich plikó w *.cpp, oraz ewentualnie jego plikó w 
*.obj i *.lib, należ y klikną ć  ikonę Build. Spowoduje to niezależ ne kompilacje wszystkich modułó w źró dłowych 
oraz połą czenie ich w program wykonalny. 
 
Przebieg  budowania  projektu  jest  diagnozowany  w  oknie  Output.  Jeśli  okno  nie  jest  widoczne,  to  moż na  je 
wyświetlić  wydają c polecenie View / Output
 
Błędy  modułu  wyszczegó lnia  się  w  oknie  Output.  Po  rozpoznaniu  każ dego  z  nich  podaje  się  kró tki  opis 
przyczyny  błędu  i  numer  wiersza  programu.  Dwu-kliknięcie  w  obrębie  opisu  błędu  powoduje  przeniesienie 
kursora w pobliż e miejsca, w któ rym wykryto błą d.  
 
W  rzadkich  przypadkach,  gdy  poprawność   diagnozy  budzi  wą tpliwości,  zaleca  się  zastą pienie  polecenia  Build 
poleceniem Build / Rebuild All
 
 

Wykonanie programu 

 
Program wykonalny, pod nazwą  jbTests.exe jest umieszczany w podkatalogu jbTests\Debug. Jeśli wykonuje się 
bezbłędnie i jest należ ycie wytestowany, to moż e zostać  zoptymalizowany. 
 
W  celu  zoptymalizowania  programu  należ y  wydać   polecenie  Build  /  Set  Active  Configuration,  a  następnie 
zamiast konfiguracji Win 32 Debug, wybrać  konfigurację Win 32 Release. Po ponownym zbudowaniu projektu, 
w katalogu  jbTests\Release, powstanie program znacznie kró tszy, ale już  bez informacji uruchomieniowych. 
 
 

Zarzą dzanie projektami 

 
Przestrzeń  robocza  moż e  zawierać   więcej  niż   jeden  projekt,  a  projekt  moż e  składać   się  z  więcej  niż   jednego 
pliku.  
 
Jeśli przestrzeń zawiera  więcej niż  jeden projekt,  to tylko jeden  z nich  moż e być  aktywny,  to jest taki, któ rego 
dotyczą  polecenia Build. Uaktywnienie projektu odbywa się przez p-kliknięcie jego nazwy i wydanie polecenia 
Set Active Project
 
W celu umieszczenia w przestrzeni dodatkowego projektu należ y p-klikną ć  nazwę przestrzeni, wydać  polecenie 
Add New Project to Workspace, a dalej postępować  tak, jak podczas tworzenia pierwszego projektu. 
 
W celu włą czenia do projektu dodatkowego pliku należ y p-klikną ć  nazwę projektu, a następnie wydać  polecenie 
Add Files to Project i wybrać  skopiowany plik. 
 
Jeśli  włą czany  do  projektu  plik  źró dłowy  już  istnieje,  to  należ y  skopiować   go  do  katalogu  projektowego 
(posługują c  się  np.  Eksploratorem  Windows),  a  następnie  postą pić   tak,  jak  podczas  dodawania  pliku  do 
projektu.  
 
 

Dopasowanie oblicza 

 
Oblicze  środowiska  uruchomieniowego  składa  się  z  menu  oraz  z  paskó w,  któ re  moż na  konfigurować .  Odbywa 
się to za pomocą  polecenia Tools / Customize umoż liwiają cego zarzą dzanie wyświetlaniem paskó w edycyjnych, 
uruchomieniowych i innych. 

background image

 

23

 
 

Uruchamianie programu 

 
Systematyczne wyszukiwanie błędó w w programie odbywa się za pomocą  uruchamiacza. W celu wyświetlenia 
paska zawierają cego jego narzędzia należ y wydać  polecenie Tools / Customize / Toolbars, a następnie odhaczyć  
nastawę Debug
 
Wykonanie programu nadzorowanego przez uruchamiacz zaczyna się w konfiguracji Win32 Debug po wydaniu 
polecenia Build / Start Debug / Step into (F10). Program zatrzyma się tuż  przed przystą pieniem do wykonania 
pierwszej funkcji (zazwyczaj funkcji main). 
 
Począ wszy od tego momentu moż na  
 
 

Określać  argumenty funkcji głó wnej 

 

 

Project / Settings // DebugProgram arguments 

 
 

Zastawiać  / usuwać  pułapki 

 

 

ikona Hand (F9

 
 

Usuwać  pułapki 

 

 

Edit / Breakpoints / Remove All (Alt-F9) 

 
 

Wykonywać  program krokowo 

 

 

ikona Go (po zastawieniu pułapki) 

 

 

ikona Step over (F10

 

 

ikona Step into (F11

 

 

ikona Step out (Shift-F11

 
 

Obserwować  zmienne 

 

 

ikona Quick Watch (Shift-F9

 
 

Kompilacja warunkowa 

 
Podczas  uruchamiania  programu  przydaje  się  ignorowanie  jego  wybranych  fragmentó w.  Odbywa  się  to  za 
pomocą  dyrektyw kompilacji warunkowej: #if#else#endif
 
Zinterpretowanie dyrektywy 
 

 

#if c 

 

    kod-źró dł

owy 

 

#else 

 

    kod-alternatywny 

 

#endif 

 
zaczyna się od wyznaczenia wartości wyraż enia c (najczęściej liczby 1 albo 0). Jeśli wyraż enie ma wartość  ró ż ną  
od 0, to całą  dyrektywę zastępuje się napisem kod-źró dł

owy. W przeciwnym razie zastępuje się ją  napisem kod-

alternatywny
 
Uwaga: Jeśli napis kod-źró dł

owy jest pusty, to dyrektywę moż na zapisać  bez frazy #else

 

#include <iostream.h> 
 
int main(void) 

    int one, two; 
    cin >> one >> two; 
#if 1 

background image

 

24

    cout << "Sum = "; 
#endif 
    cout << one + two << endl; 
 
    return 0; 

 
Program  wyprowadza  sumę pary  danych  wejś ciowych  poprzedzają c  ją   napisem Sum =. Jeś li  w  dyrektywie #if 
zmieni się 1, na 0, to powstanie program, któ ry takiego napisu nie wyprowadzi.
 

background image

 

25

Wskaźniki i odnośniki 

 
 
 
 
Wskaźniki  i  odnośniki  są   zmiennymi,  któ re  służ ą   do  identyfikowania  innych  zmiennych.  Wskaźnik  moż e 
identyfikować   wiele  zmiennych  pokrewnego  mu  typu,  natomiast  odnośnik  moż e  identyfikować   tylko  jedną  
zmienną .  
 
Wskaźnikom  przypisuje  się  wskazania,  a  odnośnikom  odniesienia.  Mimo  iż   w  typowych  implementacjach 
zaró wno  wskazania  jak  i  odniesienia  są   reprezentowane  przez  adresy,  posługiwanie  się  pojęciem  adres  jest 
całkowicie  zbyteczne  i  dowodzi  myślenia  o  C++  nie  w  kategoriach  języka  wysokiego  poziomu,  ale  w 
kategoriach implementacji. Dlatego o adresach nie będzie już  mowy. 
 
 

Zmienne wskaźnikowe 

 
Wskaźnikiem jest zmienna, któ rej moż na przypisywać  wskazania. Deklarację wskaźnika moż na poznać  po tym, 
ż e jej identyfikator jest poprzedzony symbolem (gwiazdka). 
 
Jeśli w pewnym miejscu programu jest wymagane uż ycie wskazania zmiennej, to otrzymuje się je poprzedzają c 
nazwę zmiennej operatorem wskazywania & (ampersand). 
 
Po  przypisaniu  wskaźnikowi  ptr  wskazania  zmiennej,  napis  *ptr  staje  się  chwilową   nazwą   tej  zmiennej.  Po 
przypisaniu wskaźnikowi wskazania pustego (reprezentowanego przez liczbę 0), uż ycie nazwy *ptr albo nazwy 
jej ró wnoważ nej (np. ptr[0]) jest zabronione. 
 

int fix1 = 10,  
    fix2 = 20; 
 
int *ptr = &fix1; 
cout << *ptr;         // 10 
*ptr = 11; 
cout << *ptr << fix;  // 11 11 
ptr = &fix2; 
cout << *ptr;         // 20 
*ptr = 22; 
cout << *ptr << fix;  // 22 22 
ptr = 0; 
cout << *ptr;         // b

łąd 

 
Wskaźnik  ptr  jest  przystosowany  do  wskazywania  zmiennych  typu  int.  Przypisano  mu  kolejno:  wskazanie 
zmiennej fix1, wskazanie zmiennej fix2 i wskazanie puste. 
 
Po  przypisaniu  wskaźnikowi  ptr  wskazania  zmiennej  fix1,  napis  *ptr  jest  chwilową   nazwą   zmiennej  fix1,  a  po 
przypisaniu mu wskazania zmiennej fix2, jest chwilową  nazwą  zmiennej fix2
 
Po  przypisaniu  wskaźnikowi  ptr  wskazania  pustego,  aż  do  chwili  przypisania  mu  wskazania  zmiennej,  użycie 
nazwy *ptr jest zabronione. 
 
 

Dla dociekliwych 

 
Typ wyraż enia inicjują cego wskaźnik musi być  zgodny z typem wskaźnika. Przyjmuje się z definicji, ż e zgodne 
ze  wskaźnikiem  typu  Type  jest  każ de  wyraż enie  typu  Type  oraz  każ de  wyraż enie,  któ re  moż e  być   poddane 
niejawnej konwersji do typu Type (por. Dodatek C). 
 

background image

 

26

char *ptr1 = "0\0\0\0"           // niejawna konwersja 
int *ptr2 = "0\0\0\0";           // b

łąd 

int *ptr = (int *)"0\0\0\0";     // jawna konwersja 
cout << *ptr;                    // 48 (kod cyfry 0) 

 
Skutek  użytej  tu jawnej konwersji  zależy od implementacji. W Visual  C++ powoduje to potraktowanie  obszaru 
pamięci zajętego przez pierwsze 4 bajty literał

u jako zmiennej cał

kowitej. 

 
 

Wskaźniki i tablice 

 
Zwią zki między wskaźnikami i tablicami są  bardzo bliskie. Każ da nazwa tablicy jest niejawnie przekształcana na 
wskaźnik jej zerowego elementu, a każ da nazwa wskaźnika moż e być  indeksowana tak, jak nazwa tablicy. 
 
Jeśli  wskaźnik  ptr  wskazuje  pewien  element  tablicy,  to  zaró wno  *ptr  jak  i  ptr[0]  jest  nazwą   tego  elementu. 
Elementy położ one z lewej strony elementu wskazywanego maj ą  nazwy ptr[-1]ptr[-2], itd., a elementy położ one 
z prawej mają  nazwy ptr[1]ptr[2], itd. 
 
Jeśli  i  jest  wyraż eniem  całkowitym,  to  wyraż enie ptr+i jest wskaźnikiem  elementu  odległego o i elementó w od 
wskazywanego (dla i ujemnego - w lewo, a dla i dodatniego - w prawo). 
 
Jeśli wskaźniki ptr1 ptr2 wskazują  odpowiednio elementy o indeksach  oraz tej samej n-elementowej tablicy 
(a takż e gdy wskazują  nie istnieją ce "elementy" o indeksach -1 i n), to wyraż enie ptr1-ptr2 ma wartość  i-j
 

int vec[3] = { 10, 20, 30 }; 
int *ptr = vec + 2; 
cout << ptr++[-1];             // 20 
cout << *(vec + 2);            // 30 
cout << vec - ptr;             // -3 

 
Nazwa vec zostaje niejawnie przekształ

cona na wskazanie elementu vec[0], to jest na &vec[0]

 
Wyrażenie vec + 2  wskazuje element o wartoś ci 30
 
Wyrażenie ptr++[-1] jest nazwą  elementu o wartoś ci 20
 
W  wyrażeniu  vec - ptr  pierwszy  argument  wskazuje  element  zerowy,  a  drugi  argument  wskazuje  nie  istnieją cy 
element vec[3]
 
 

Wskaźniki i struktury 

 
Jeśli wskaźnik  ptr wskazuje strukturę o  polu f, to nazwą   zmiennej  odpowiadają cej  temu polu  jest  (*ptr).f, albo 
kró cej ptr->f
 

#include <iostream.h> 
 
struct Child { 
    char name[20]; 
    int age; 
    Child *pNext; 
}; 
 
Child bob = { "Robert", 20 }, 
      tom = { "Thomas", 30, 0 }; 
 
Child *pBob = &bob, 
      *pTom = &tom; 
 
int main(void)  

    cout << pBob->name << endl;         // Robert 
 

background image

 

27

    pBob->pNext = pTom; 
     
    cout << pBob->pNext->age << endl;   // 30 
 
    return 0; 

 
Zmienna  pBob  jest  wskaźnikiem  przystosowanym  do  wskazywania  zmiennych  typu  Child.  Przypisano  jej 
wskazanie struktury bob
 
Napis pBob->name jest chwilową  nazwą  tego elementu struktury bob, któ ry jest opisany przez pole name
 
Napis  pBob->pNext  jest  nazwą   wskaźnika  opisanego  przez  pole  pNext.  Ponieważ  wskazuje  on  strukturę tom
więc pBob->pNext->age jest nazwą  tego elementu struktury tom, któ ry jest opisany przez pole age
 
 

Tablice wskaźników 

 
Tablicą   wskaźnikó w  jest  tablica,  któ rej  elementami  są   wskaźniki.  W  deklaracji  tablicy  wskaźnikó w  jej 
identyfikator jest poprzedzony znakiem * (gwiazdka). 
 
W deklaracji wskaźnika, któ ry służ y do wskazywania-wskaźnikó w, jego identyfikator jest poprzedzony dwiema 
znakami * (gwiazdka). 
 

#include <iostream.h> 
 
const int Count = 3; 
 
struct Child { 
    char name[20]; 
    int age; 
}; 
 
Child john = { "John Smith",  30 }, 
      tom  = { "Thomas Mill", 10 }, 
      bill = { "Robert Dole", 20 }; 
       
Child *pBoys[Count] = { &john, &tom, &bill }; 
 
int main(void) 
{    
    for(int i = 0; i < Count-1 ; i++) { 
        int minAge = pBoys[i]->age; 
        for(int j = i+1; j < Count ; j++) { 
            if(pBoys[j]->age < minAge) { 
                minAge = pBoys[j]->age; 
                Child *ptr = pBoys[i]; 
                pBoys[i] = pBoys[j]; 
                pBoys[j] = ptr; 
            } 
        } 
    } 
 
    Child **ptr = pBoys; 
    for(i = 0; i < Count ; i++) 
        cout << (*ptr++)->name << endl; 
 
    return 0; 

 
Program  wyprowadza  nazwiska  chł

opcó w,  w  kolejnoś ci  ich  rosną cego  wieku.  Sortowanie  dotyczy  tylko 

elementó w tablicy wskaźnikó w i nie powoduje kopiowania struktur typu Child
 
 

Wskaźniki a ustalenia 

background image

 

28

 
Podobnie jak zwykła zmienna, tak i wskaźnik moż e być  ustalony albo nie-ustalony. Ponadto wskaźnik moż e być  
przystosowany do wskazywania zmiennych ustalonych albo nie-ustalonych. Daje to cztery moż liwości. 
 
Uwaga: Zabrania się, aby wskaźnikowi przystosowanemu do wskazywania zmiennych nie-ustalonych przypisano 
wskazanie zmiennej ustalonej. 
 

#include <iostream.h> 
 
int main(void) 
{     
 
    int mod = 10; 
    const int fix = 20; 
 
    int *ptr1 = &mod; 
    int *const ptr2 = &mod;         
    const int *ptr3 = &mod; 
    const int *const ptr4 = &fix; 
 
    cout << *ptr1 << endl;  // 10 
    cout << *ptr2 << endl;  // 10 
    cout << *ptr3 << endl;  // 10 
    cout << *ptr4 << endl;  // 20 
 
    ptr1 = &fix;            // b

łąd 

    ++ptr2;                 // b

łąd 

    ++*ptr3;                // b

łąd 

 
    ptr1 = &(int &)fix;     // dozwolone 
    cout << *ptr1 << endl;  // 20 
 
    return 0; 

 
Wskaźnik ptr1 

uży do wskazywania zmiennych nie-ustalonych. Wskaźnik ptr2 jest wskaźnikiem ustalonym, któ ry 

uży do wskazywania zmiennych nie-ustalonych. Wskaźnik ptr3 jest wskaźnikiem nie-ustalonym, któ ry sł

uży do 

wskazywania  zmiennych  ustalonych.  Wskaźnik  ptr4  jest  wskaźnikiem  ustalonym,  któ ry  sł

uży  do  wskazywania 

zmiennych ustalonych. 
 
 

Zmienne odnośnikowe 

 
Odnośnikiem jest zmienna, któ rą  moż na zainicjować  odniesieniem. Deklarację odnośnika moż na poznać  po tym, 
ż e jej identyfikator jest poprzedzony symbolem (ampersand). Istnieją  odnośniki do zmiennych, ale nie istnieją  
tablice odnośnikó w. Każ dy odnośnik musi być  zainicjowany. 
 
Uwaga:  Jeśli  w  pewnym  miejscu  programu  występuje  nazwa  zmiennej,  a  program  byłby  poprawny  tylko 
wó wczas, gdyby występowała tam nazwa odnośnika do zmiennej, to nazwę zmiennej niejawnie przekształca się 
w odnośnik. 
 

int fix = 10; 
int &ref = fix;    // int &ref = (int &)fix; 

 
Ponieważ odnoś nik  ref jest  typu int &, więc nie może być zainicjowany wartoś cią  zmiennej fix, któ ra jest typu 
int.  Dlatego,  za  pomocą   niejawnej  konwersji  (int  &)fix,  nazwę  zmiennej  fix  niejawnie  przekształ

ca  się  w 

odnoś nik. 
 
Po zainicjowaniu odnośnika ref odniesieniem do zmiennej, napis ref staje się trwałą  nazwą  tej zmiennej. A więc 
odnośnik moż na zainicjować , ale nie moż na mu przypisać  odniesienia. 
 

#include <iostream.h> 
 

background image

 

29

int main(void)  

    int fix = 10; 
    int &ref = fix; 
    ref = 10; 
    cout << fix << ref << endl; // 10 10 
 
    return 0; 

 
Po zainicjowaniu odnoś nika, napis ref staje się trwał

ą  nazwą  zmiennej fix. Dlatego przypisanie ref = 10 zmienia 

wartoś ć zmiennej fix, ale nie zmienia wartoś ci odnoś nika ref. 
 
 

Dla dociekliwych 

 
Typ  wyraż enia  inicjują cego  odnośnik  musi  być   zgodny  z  typem  odnośnika.  Przyjmuje  się  z  definicji,  ż e  typ 
"odnoś nik do zmiennej typu Type" (np. int &) jest zgodny z typem Type (np. int). Jeśli wyraż enie inicjują ce jest 
innego typu, to moż e być  poddane niejawnej konwersji do typu zgodnego, ale tylko wó wczas, gdy typ odnośnika 
jest ustalony (const). W takim wypadku odnośnik zostanie zainicjowany odniesieniem do zmiennej pomocniczej 
typu z nim zgodnego, zainicjowanej wartością  wyraż enia po konwersji. 
 

int &ref1 = 2.4;           // b

łąd 

const int &ref = 2.4; 

 
Identyfikator ref2 jest trwał

ą  nazwą  zmiennej pomocniczej o wartoś ci (int)2.4

 
 

Wskaźniki i odnośniki 

 
Podejmują c  decyzję  o  uż yciu  wskaźnika,  czy  odnośnika,  należ y  kierować   się  wytyczną ,  ż e  wszędzie  tam  gdzie 
jest to moż liwe, należ y stosować  odnoś niki, gdyż  zwiększa to czytelność  programu.  
 
W  rzadkich  przypadkach  stosuje  się  odnośniki  do  wskaźnikó w.  Jest  to  niezbędne  wó wczas,  gdy  poprzez 
odnośnik należ y zmodyfikować  wskaźnik. 
 

#include <iostream.h> 
 
int main(void) 

    int vec[3] = { 10, 20, 30 }; 
    int *ptr = vec; 
    int *&ref = ptr; 
 
    ++ref; 
 
    cout << *ptr << endl;            // 20 
 
    return 0; 

 
Po zadeklarowaniu odnoś nika, napis ref jest trwał

ą  nazwą  wskaźnika ptr. Dlatego po wykonaniu operacji ++ref 

wskaźnik ptr wskazuje element vec[1] o wartoś ci 20
 
Gdyby  z  deklaracji  odnoś nika  usunięto  znak  &,  to  napis  ref  stał

by  się  nazwą   wskaźnika  zainicjowanego 

wskazaniem elementu vec[0], a wykonanie operacji ++ref nie miał

oby żadnego wpł

ywu na wskaźnik ptr. W takim 

wypadku nastą pił

oby wyprowadzenie liczby 10

background image

 

30

Przetwarzanie łań cuchów 

 
 
 
 
Ł ańcuchem jest dowolna sekwencja elementó w tablicy znakowej, zakończona elementem o wartości 0. Ponieważ  
każ dy literał łańcuchowy jest nazwą  takiej właśnie sekwencji elementó w, więc jest nazwą  łańcucha. 
 
W  szczegó lności,  literał  "Hello"  jest  nazwą   6-elementowej  tablicy  znakowej,    któ rej  element  "Hello"[0]  ma 
wartość  ’H’, a element "Hello"[5] ma wartość  0
 
Do  typowych  operacji  wykonywanych  na  łańcuchach  należ ą :  wprowadzenie  i  wyprowadzenie  łańcucha, 
wyznaczenie  długości  łańcucha  (strlen),    skopiowanie  łańcucha  (strcpy),  połą czenie  łańcuchó w  (strcat)  i 
poró wnanie  łańcuchó w  (strcmp).  Operacje  te  moż na  wykonać   za  pomocą   funkcji  bibliotecznych, 
zadeklarowanych w nagłó wku string.h
 
Uwaga: Jeśli wskaźnik wskazuje pierwszy element łańcucha, to mó wi się w skró cie, ż e wskazuje łańcuch.. 
 

int strlen(char *pStr) 

Dostarcza liczbę znakó w łańcucha wskazanego przez argument. 
np. 

cout << strlen("Hello");              // 5 

 

char *strcpy(char *pTrg, const char *pSrc) 

Dostarcza  pierwszy  argument.  Ponadto  kopiuje,  począ wszy  od  miejsca  wskazanego  przez  pierwszy  argument, 
łańcuch wskazany przez drugi argument. 
np. 

char buf[100] = "Hello "; 
cout << strcpy(buf + 6, "World")- 6;  // Hello World 

 

char *strcat(char *pTrg, const char *pSrc) 

Dostarcza  pierwszy  argument.  Ponadto  kopiuje,  poczawszy  od  miejsca,  w  któ rym  znajduje  się  znak  końca 
łańcucha wskazanego przez pierwszy argument, łańcuch wskazany przez drugi argument. 
np. 

char buf[100] = "Hello "; 
cout << strcat(buf, "World");         // Hello World 

 

int strcmp(const char *pOne, const char *pTwo) 

Dostarcza wartość  +1 jeśli łańcuch wskazany przez pierwszy argument jest wię kszy niż  łańcuch wskazany przez 
drugi argument, dostarcza wartość  0 jeśli są  ró wne, albo wartość  -1 jeśli pierwszy jest mniejszy.  
Uwaga: Poró wnanie  łańcuchó w  zastępuje  się  poró wnaniem  pierwszej  pary  znakó w  ró ż nych.  Jeśli  jeden  z 
łańcuchó w jest podłańcuchem drugiego, to za większy uznaje się dłuż szy.  
np. 

cout << strcmp("abc", "abaaaaa");     // -1 
cout << strcmp("abcde", "ab");        // 1 
cout << strcmp("ab", "ab");           // 0 

 
 

Wprowadzanie i wyprowadzanie łań cuchów 

 
Operacja  wprowadzenia  łańcucha  ma  postać   cin >> ptr,  w  któ rej  ptr  jest  wskaźnikiem  elementu  tablicy 
znakowej.  Jej  wykonanie  powoduje  umieszczenie  w  tablicy,  począ wszy  od  jej  elementu  *ptr,  kodó w  spó jnego 
cią gu znakó w wejściowych oraz kodu znaku ’\0’ (o wartości 0).  
 
Przed  wprowadzeniem  znakó w  zostaną   pominięte  odstępy  wiodą ce.  W  celu  zabezpieczenia  się  przed 
przepełnieniem tablicy moż na uż yć  manipulatora setw.  
 

background image

 

31

Operacja  wyprowadzenia  łańcucha  ma  postać   cout << ptr,  w  któ rej ptr jest wskaźnikiem. Zabrania się, aby  ptr 
było wskaźnikiem elementu tablicy, któ ry nie jest zerowym elementem łańcucha. 
 

#include <iostream.h> 
#include <string.h> 
 
const int Size = 100; 
 
char buffer[Size] = "prof. "; 
 
int main(void)  

    int len1 = strlen(buffer); 
    cin >> buffer + len1; 
    int len2 = strlen(buffer); 
    buffer[len2] = ’ ’; 
    buffer[len2+1] = 0; 
    cin >> buffer + len2 + 1; 
    cout << buffer << endl; 
    cout << "dr " << buffer + len1 << endl; 
 
    return 0; 

 
Jeś li z klawiatury wprowadzi się imię i nazwisko (np. Jan Bielecki), to program wyprowadzi to imię i to nazwisko 
poprzedzone napisem prof. (np. prof. Jan Bielecki), a ponadto tylko to imię i to nazwisko.  
 
 

Wyznaczenie długości 

 

#include <iostream.h> 
#include <string.h> 
 
char str[6] = "Hello"; 
 
int main(void)  

    cout << strlen("Hello") << endl;  // 5 
 
    char *ptr = str; 
    int len = 0; 
    while(*ptr != 0) { 
        len++; 
        ptr++; 
    } 
    cout << len << endl;              // 5 
 
    ptr = str; 
    len = 0; 
    while(*ptr++) 
        len++; 
    cout << len << endl;              // 5 
 
    return 0; 

 
Pokazano trzy sposoby wyznaczenia dł

ugoś ci ł

ańcucha zapisanego w tablicy znakowej. 

 
Wyrażenie  *ptr++  jest  nazwą   zmiennej,  wskazywanej  przez  wskaźnik  ptr,  przed  wykonaniem  na  nim  operacji 
zwiększenia.  
 
 

Kopiowanie 

 

#include <iostream.h> 
#include <string.h> 
 
char src[7] = "Hello "; 

background image

 

32

char trg[100]; 
 
int main(void)  

    char *pSrc = src, 
         *pTrg = trg; 
     
    strcpy(pTrg, pSrc); 
    cout << trg << endl;              // Hello 
 
    while(*pSrc != 0) { 
        *pTrg = *pSrc; 
        pSrc++; 
        pTrg++; 
    } 
    pTrg = 0; 
    cout << trg << endl;              // Hello 
 
    pSrc = src; 
    pTrg  = trg; 
    while(*pTrg++ = *pSrc++) 
        ; 
    cout << trg << endl;              // Hello 
 
    return 0; 

 
Pokazano trzy sposoby kopiowania ł

ańcucha znakó w. 

 
 

Łą czenie 

 

#include <iostream.h> 
#include <string.h> 
 
char *pSrc = "Hello", 
     buf[100]; 
 
int main(void)  

    strcat(strcpy(buf, pSrc), "!"); 
    cout << buf << endl;              // Hello! 
 
    char *pBuf = buf; 
    strcpy(pBuf, pSrc); 
    while(*pBuf++) 
        ; 
    char *pSrc = "!"; 
    while(pBuf++[-1] = *pSrc++) 
        ; 
    cout << buf << endl;              // Hello! 
 
    return 0; 

 
Pokazano dwa sposoby ł

ą czenia ł

ańcuchó w. 

 
 

Porównanie 

 

#include <iostream.h> 
#include <string.h> 
 
char one[100], 
     two[100]; 
 
int main(void)  

    cin >> one >> two; 

background image

 

33

 
    cout << one; 
    switch(strcmp(one, two)) { 
        case +1: 
            cout << " > "; 
            break; 
        case -1: 
            cout << " < "; 
            break; 
        default: 
            cout << " == "; 
    } 
    cout << two << endl; 
     
    char *pOne = one, 
         *pTwo = two; 
    cout << one; 
    while(*pOne == *pTwo && *pOne != 0) { 
        pOne++; 
        pTwo++; 
    } 
    if(*pOne == 0 && *pTwo == 0) 
        cout << " == "; 
    else if(*pOne > *pTwo) 
        cout << " > "; 
    else 
        cout << " < "; 
    cout << two << endl; 
 
    pOne = one; 
    pTwo = two; 
    cout << one; 
    while(*pOne || *pTwo) { 
        if(*pOne++ != *pTwo++) { 
            if(pOne[-1] > pTwo[-1]) 
                cout << " > "; 
            else  
                cout << " < "; 
            cout << two << endl; 
            return 0; 
        } 
    } 
    cout << " == "; 
    cout << two << endl; 
 
    return 0; 

 
Pokazano trzy sposoby poró wnywania ł

ańcuchó w wprowadzonych z klawiatury. 

 

background image

 

34

Posługiwanie się  funkcjami 

 
 
 
 
Funkcja  jest  sparametryzowanym  opisem  czynności.  W  miejscu  wywołania  funkcji  musi  być   znana  jej 
deklaracja albo definicja. W szczegó lności oznacza to, ż e wywołanie 
 
sum(10, 20)  
 
funkcji sumują cej argumenty, musi być  poprzedzone 
 
albo jej definicją  
 

int sum(int one, int two) 

    return one + two; 

 
albo jej deklaracją  
 

int sum(int one, int two); 

 
albo włą czeniem nagłó wka zawierają cego deklarację. 
 
 
Uwaga: W deklaracji funkcji moż na pominą ć  dowolny zestaw identyfikatoró w parametró w. Jeśli uczyni się to w 
definicji, to uniemoż liwi to odwoływanie się do argumentó w. 
 
 

Parametry i argumenty 

 
Wywołanie  funkcji  zaczyna  się  od  skojarzenia  jej  parametró w  z  argumentami.  Skojarzenie  parametru  z 
argumentem odbywa się przez-wartość, co oznacza, ż e parametr jest traktowany tak, jak lokalna zmienna funkcji, 
zadeklarowana tuż  przed jej pierwszą  instrukcją  i zainicjowana wartością  argumentu. 
 
A zatem, jeśli definicją  funkcji jest 
 

int sum(int one, int two) 

    return one + two; 

 
to dla wywołania  
 

sum(10, 20) 

 
funkcja jest traktowana tak, jakby miała postać  
 

int sum() 

    int one = 10; 
    int two = 20; 
 
    return one + two; 

 
 

background image

 

35

Parametry zwykłe 

 
Parametr  funkcji  jest  "zwykły",  jeśli  nie  jest  wskaźnikiem  ani  odnośnikiem.  Z  parametrem  zwykłym  moż na 
skojarzyć  argument, któ ry jest takiego samego typu jak parametr, albo któ ry moż na poddać  niejawnej konwersji 
do typu parametru.  
 
Zainicjowanie  parametru  polega  na  skopiowaniu  argumentu.  Jeśli  argument  jest  strukturą ,  to  kopiuje  się 
wszystkie jej elementy (co w przypadku duż ych struktur ma oczywiste wady!). 
 
Po  dokonaniu  skojarzenia,  wszelkie  operacje  wykonywane  na  parametrze  dotyczą   lokalnej  zmiennej 
zainicjowanej argumentem i nie powodują  zmiany wartości skojarzonego z nim argumentu. 
 

#include <iostream.h> 
 
int main(void)  

    void inc(int par); 
    int fix = 10; 
    cout << fix << endl;      // 10 
    inc(fix); 
    cout << fix << endl;      // 10     
 
    return 0; 

 
void inc(int par) 

    ++par; 

 
Program  potwierdza,  że  wykonanie  operacji  na  parametrze  "zwykł

ym"  nie  powoduje  zmiany  wartoś ci 

skojarzonego z nim argumentu. 
 
 

Parametry wskaźnikowe 

 
Z  parametrem  wskaźnikowym  moż na  skojarzyć   argument,  któ ry  jest  takiego  samego  typu  jak  parametr,  albo 
któ ry moż na poddać  niejawnej konwersji do typu parametru.  
 
Zainicjowanie  parametru  polega  na  skopiowania  wskaźnika.  Nie  pocią ga  to  za  sobą   kopiowania  zmiennej 
identyfikowanej przez argument (co moż na wykorzystać  w przypadku duż ych struktur!). 
 
Po  dokonaniu  skojarzenia,  wszelkie  operacje  wykonywane  na  parametrze  dotyczą   lokalnej  zmiennej 
zainicjowanej argumentem, ale operacje wykonywane za po średnictwem parametru (np. *parpar[i] albo par->f
dotyczą  zmiennej wskazywanej przez argument. Moż e to mieć  wpływ na wartość  argumentu. 
 

#include <iostream.h> 
 
int main(void)  

    void inc(int *ptr); 
    int fix = 10; 
    cout << fix << endl;      // 10 
    inc(&fix); 
    cout << fix << endl;      // 11     
 
    return 0; 

 
void inc(int *ptr) 

    ++*ptr; 

 

background image

 

36

Program  potwierdza,  że  wykonanie  operacji  za  poś rednictwem  parametru  wskaźnikowego  może  powodować 
zmianę wartoś ci zmiennej wskazywanej przez skojarzony z nim argument. 
 
 

Parametry tablicowe 

 
Każ dy  parametr  tablicowy  jest  niejawnie  zastępowany  parametrem  wskaźnikowym,  któ ry  powstaje  po 
zastą pieniu deklaratora vec[i] deklaratorem (* const vec)
 
W szczegó lności funkcja 
 

int sum(int tab[20]) 

    int sum = 0; 
    for(int i = 0; i < 3 ; i++) 
        sum += tab[i]; 
    return sum; 

 
jest niejawnie przekształcana w funkcję 
 

int sum(int *const tab) 

    int sum = 0; 
    for(int i = 0; i < 3 ; i++) 
        sum += tab[i]; 
    return sum; 

 
Powoduje  to,  ż e  jeśli  chce  się  zdefiniować   funkcję  do  sumowania  tablic,  nie  odwołują cą   się  do  zmiennych 
globalnych, to jeden z jej argumentó w musi określać  liczbę elementó w tablicy. 
 

#include <iostream.h> 
 
int sum(int tab[], int count) 

    int sum = 0; 
    for(int i = 0; i < count ; i++) 
        sum += tab[i]; 
    return sum; 

 
int main(void) 

    int small[] = { 10 }, 
        large[] = { 10, 20, 30 }; 
 
    cout << sum(small, 1) << endl; 
    cout << sum(large, 3) << endl; 
 
    return 0; 

 
 

Parametry odnośnikowe 

 
Z  parametrem  odnośnikowym  moż na  skojarzyć   argument,  któ ry  jest  takiego  samego  typu  jak  parametr,  albo 
któ ry moż na poddać  niejawnej konwersji do typu parametru.  
 
Zainicjowanie  parametru  polega  na  skopiowania  odnośnika.  Podobnie  jak  dla  parametru  wskaźnikowego,  nie 
pocią ga to za sobą  kopiowania zmiennej identyfikowanej przez argument. 
 
Po  dokonaniu  skojarzenia,  wszelkie  operacje  wykonywane  na  parametrze  dotyczą   zmiennej  identyfikowanej 
przez argument. Moż e to powodować  zmianę wartości skojarzonego z nim argumentu. 

background image

 

37

 

#include <iostream.h> 
 
int main(void)  

    void inc(int &ref); 
    int fix = 10; 
    cout << fix << endl;      // 10 
    inc(fix); 
    cout << fix << endl;      // 11     
 
    return 0; 

 
void inc(int &ref) 

    ++ref; 

 
Program  potwierdza,  że  wykonanie  operacji  za  poś rednictwem  parametru  odnoś nikowego  może  powodować 
zmianę wartoś ci skojarzonego z nim argumentu. 
 
 

Parametry funkcji main 

 
Funkcja  głó wna  moż e  być   zadeklarowana  jako  bezparametrowa  albo  dwu-parametrowa.  Jeśli  jest 
dwuparametrowa,  to  jej  pierwszy  parametr  jest  typu  int  i  ma  wartość   ró wną   liczbie  argumentó w  programu 
zwiększonej  o  1,  a  drugi  jest  typu  char  *[]  i  jest  tablicą   odnośnikó w  do  łańcuchó w  zainicjowanych  nazwą  
programu oraz nazwami jego argumentó w. 
 

#include <iostream.h> 
 
int main(int argc, char *argv[]) 

    cout << "My name is: " << argv[0] << endl; 
    cout << "My arguments are: " << endl; 
    for(int i = 1; i < argc ; i++) 
        cout << argv[i] << endl; 
 
    return 0; 

 
Program wyprowadza swoją  nazwę i  argumenty; każde w osobnym wierszu. 
 
 

Skojarzenia powrotne 

 
W  chwili  zakończenia  wykonywania  funkcji  rezultatowej  (o  typie  ró ż nym  od  void)  następuje  skojarzenie  jej 
rezultatu  z  wyraż eniem  występują cym  w  instrukcji  powrotu.  Odbywa  się  to  według  tych  samych  zasad  co 
skojarzenie  parametru  z  argumentem  i  polega  na  zainicjowaniu  rezultatu  funkcji  wyraż eniem  występują cym  w 
instrukcji powrotu. 
 
Uwaga:  Rezultat  funkcji  jest  zmienną .  Typ  rezultatu  jest  identyczny  z  typem  funkcji.  Nazwą   rezultatu  jest 
wywołanie  funkcji.  Z  punktu  widzenia  łą czenia  operacji  (np.  ++*fun(1,2)+3),  nazwa  funkcji  jest  zastępowana 
nazwą  rezultatu. 
 

#include <iostream.h> 
#include <math.h> 
 
double sqr(double val) 

    return val * val; 

 

background image

 

38

int main(void) 
{    
    double a, b; 
    cin >> a >> b; 
    cout << sqrt(sqr(a) + sqr(b)) << endl; 
 
    return 0; 

 
Wywoł

anie sqr(a) jest nazwą  rezultatu o wartoś ci "kwadrat a", wywoł

anie sqr(b) jest nazwą  rezultatu o wartoś ci 

"kwadrat b",  a wyrażenie sqrt(sqr(a) + sqr(b)) jest nazwą  rezultatu o wartoś ci "pierwiastek z sumy kwadrató w 
b". 
 
 

Typ nie-odnośnikowy 

 
Jeśli typ funkcji jest nie-odnoś nikowy, to zainicjowanie rezultatu polega na skopiowaniu zmiennej, któ rej nazwą  
jest wyraż enie występują ce w instrukcji powrotu. 
 
Jeśli typ wyraż enia nie jest identyczny z typem funkcji, to wyraż enie jest poddawane konwersji do typu rezultatu. 
Zezwala się na niejawne wykonanie co najwyżej jednej konwersji standardowej i jednej definiowanej. 
 

#include <iostream.h> 
 
double getSqr(int par); 
 
int main(void)  

    cout << getSqr(3) << endl;  // 9 
 
    return 0; 

 
double getSqr(int par) 

    return par * par;     // return double(par * par); 

 
Wyrażenie par * par jest nazwą  zmiennej typu int zainicjowanej daną  o wartoś ci 9
 
Ponieważ  typ  rezultatu  jest  ró żny  od  typu  tej  zmiennej,  więc  zostanie  zastosowana  niejawna  konwersja 
standardowa z typu int do double
 
Po zainicjowaniu rezultatu zmienną  double(par * par), wywoł

anie getSqr(3) można traktować nazwę rezultatu. 

 
 

dla dociekliwych 

 

#include <iostream.h> 
 
struct Child { 
    char name[20]; 
    int age; 
}; 
 
Child isa = { "Isabel", 15 }; 
 
Child getOlder(Child child, int val); 
void show(Child &child); 
 
int main(void)  

    show(isa);                // Isabel is 15 
    show(getOlder(isa, 2));   // Isabel is 17 
    show(isa);                // Isabel is 15 
 

background image

 

39

    return 0; 

 
Child getOlder(Child child, int val) 

    child.age += val; 
 
    return child; 

 
void show(Child &child) 

    cout << child.name << " is " <<  
            child.age << endl; 

 
Wywoł

anie funkcji getOlder(isa, 2) powoduje skopiowanie struktury isa do lokalnej zmiennej funkcji getOlder

 
Operacja child.age += val jest wykonywana na tej zmiennej lokalnej. 
 
Wywoł

anie getOlder(isa, 2) jest nazwą  zmiennej, do któ rej skopiowano tę zmienną  lokalną . 

 
 

Typ odnośnikowy 

 
Jeśli typ funkcji jest odnoś nikowy, to zainicjowanie rezultatu polega na skopiowaniu odnośnika do tej zmiennej, 
któ rej  nazwą   jest  wyraż enie  występują ce  w  instrukcji  powrotu.  A  zatem  wywołanie  funkcji  jest  nazwą   tej 
zmiennej. 
 

#include <iostream.h> 
 
int &refVal(void) 

    static int val = -1; 
 
    return ++val; 

 
int main(void) 

    cout << refVal() << endl;   // 0 
    cout << refVal() << endl;   // 1 
    refVal() = 5; 
    cout << refVal() << endl;   // 6 
    ++refVal(); 
    cout << refVal() << endl;   // 9 
 
    return 0; 

 
Wywoł

anie  refVal()  jest nazwą  statycznej zmiennej val. A zatem każda operacja wykonana na  refVal() dotyczy 

tej wł

aś nie zmiennej. 

 
 

dla dociekliwych 

 
Jeśli  typ  wyraż enia  w  instrukcji  powrotu  nie  jest  zgodny  z  typem  funkcji,  to  typ  funkcji  musi  być   ustalony 
(const), a ponadto musi istnieć  niejawna konwersja z typu wyraż enia do typu zgodnego z typem funkcji. 
 

#include <iostream.h> 
 
const int &refVal(double par) 

    return par * par;         // return double(par * par); 

 

background image

 

40

int main(void) 

    cout << refVal(3) << endl; 
 
    return 0; 

 
Uwaga: Wyraż enie zawarte w instrukcji powrotu moż e tylko wówczas identyfikować  zmienną  lokalną  funkcji, 
gdy typ funkcji jest ustalony
 

#include <iostream.h> 
 
int &getInc(int par); 
 
int main(void)  

    cout << getInc(3) << endl;   // b

łąd 

 
    return 0; 

 
int &getInc(int par) 

    return ++par; 

 
Wywoł

anie getInc(3) jest nazwą  lokalnej zmiennej par. Ponieważ po powrocie z funkcji getInc zmienna par już 

nie istnieje, więc odwoł

anie się do niej jest zabronione. W Visual C++ program wyprowadza liczbę 4

 
Program można poprawić, nadają c mu postać 

 
#include <iostream.h> 
 
const int &getInc(int par); 
 
int main(void)  

    cout << getInc(3) << endl;   // 4 
 
    return 0; 

 
const int &getInc(int par) 

    return ++par; 
}  

 
 

Deklarowanie funkcji 

 
Deklaracja  funkcji  podaje  jej  identyfikator  oraz  określa  typ  funkcji  oraz  typy  jej  parametró w.  Jeśli  ponadto 
podaje ciało funkcji, to jest jej definicją 
 
 

Funkcje bezrezultatowe 

 
Funkcja,  któ rej  typem  jest  void,  jest  funkcją   bezrezultatową .  Jej  wywołanie  kończy  się  w  chwili  wykonania 
instrukcji powrotu nie zawierają cej wyraż enia, albo w chwili zakończenia wykonywania jej ciała. 
 

void outDiv(int a, int b) 

    if(b == 0) 
        return; 
    cout << a / b; 

background image

 

41

 
 

Funkcje otwarte i zamknię te 

 
Funkcja  zadeklarowana  ze  specyfikatorem  inline  jest  realizowana  jako  otwarta.  W  odró ż nieniu  od  funkcji 
zamknię tej,  ciało  funkcji  otwartej  wstawia  się  w  każ dym  miejscu  jej  wywołania.  Powoduje  to  przyspieszenie 
wykonania programu, ale niekiedy wydłuż a jego kod wynikowy. 
 
Uwaga: Wystą pienie specyfikatora  inline nie ma wpływu na skutek wykonania programu. Jeśli funkcja otwarta 
zostanie uznana za zbyt skomplikowaną , to moż e być  zrealizowana jako zamknięta. 
 

inline in sum(int a int b) 

    return a + b; 

 
 

Funkcje przecią żone 

 
Jeśli  w  pewnym  zakresie  są   widoczne  deklaracje  dwó ch  lub  większej  liczby  funkcji  o  takiej  samej  nazwie,  ale 
ró ż nią cych się typami parametró w, to ogó ł takich funkcji stanowi wieloaspektową  funkcję  przecią żoną 
 
W  miejscu  wywołania  funkcji  przecią ż onej  wywołuje  się  ten  z  jej  aspektó w,  do  któ rego  parametró w  najlepiej 
pasują   podane  argumenty.  Ma  to  miejsce  wó wczas,  gdy  istnieje  taki  aspekt,  ż e  do  każ dego  z  jego  parametró w 
podany argument pasuje nie gorzej niż  do pozostałych, ale istnieje taki parametr, do któ rego jeden z argumentó w 
pasuje lepiej niż  do pozostałych. 
 
Uwaga: Jeśli argument nie pasuje do parametru dokładnie, to moż e być  poddany konwersji dopasowują cej, ale 
im konwersja ta jest bardziej złoż ona, tym dopasowanie pierwotnego argumentu uznaje się za gorsze. 
 

#include <iostream.h> 
 
void out(char par); 
void out(int par); 
 
int main(void)  

    out(’a’); 
    out(2); 
    out(2.0);    // b

łąd (niejednoznaczność) 

 
    return 0; 

 
void out(char par) 

    cout << par << endl; 

 
void out(int par) 

    cout << par << endl; 

 
Argument  ’a’  typu  char  najlepiej  pasuje  do  parametru  typu  char,  a  argument  2  typu  int  najlepiej  pasuje  do 
parametru typu int
 
Argument 2.0 typu double pasuje ró wnie dobrze do parametru typu char jak i do parametru typu int. Ponieważ 
do żadnego z nich nie pasuje najlepiej, więc wywoł

anie out(2.0) jest bł

ędne. 

 
Gdyby z programu usunięto dowolną  z funkcji out, to wszystkie odwoł

ania do out był

yby poprawne. 

 
 

background image

 

42

Argumenty domniemane 

 
W  deklaracji  parametru  funkcji  moż e  wystą pić   inicjator  wyraż eniowy  określają cy  domniemaną   wartość  
argumentu kojarzonego z tym parametrem. 
 

int sum(int a, int b =0, int c =0); 

 
Jeśli pewien parametr wyposaż ono w argument domniemany, to każ dy z następnych parametró w takż e musi być  
wyposaż ony w argument domniemany. 
 

int sum(int a, int b =0, int c);   // b

łąd 

 
Z  każ dym  parametrem  nie  wyposaż onym  w  argument  domniemany  musi  być   skojarzony  jawny  argument. 
Końcowy zestaw argumentó w, dla któ rych podano domniemania, moż na pominą ć . W ich miejscu zostaną  uż yte 
argumenty domniemane. 
 

#include <iostream.h> 
 
int sum(int a, int b, int c =0, int d =0); 
 
int main(void)  

    cout << sum(1, 2, 3) << endl;   // 6 
    cout << sum(1, 2) << endl;      // 3 
    cout << sum(1) << endl;         // b

łąd 

 
    return 0; 

 
int sum(int a, int b, int c, int d) 

    return a + b + c + d; 

 
 

dla dociekliwych 

 
Wyraż enie  określają ce  wartość   argumentu  domniemanego  nie  musi  być  wyraż eniem stałym. W  takim wypadku 
jest opracowywane w kontekście jego deklaracji, a nie w kontekście jego uż ycia. 
 

#include <iostream.h> 
 
int p = 20; 
 
int sub(int a =p*p) 

    return a; 

 
int main(void)  

    int p = 10; 
    cout << sub() << endl;   // 400 
    ::p = 10; 
    cout << sub() << endl;   // 100 
 
    return 0; 

 
 

Wywołania rekurencyjne 

 

background image

 

43

Wywołanie  funkcji  jest  rekurencyjne,  jeśli  nastą pi  przed  powrotem  z  jej  poprzedniego  wywołania.  Uż ycie 
rekurencji  moż e  uczynić   program  czytelniejszym,  ale  w  wielu  wypadkach  powoduje  zwiększenie  rozmiaru 
pamięci operacyjnej niezbędnej do jego wykonania. 
 

#include <iostream.h> 
#include <limits.h> 
#include <stdlib.h> 
 
int sqrt(int par, int min =0, int max =INT_MAX) 

    int mid = (min + max) / 2; 
    if(mid == min) 
        return mid; 
    if(par < double(mid) * mid) 
        return sqrt(par, min, mid); 
    else 
        return sqrt(par, mid, max); 

 
int main(void) 
{     
    int val; 
    cin >> val; 
    val = abs(val); 
    cout << "sqrt(" << val << ") = " <<  
            sqrt(val) << endl; 
 
    return 0; 

 
Funkcja  sqrt  dostarcza  pierwiastek  z  jej  nieujemnego  argumentu.  Nieobowią zkowe  argumenty  dodatkowe 
okreś lają  przedział

, w któ rym znajduje się pierwiastek. 

 
 

Definiowanie funkcji 

 
Zdefiniowanie  funkcji  polega  na  podaniu  jej  ciała.  Dobry  styl  programowania  poznaje  się  po  uż yciu  wielu 
kró tkich, a nie małej liczby długich funkcji. 
 
Tak  dalece  jak  jest  to  moż liwe,  należ y  posługiwać   się  funkcjami  bibliotecznymi.  Ilustruje  to  następują cy 
program, któ ry napisano w dwó ch wersjach: z uż yciem i bez uż ycia funkcji bibliotecznych. 
 

#include <iostream.h> 
#include <iomanip.h> 
#include <string.h> 
 
const int Size = 100; 
 
int main(void)  

    char srcOne[Size], 
         srcTwo[Size]; 
 
    cin >> setw(Size) >> srcOne >> 
           setw(Size) >> srcTwo; 
 
    char trg[2*Size-1]; 
 
    strcat(strcpy(trg, srcOne), " "); 
    int len = strlen(strcat(trg, srcTwo)); 
 
    cout << trg << endl << len << endl; 
 
    return 0; 

 

background image

 

44

Program  wprowadza  dwa  ł

ańcuchy,  ł

ą czy  je  oddzielają c  spacją ,  a  następnie  wyprowadza:  ł

ańuch  docelowy, 

ugoś ć ł

ańcucha docelowego i wynik poró wnania ł

ańcuchó w źró dł

owych. 

 

#include <iostream.h> 
 
const int Size = 100; 
 
int strLen(char *ptr); 
char *strCpy(char *pTrg, char *pSrc); 
char *strCat(char *pTrg, char *pSrc); 
int strCmp(char *pOne, char *pTwo); 
 
int main(void)  

    char srcOne[Size], 
         srcTwo[Size]; 
 
    cin >> setw(Size) >> srcOne >> 
           setw(Size) >> srcTwo; 
 
    char trg[2*Size-1]; 
 
    strCat(strCpy(trg, srcOne), " "); 
    int len = strLen(strCat(trg, srcTwo)); 
 
    cout << trg << endl << len << endl; 
 
    cout << srcOne << ’ ’; 
    char chr = ’=’; 
    switch(strCmp(srcOne, srcTwo)) { 
        case +1: 
            chr = ’>’; 
            break; 
        case -1: 
            chr = ’<’; 
            break; 
    } 
    cout << chr << ’ ’ << srcTwo << endl;     
 
    return 0; 

 
int strLen(char *ptr) 

    int len = 0; 
    while(*ptr++) 
        len++; 
    return len; 

 
char *strCpy(char *pTrg, char *pSrc) 

    char *pTrg2 = pTrg; 
    while(*pTrg++ = *pSrc++); 
    return pTrg2; 

 
char *strCat(char *pTrg, char *pSrc) 

    char *pTrg2 = pTrg; 
    strCpy(pTrg += strLen(pTrg), pSrc); 
    return pTrg2; 

 
int strCmp(char *pOne, char *pTwo) 

    while(*pOne || *pTwo) 
        if(*pOne++ != *pTwo++) 
            if(pOne[-1] > pTwo[-1]) 
                return +1; 
            else  
                return -1; 

background image

 

45

    return 0; 

 

background image

 

46

Zarzą dzanie pamię cią  

 
 
 
 
Wykonanie programu polega na przepływie sterowania przez jego deklaracjedefinicje instrukcje
 
W pierwszej kolejności sterowanie przepływa  przez wszystkie deklaracje globalne (takie, któ re nie wchodzą  w 
skład  innych  deklaracji).  Następnie  jest  wyszukiwana  funkcja  główna  i  sterowanie  przepływa  przez  zawarte  w 
niej  instrukcje.  Przepływ  sterowania  kończy  się  po  powrocie  z  wywołania  funkcji  exit  albo  po  wykonaniu 
instrukcji powrotu z funkcji głó wnej. 
 

#include <iostream.h> 
#include <stdlib.h> 
 
int main(void) 

    int num; 
    cin >> num; 
    if(num != 0) { 
        cout << num << endl; 
        exit(num); 
    } 
 
    return 0; 

 
W  zależnoś ci  od  tego,  jaką   wartoś ć  ma  wprowadzona  liczba,  program  kończy  się  po  napotkaniu  instrukcji 
powrotu albo po wywoł

aniu funkcji exit

 
 

Zmienne statyczne 

 
Jeśli  sterowanie  przepłynie  przez  definicję  zmiennej  globalnej,  albo  przez  definicję  zmiennej  lokalnej 
zadeklarowanej  ze  specyfikatorem  static,  to  zostanie  utworzona  zmienna  statyczna.  Tuż   przed  zakończeniem 
wykonywania  programu  wszystkie  zmienne  statyczne  zostaną   zniszczone.  Odbędzie  się  to  w  kolejności 
odwrotnej do ich tworzenia. 
 
Uwaga:  Zmienna  statyczna  jest  tworzona  w  obszarze  statycznym.  Inicjator  zmiennej  statycznej  jest  brany  pod 
uwagę tylko podczas pierwszego opracowania jej deklaracji. 
 

#include <iostream.h> 
 
int main(void)  

    void fun(int par); 
 
    fun(10); 
    static int one = 1; 
    fun(20); 
 
    return 0; 

 
int two = 2; 
 
void fun(int par) 

    static int loc = par; 
    cout << loc << ’ ’ << par << endl; 
    loc++; 

background image

 

47

 
Zmienne  statyczne  one,  two,  loc  zostaną   utworzone  w  kolejnoś ci:  two,  loc,  one,  a  zostaną   zniszczone  w 
kolejnoś ci: oneloctwo
 
Program wyprowadzi dwie pary liczb: 10 10 11 20
 
 

Zmienne automatyczne 

 
Jeśli sterowanie przepłynie przez definicję zmiennej lokalnej, nie zadeklarowanej ze specyfikatorem static albo 
extern,  to  zostanie  utworzona  zmienna  automatyczna.  Jawny  albo  niejawny  inicjator  zmiennej  automatycznej 
będzie brany pod uwagę podczas każdego opracowania tej definicji. 
 
Uwaga:  Zmienne  automatyczne  tworzy  się  na  stosie.  Stos  jest  obszarem  pamięci,  w  któ rym  moż na  tworzyć  
zmienne, ale takim, ż e moż na je niszczyć  tylko w kolejności odwrotnej do ich tworzenia. 
 

void sub(void) 

    int num;         // int num = int(); 
    cout << num;     // b

łąd 

    // ... 

 
Zmienną  automatyczną  num wyposażono w niejawny inicjator = int() dostarczają cy wartoś ć nieokreś loną . 
 
 
Zmienna  automatyczna  zostanie  zniszczona  tuż   przed  zakończeniem  wykonywania  bloku  (wnętrza  instrukcji 
grupują cej), w któ rym ją  zadeklarowano. Jeśli w bloku zadeklarowano więcej niż  jedną  zmienną  automatyczną , 
to ich niszczenie odbędzie się w kolejności odwrotnej do ich tworzenia, ale przed przystą pieniem do niszczenia 
zmiennych statycznych. 
 
 

#include <iostream.h> 
 
int main(void)  

    int cnt = 2; 
    while(cnt > 0) { 
        int val = cnt--; 
        cout << val << endl; 
    } 
 
    return 0; 

 
int one = 10; 

 
Najpierw  zostanie  utworzona  zmienna  statyczna one,  a  po  niej  zmienna  automatyczna cnt.  Następnie  zostanie 
utworzona  i  zniszczona  zmienna  automatyczna  val  zainicjowana  wartoś cią   2,  a  po  tym  zostanie  utworzona  i 
zniszczona  zmienna  automatyczna  val  zainicjowana  wartoś cią   1.  Tuż  przed  wykonaniem  instrukcji  powrotu 
zostanie zniszczona zmienna cnt, a po niej zmienna one
 
 

Zmienne kontrolowane 

 
Zmienna  kontrolowana  powstaje  na  skutek  wykonania  operacji  new,  a  jest  niszczona  po  jawnym  wykonaniu 
operacji delete. Zmienne kontrolowane są  tworzone na stercie. Sterta jest obszarem pamięci, do któ rego moż na 
dokładać  zmienne, a następnie usuwać  je w dowolnej kolejności. 
 

background image

 

48

Jeśli  wykonanie  operacji  new  jest  niemoż liwe,  ponieważ   wyczerpano  obszar  sterty,  to  rezultatem  operacji 
przydzielenia pamięci jest wskaźnik pusty (o wartości reprezentowanej przez 0). 
 
Uwaga: Programiści rzadko badają  rezultat operacji new, bo są  z natury optymistami. 
 

int *ptr = new char [10000000]; 
if(ptr == 0) { 
    cout << "No memory" << endl; 
    exit(-1); 

 
 

Zmienne skalarne  

 
Wykonanie operacji 
 

 

new Type 

 
w któ rej Type jest opisem typu skalarnego (tj. nie-tablicowego!), powoduje utworzenie na stercie zmiennej typu 
Type. Rezultatem operacji jest wskaźnik zainicjowany wskazaniem utworzonej zmiennej. 
 
Wykonanie operacji 
 

 

delete ptr 

 
w któ rej ptr wskazuje zmienną  utworzoną  na stercie, powoduje zniszczenie tej zmiennej. 
 

#include <iostream.h> 
 
int main(void)  

    int *pOne = new int; 
    double &two = *new double; 
    two = 2.8; 
    *pOne = (int)two; 
    cout << *pOne << endl;   // 2 
    delete pOne; 
    delete &two; 
 
    return 0; 

 
Najpierw zostanie utworzona zmienna typu int, a następnie zmienna typu double. Najpierw zostanie zniszczona 
zmienna typu int, a następnie zmienna typu double
 
 

Zmienne tablicowe 

 
Wykonanie operacji 
 

 

new Type 

 
w któ rej Type jest opisem typu tablicowego (np. int [12]), powoduje utworzenie na stercie zmiennej typu Type
Rezultatem operacji jest wskaźnik zainicjowany wskazaniem zerowego elementu utworzonej tablicy. 
 
Jeśli elementami tablicy są  obiekty, to do ich zainicjowania jest niejawnie stosowany konstruktor domyślny. 
 
Uwaga: Wyraż enie określają ce liczbę elementó w tablicy nie musi być  wyraż eniem stałym. 
 
Wykonanie operacji 
 

 

delete [] ptr 

background image

 

49

 
w któ rej ptr wskazuje zerowy element tablicy utworzonej na stercie, powoduje zniszczenie tej tablicy.  
 

#include <iostream.h> 
#include <string.h> 
 
int main(void)  

    char *ptr = new char [100]; 
    cin >> ptr; 
    char &vec = *new char [strlen(ptr) + 1]; 
    cout << strcpy(&vec, ptr) << endl; 
    delete [] ptr; 
    delete [] &vec; 
 
    return 0; 

 
Program tworzy na stercie 100-elementową  tablicę znakową  i wprowadza do niej cią g znakó w. Następnie tworzy 
na  stercie  najmniejszą   tablicę,  w  któ rej  można  pomieś cić  wprowadzony  cią g  znakó w  oraz  tworzy  na  stosie 
odnoś nik vec identyfikują cy zerowy element tej tablicy. 
 
Przed zakończeniem wykonywania program niszczy obie tablice, w kolejnoś ci ich utworzenia. 
 
 

Ostrzeżenie 

 
W ż adnym wypadku nie wolno zmiennej utworzonej za pomocą  operacji new dla zmiennych skalarnych niszczyć  
za  pomocą   operacji  delete  dla  zmiennych  tablicowych,  a  zmiennej  utworzonej  za  pomocą   operacji  new  dla 
zmiennych tablicowych niszczyć  za pomocą  operacji delete dla zmiennych skalarnych. 
 
Nie  wolno  takż e  uż ywać   operacji  delete  ze  wskaźnikiem  ptr  identyfikują cym  co  innego  niż   zmienna  skalarna 
albo  zerowy  element  tablicy  utworzonej  za  pomocą   operacji  new,  ani  przyjmować ,  ż e  po  wykonaniu  operacji 
delete wskaźnik ptr ma wartość  określoną . 
 
Uwaga: W celu uniknięcia trudnych do wykrycia błędó w, zaleca się (o ile to moż liwe) zerowanie wskaźnika ptr 
bezpośrednio po uż yciu go w operacji delete
 

#include <iostream.h> 
 
int main(void)  

    int *ptr = new int [5]; 
    delete [] (ptr + 2);        // b

łąd 

 
    int &vec = *new int [5]; 
    delete &vec;                // b

łąd 

 
    int &ref = (*new int) = 3; 
    delete &ref; 
    cout << ref << endl;        // b

łąd 

 
    return 0; 

 
Mimo  iż  program  jest  poprawny  skł

adniowo,  zawiera  3  poważne  bł

ędy  logiczne.  Wykonany  w  ś rodowisku 

Visual C++, program ten zał

amuje system zarzą dzania stertą . 

background image

 

50

Widoczność deklaracji 

 
 
 
 
Identyfikatorem zmiennejfunkcji typu moż na posługiwać  się tylko w miejscu, w któ rym jest widoczna jego 
deklaracja.  
 
Zaleca  się,  aby  w  tym  samym  zakresie,  identyfikator  uż yty  do  zadeklarowania  zmiennej,  funkcji  albo  typu  nie 
został uż yty do zadeklarowania innej zmiennej, funkcji albo typu. 
 
Uwaga: Podano zalecenie, a nie zakaz, ponieważ  w tym samym zakresie mogą  wystą pić , nie kolidują ce za sobą , 
deklaracje funkcji i typu. 
 

void id(int id) 
{     
    struct id { 
    }; 
    extern void id(id id); 
    int id = 10;    // b

łąd 

 
Z  każ dą   deklaracja  jest  zwią zany  jej  zakres  i  zasię g.  Jeśli  w  pewnym  module  zdefiniowano  identyfikator  o 
zasięgu globalnym, a w innym zadeklarowano go ze specyfikatorem  extern, to oba dotyczą  tej samej zmiennej, 
funkcji albo typu. 
 
plik Main.cpp 
 

#include <iostream.h> 
 
int fix = 10;                // definicja 
 
int main(void) 

    extern void fun(void);   // deklaracja 
    fun(); 
 
    return 0; 

 
 
plik One.cpp 
 

#include <iostream.h> 
 
void fun()                   // definicja 

    extern int fix;          // deklaracja 
    cout << fix << endl;     // 10 

 
Gdyby  pominięto  wszystkie  specyfikatory  extern,  to  program  stał

by  się statycznie  poprawny,  ale  dynamicznie 

ędny. Bł

ą d polegałby na użyciu wartoś ci zmiennej, któ rej nie zainicjowano. 

 
 

Deklaracje lokalne 

 
Zakresem  deklaracji  identyfikatora  zadeklarowanego  w  bloku  jest  obszar  programu  od  punktu  zadeklarowania 
do  końca  bloku.  Zasięgiem  deklaracji  jest  ta  część   zakresu,  któ ra  nie  jest  zakresem  innej  deklaracji  takiego 
samego identyfikatora. 

background image

 

51

 
 

#include <iostream.h> 
 
int main(void)  

    int num = 10; 
    cout << num << endl;       // 10 
    { 
        cout << num << endl;   // 10 
        int num = 20; 
        cout << num << endl;   // 20 
    } 
    cout << num << endl;       // 10 
 
    return 0; 

 
Zakresem  deklaracji  pierwszej  zmiennej  num  jest  obszar  zaczynają cy  się  od  = 10  i  kończą cy  na  klamrze 
zamykają cej funkcję main
 
Zakresem  deklaracji  drugiej  zmiennej  num  jest  obszar  zaczynają cy  się  od  = 20  i  kończą cy  na  klamrze 
zamykają cej blok wewnętrzny. 
 
Zasięgiem  deklaracji  pierwszej  zmiennej  num  jest  zakres  deklaracji  pierwszej  zmiennej  num,  pomniejszony  o 
zakres deklaracji drugiej zmiennej num
 
 

Deklaracje globalne 

 
Zakresem  deklaracji  identyfikatora  zadeklarowanego  w  module  (tj.  poza  blokiem),  jest  obszar  programu  od 
punktu  zadeklarowania  do  końca  modułu.  Zasięgiem  deklaracji  jest  ta  część   zakresu,  któ ra  nie  jest  zakresem 
innej deklaracji takiego samego identyfikatora. 
 
Uwaga: Modułem jest zawartość  pliku *.cpp projektu, po zastosowaniu uż ytych w nim dyrektyw (#include#if
#endif, itp.). 
 

#include <iostream.h> 
 
int num = 10; 
 
int main(void)  

    cout << num << endl;       // 10 
    { 
        cout << num << endl;   // 10 
        int num = 20; 
        cout << num << endl;   // 20 
    } 
    cout << num << endl;       // 10 
 
    return 0; 

 
int num2 = num; 

 
Zasięg deklaracji pierwszego identyfikatora num obejmuje m.in. deklarację występują cą  po funkcji main. 
 
 

Deklaracje i definicje 

 

background image

 

52

Jeśli  deklaracja  globalna  zawiera  specyfikator  static,  to  jest  widoczna  tylko  w  jej  module.  Jeśli  deklaracja 
globalna  jest  definicją ,  ale  nie  zawiera  specyfikatora  static,  to  jest  widoczna  w  tych  obszarach  pozostałych 
modułó w  programu,  w  któ rych  jest  widoczna  zgodna  z  nią   deklaracja ze specyfikatorem  extern  bez inicjatora, 
nie dotyczą ca deklaracji globalnej ze specyfikatorem static
 
Uwaga:  Globalne  zmienne  ustalone  są   domyślnie  wyposaż one  w  specyfikator  static.  Specyfikator  extern 
występują cy w deklaracji funkcji moż na pominą ć . 
 
plik Main.cpp 
 

#include <iostream.h> 
 
int main(void)  

    int fun(void);           // pomini

ęto extern 

    cout << fun() << endl;   // 10 
 
    extern int num;  
    cout << num << endl;     // 20 
 
    return 0; 

 
 
plik One.cpp 
 

static int num = 10; 
 
int fun(void) 

    extern int num;          // zb

ędne 

    return num; 

 
 
plik Two.cpp 
 

int num = 20; 

 
 

Deklaracje typów 

 
Globalna deklaracja typu, na przykład  
 

 

struct Child; 

 
nie wystarczy do tego, aby moż na było nawią zać  do definicji tego typu podanej w innym module. 
 
W  odró ż nieniu  od  definicji  zmiennej  i  funkcji,  któ ra  w  zbiorze  modułó w  programu  moż e  wystą pić   tylko  jeden 
raz, definicja struktury musi być  powtó rzona w każdym z odwołują cych się do niej modułó w. 
 
plik Main.cpp 
 

#include <iostream.h> 
 
struct Child { 
    char name[20]; 
    int age; 
}; 
 
int main(void)  

    Child getIsa(void); 
    Child isa = getIsa(); 

background image

 

53

    cout << isa.name << " is " << 
            isa.age << endl; 
 
    return 0; 

 
 
plik Isa.cpp 
 

struct Child { 
    char name[20]; 
    int age; 
}; 
 
Child isa = { "Isabel", 15 }; 
 
Child getIsa(void) 

    return isa; 

 
albo lepiej i bezpieczniej 
 
plik child.h 
 

struct Child { 
    char name[20]; 
    int age; 
}; 

 
 
plik Main.cpp 
 

#include <iostream.h> 
#include "child.h" 
 
int main(void)  

    Child getIsa(void); 
    Child isa = getIsa(); 
    cout << isa.name << " is " << 
            isa.age << endl; 
 
    return 0; 

 
 
plik Isa.cpp 
 

#include "child.h" 
 
Child isa = { "Isabel", 15 }; 
 
Child getIsa(void) 

    return isa; 

 

background image

 

54

Studia programowe 

 
Przedstawiono dwa rozwią zania następują cego problemu 
 
 

Napisać program, któ ry wprowadza z  pliku sekwencję danych arytmetycznych, a następnie wyprowadza 
ich  ś rednie  odchylenie  standardowe:  pierwiastek  z  sumy  kwadrató w  ró żnic  dana-ś rednia,  podzielony 
liczbę danych.
 

 
W  szczegó lności,  jeśli  w  pliku  Data.txt  umieści  się  liczby  6  9  12,  a  jako  argument  programu  poda  Data.txt 
(polecenie Project / Settings // Debug), to nastą pi wyprowadzenie liczby 1.41421
 
 

Struktura tablicowa 

 

#include <iostream.h> 
#include <fstream.h> 
#include <math.h> 
 
int readData(char *fileName, double *&pData); 
double getAverage(double *pData, int count); 
double getResult(double *pData, int count, double average); 
void freeMemory(double *pData); 
 
int main(int noOfArgs, char *pArg[]) 

    if(noOfArgs != 2) { 
        cout << "Usage is: " << pArg[0] <<  
                " fileName" << endl; 
        return -1; 
    } 
    double *pData; 
    char *fileName = pArg[1]; 
    int count = readData(fileName, pData); 
    if(count) { 
        double average = getAverage(pData, count); 
        double result = getResult(pData, count, average); 
        cout << "Result = " << result << endl; 
    } else 
        cout << "Error!" << endl; 
 
    return 0; 

 
int readData(char *fileName, double *&pData) 

    const int start = 200; 
    ifstream inp; 
    inp.open(fileName, ios::in | ios::nocreate); 
    int count = 0; 
    if(inp.is_open()) { 
        pData = new double [start]; 
        int len = start; 
        double tmp; 
        while(tmp = 0, inp >> tmp, tmp) { 
            if(count == len) { 
                double *ptr = new double [len *= 2]; 
                for(int j = 0; j < len /2 ; j++) 
                    ptr[j] = pData[j]; 
                delete [] pData; 
                pData = ptr; 
            } 
            pData[count++] = tmp; 
        } 
    } 
    return count; 

 

background image

 

55

double getAverage(double *pData, int count) 

    double sum = 0; 
    for(int i = 0; i < count ; i++) 
        sum += pData[i]; 
    return sum / count; 

 
double getResult(double *pData, int count, double average) 

    double sumSqr = 0; 
    for(int i = 0; i < count ; i++) { 
        double dif = pData[i] - average; 
        sumSqr += dif * dif; 
    } 
    return sqrt(sumSqr) / count; 

 
void freeMemory(double *pData) 

    delete [] pData; 

 
 

Struktura listowa 

 

#include <iostream.h> 
#include <fstream.h> 
#include <math.h> 
 
struct Item { 
    Item *pNext; 
    double value; 
}; 
 
struct List { 
    Item *pFirst; 
    int count; 
}; 
 
List list = { 0 }; 
 
int readData(char *fileName, List &list); 
double getAverage(List &list); 
double getResult(List &list, double average); 
void freeMemory(List &list); 
 
int main(int noOfArgs, char *pArg[]) 

    if(noOfArgs != 2) { 
        cout << "Usage is: " << pArg[0] <<  
                " fileName" << endl; 
        return -1; 
    } 
    char *fileName = pArg[1]; 
    int count = readData(fileName, list); 
    if(count) { 
        double average = getAverage(list); 
        double result = getResult(list, average); 
        cout << "Result = " << result << endl; 
    } else 
        cout << "Error!" << endl; 
    freeMemory(list); 
 
    return 0; 

 
int readData(char *fileName, List &list) 

    ifstream inp; 
    inp.open(fileName, ios::in | ios::nocreate); 

background image

 

56

    int count = 0; 
    if(inp.is_open()) { 
        double tmp; 
        while(tmp = 0, inp >> tmp, tmp) { 
            Item *pItem = new Item; 
            pItem->pNext = list.pFirst; 
            pItem->value = tmp; 
            list.pFirst = pItem; 
            count++; 
        } 
    } 
    return list.count = count; 

 
double getAverage(List &list) 

    double sum = 0; 
    Item *pItem = list.pFirst; 
    while(pItem) { 
        sum += pItem->value; 
        pItem = pItem->pNext; 
    } 
    return sum / list.count; 

 
double getResult(List &list, double average) 

    double sumSqr = 0; 
    Item *pItem = list.pFirst; 
    while(pItem) { 
        double dif = pItem->value - average; 
        sumSqr += dif * dif; 
        pItem = pItem->pNext; 
    } 
    return sqrt(sumSqr) / list.count; 

 
void freeMemory(List &list) 

    Item *pItem = list.pFirst, *pTmp; 
    while(pItem) { 
        pTmp = pItem->pNext; 
        delete pItem; 
        pItem = pTmp; 
    } 

 

 

background image

 

57

Dodatek A  

 

Priorytety operatorów 

 
 
 
 
Operatory wyszczegó lniono w kolejnoœ ci malej¹cego priorytetu. 
 
 

Wi¹zanie  Operator 

 
 

prawe 

:: 

 

lewe 

Type:: 

 

lewe 

[]  .  ->  ()  Type() 

 

lewe 

++  --  (nastêpnikowe) 

 

prawe 

++  --  (poprzednikowe) 

 

prawe 

sizeof  +  -  ~  !  &  *  new  delete  (Type)  throw 

 

lewe 

.*  ->* 

 

lewe 

*  /  % 

 

lewe 

+  - 

 

lewe 

<<  >> 

 

lewe 

<  <=  >  >= 

 

lewe 

==  != 

 

lewe 

& 

 

lewe 

^ 

 

lewe 

| 

 

lewe 

&& 

 

lewe 

|| 

 

prawe 

?: 

 

prawe 

=  *=  /=  %=  +=  -=  <<=  >>=  &=  ^=  |= 

 

lewe 

 
l-nazwą   zmiennej  (por.  Dodatek  B)  jest  tylko:  operacja  przypisania  (np.  a+=b),  przedrostkowego  zwię kszenia 
(np.  ++a), przedrostkowego  zmniejszenia (np. --a), indeksowania (np. ptr[i]), wyłuskania  (np. *ptr), wyboru 
(np.  str.f  i  ptr->f),  warunku  któ rego  dwa  ostatnie  argumenty  są   l-nazwami  (np.  a>0?a:b),  konwersji  do  typu 
odnośnikowego (np. (int &)a) oraz globalności (np. ::) i zakresu (np. Child::name). 
 

background image

 

58

 

Dodatek B 

 

Opracowywanie wyrażeń  

 
 
 
 
Wyraż enia  są   zapisami  operacji.  O  kolejności  wykonywania  operacji  decyduje  sposó b  uż ycia  nawiasó w  oraz 
uwzględnienie priorytetów wią zań  operatoró w (por. Dodatek A). 
 
Jeśli  kilka  operatoró w  zapisano  spójnie  (tj.  bez  odstępó w),  wó wczas  za  pierwszy  uznaje  się  najdłuższy.  A 
zatem: ponieważ  w C++ istnieją  operatory + i ++, ale nie istnieje operator +++, więc wyraż enie 
 

 

a +++ b 

 
jest traktowane jak 
 

 

(a++) + b         // a nie jak: a + (++b) 

 
 

Priorytety 

 
Ponieważ  w C++ priorytet mnoż enia jest wyższy niż  priorytet dodawania, więc wyraż enie 
 

 

a + b * c 

 
jest traktowane jak 
 

 

a + (b * c)       // a nie jak: (a + b) * c 

 
 
Podobnie, ponieważ  w C++ priorytet następnikowej operacji zwiększenia (++) jest wyż szy niż  priorytet operacji 
wyłuskania (*), więc wyraż enie 
 

 

*ptr++ 

 
jest traktowane jak 
 

 

*(ptr++)             // a nie jak: (*ptr)++ 

 
 

Wią zania 

 
Ponieważ   w  C++  priorytet  odejmowania  (-)  jest  ró wny  priorytetowi  dodawania  (+),  więc  jeśli  pewnego 
podwyraż enia dotyczą  oba takie operatory, to odwołanie się do priorytetó w nie wystarcza i trzeba odwołać  się do 
wią zań.  
 
Ponieważ  w C++ wią zanie operacji odejmowania i dodawania jest lewe, więc wyraż enia 
 

 

a - b + c 

 

cout << a << b 

 
są  traktowane jak 
 

 

(a - b) + c          // a nie jak a - (b + c) 

background image

 

59

 

(cout << a) << b     // a nie jak: cout << (a << b) 

 
(środkowe podwyraż enia dowią zano do lewej). 
 
 
Dla poró wnania, ponieważ  wią zanie operacji przypisania jest prawe, więc wyraż enie 
 

 

a = b = c 

 
jest traktowane jak 
 

 

a = ( b = c)         // a nie jak: (a = b) = c 

 
 

Kolejność 

 
Kolejność  opracowywania argumentó w operacji jest nieokreś lona. Dotyczy to zaró wno argumentó w wywołania 
funkcji, jak i argumentó w operacji dwuargumentowych, takich jak przypisanie. 
 
Dlatego  zaleca  się,  aby  w  wyraż eniu,  w  któ rym  następuje  zmiana  wartości  zmiennej,  nie  odwoływano  się 
(dodatkowo!) do tej zmiennej. 
 

fun(cout << 100, cout << 200); 
int tab[4] = { 10, 20, 30 }, 
    pos = 1; 
tab[pos] = ++pos; 

 
Nie  wiadomo,  czy  przed  wykonaniem  ciał

a  funkcji  fun  zostanie  wyprowadzona  liczba  100  czy  200.  W  Visual 

C++ zostanie wyprowadzona liczba 200
 
Nie wiadomo, czy przypisanie dotyczy elementu tab[1] czy elementu tab[2]. W Visual C++ dotyczy ono tab[2]
 
 

Promocja 

 
Niektó re operacje są  wykonywane dopiero po promocji argumentu. Dotyczy to w szczegó lności zmiennych typu 
char (poddawanych promocji do typu int). 
 

    char chr = ’a’; 
    char &ref1 = chr; 
    char &ref2 = +chr;    // b

łąd 

    char &ref3 = ’a’;     // b

łąd 

 
 

Typ wspólny 

 
Jeśli  argumenty  operacji  są   ró ż nych  typó w,  to  wykonuje  się  ją   w  ich  typie  wspólnym.  W  szczegó lności  typem 
wspó lnym dla char int jest int, a typem wspó lnym dla double int jest double
 
Uwaga:  Jeśli  wyraż enie  jest  pewnego  typu,  to  nie  oznacza  to,  ż e  wszystkie  jego  operacje  wykonuje  się  w  tym 
typie.  
 

#include <iostream.h> 
#include <limits.h> 
 
int main(void) 
{     
    int max = INT_MAX;                   
    cout << max * max << endl;          // 1 (sic!) 
    cout << 0.0 +  max * max << endl;   // 1 (sic!) 
    cout << double(max) * max << endl;  // ok. 4.6e18 

background image

 

60

 
    return 0; 

 
Mimo iż typem wyrażenia zawierają cego liczbę 0.0 jest double, iloczyn max * max jest obliczany w typie int
 
 

Punkty charakterystyczne 

 
Punktem  charakterystycznym  jest  miejsce  w  programie,  w  któ rym  realizuje  się  wszystkie  "zaległe"  skutki 
uboczne, takie jak operacje wej ścia-wyjścia i przypisania. 
 
Punkt  charakterystyczny  występuje  m.in.  po  każ dym  kompletnym  wyraż eniu,  przed  każ dym  średnikiem,  przed 
pierwszą  instrukcją  funkcji oraz przed operatorami koniunkcji i dysjunkcji. 
 
Programy zależ ne od położ enia punktu charakterystycznego należ y konstruować  ze szczegó lną  ostroż nością . 
 

int fix = 10; 
++fix = fix; 
cout << fix; 

 
Ponieważ  operacja  zwiększenia  (++)  może  być  zrealizowana  dopiero  w  punkcie  charakterystycznym,  więc  nie 
wiadomo, czy zostanie wyprowadzona liczba 10 czy 11. W Visual C++ zostanie wyprowadzona liczba 11
 

Nazwy 

 
Każ de wyraż enie i podwyraż enie (w szczegó lności zapis operacji), moż na rozpatrywać  jako nazwę pomocniczej 
zmiennej tymczasowej. Podczas opracowywania wyraż enia, każ dą  z operacji zastępuje się nazwą  jej rezultatu. 
 
Uwaga:  Pomocniczą   zmienną   tymczasową   niszczy  się  bezpośrednio  po  opracowaniu  kompletnego  wyraż enia, 
któ rego opracowania wymagało utworzenia tej zmiennej. 
 
W szczegó lności, jeśli przyją ć , ż e zmiennymi tymczasowymi są  t1t2 i t3 to instrukcja 
 

cout << 1 + 2 * 3; 

 
jest wykonywana tak, jak  
 

int t1, t2, t3; 
t1
 = 2 * 3, t2 = 1 + t1, cout << t2 

 
a zmienne tymczasowe zostaną  zniszczone w chwili, gdy sterowanie "przepłynie przez średnik". 
 

l-nazwy 

 
Przyjmuje  się  z  definicji,  ż e  l-nazwą   jest  tylko:  identyfikator  zmiennej  nie-ustalonej,  rezultat  funkcji  o  typie 
odnośnikowym  oraz  rezultat  operacji  wymienionych  w  Dodatku  A.  Nie  jest  l-nazwą   literał,  ani  wskaźnik 
powstały z niejawnego przekształcenia nazwy tablicy. 
 
Posługują c się taką  definicją  moż na podać  następują ce wymagania 
 
1)  Odnośnik  do  zmiennej  nie-ustalonej  moż e  być   zainicjowany  tylko  takim  wyraż eniem,  któ re  jest  l-nazwą  
zmiennej. 
np.  

 

const int fix1 = 10; 

 

int &fix2 = 20;        // b

łąd 

 
2)  Argumentem operacji zwiększenia (++), zmniejszenia (--), wskazywania (&) i wyboru (. i ->) moż e być  tylko 
takie wyraż enie, któ re jest l-nazwą  zmiennej. 

background image

 

61

np. 

 

int fix = 10; 

 

++(int)fix;            // b

łąd 

 

fix++++;               // b

łąd 

 

int *ptr = &20;        // b

łąd 

 
3)  Lewym argumentem przypisania (=+= itp.) moż e być  tylko takie wyraż enie, któ re jest l-nazwą  zmiennej. 
np. 

 

int fix = 10; 

 

fix++ = 20;            // b

łąd 

 

int tab[] = { 10 }; 

 

tab = 20;              // b

łąd 

 
Uwaga:  Niepoprawność   operacji  fix++++  wynika  stą d,  ż e  fix++  nie  jest  l-nazwą ,  a  więc  nie  moż e  być  
argumentem ponownej operacji zwiększenia. 

background image

 

62

Dodatek C 

 

Konwersje standardowe 

 
 
 
 
Konwersją   standardową ,  jest  taka  predefiniowana  konwersja,  któ ra  moż e  być   wstawiona  do  programu 
niejawnie
 
Konwersjami standardowymi są  m.in.:  
 
0)  Przekształcenie promocyjne (np. zmiennej typu char w zmienna typu int). 
1)  Przekształcenie zmiennej arytmetycznej albo wskaźnika w orzecznik (np. zmiennej typu int w zmienną  typu 

bool). 

2)  Przekształcenie  zmiennej  arytmetycznej  w  zmienną   arytmetyczną   innego  typu  (np.  zmiennej  typu double w 

zmienną  typu int). 

3)  Przekształcenie nazwy tablicy na wskaźnik do jej zerowego elementu. 
4)  Przekształcenie nazwy zmiennej na odnośnik do tej zmiennej. 
5)  Przekształcenie wskaźnika do obiektu na wskaźnik do jego podobiektu. 
6)  Przekształcenie odnośnika do obiektu na odnośnik do jego podobiektu. 
7)  Przekształcenie wskaźnika do zmiennej na wskaźnik lokalizują cy tę zmienną . 
 
Nie są  nimi m.in. 
 
1)  Przekształcenie wskaźnika do elementu tablicy na wskaźnik do tej tablicy. 
2)  Przekształcenie wskaźnika do tablicy na wskaźnik do jej elementu. 
3)  Przekształcenie wskaźnika do podobiektu na wskaźnik do jego obiektu. 
4)  Przekształcenie odnośnika do podobiektu na odnośnik do jego obiektu. 
5)  Przekształcenie wskaźnika lokalizują cego zmienną  na wskaźnik do tej zmiennej. 
 
Uwaga: Poza konwersjami standardowymi, niejawne moż e być  zastosowany jedynie konstruktor konwerter

background image

 

63

Dodatek D 

 

Operatory bitowe 

 
 
 
 
Operatorami bitowymi są : ~ (zanegowanie bitó w), & (iloczyn bitó w), | (suma bitó w), ^ (suma modulo 2 bitó w), 
<< (przesunięcie bitó w w lewo), >> (przesunięcie bitó w w prawo). 
 
Podczas  wykonywania operacji na bitach przydatne okazują   się  literały szesnastkowe. Literał szesnastkowy ma 
postać  0xh, w któ rej h jest spó jnym cią giem cyfr szesnastkowych (0-9 i a-f). 
 

cout << 0x12;      // 18 
cout << 0xffff;    // 65535 

 
 

Operator ~ 

 
Operacja zanegowania bitó w ma postać  
 

 

~exp 

 
w któ rej exp jest wyraż eniem całkowitym. 
 
Rezultatem  operacji  zanegowania  bitó w  jest  zmienna  tymczasowa  takiego  samego  typu  jak  zmienna  exp,  po 
poddaniu jej  promocjom, a następnie zanegowaniu każ dego jej bitu. 
 
Uwaga: Negacją  bitu 1 jest bit 0, a negacją  bitu 0 jest bit 1
 

int red = 1, green = 2, blue = 4; 
int hue = red | green;        // ... 011 (kolor 

żółty) 

hue = ~hue;                   // ... 100 (kolor niebieski) 

 
Trzy  najmniej  znaczą ce  bity  zmiennej  hue  reprezentują   jeden  z  8  koloró w.  Wykonanie  operacji  zanegowania 
bitó w powoduje zmianę koloru na dopełniają cy.
 
 
 

Operator & 

 
Operacja iloczynu bitó w ma postać  
 

 

expL & expR 

 
w któ rej expL i expR są  wyraż eniami całkowitymi. 
 
W  celu  utworzenia  wyniku  operacji,  zmienne  expL  i  expR  poddaje  się  konwersjom  do  typu  wspólnego,  a 
następnie  każ dy  bit  wyniku  tworzy  się  z  odpowiadają cych  sobie  bitó w  argumentó w  wyznaczają c  ich  iloczyn 
logiczny
.  
 
Uwaga: Iloczyn logiczny pary bitó w ma wartość  1 tylko wó wczas gdy oba bity są  jedynkowe
 

int fix = 6;                  //    00 ... 110 
const int mask = ’\x3’;       //    00 ... 011 
fix &= ~mask;              
cout << Fix;                  // 4 (00 ... 100) 

 

background image

 

64

Wykonanie  operacji  na  zmiennej  fix  powoduje  wyzerowanie  tych  wszystkich  jej  bitó w,  któ re  w  mask  są  
jedynkowe.
 
 
 

Operator ^ 

 
Operacja sumy modulo 2 bitó w ma postać  
 

 

expL ^ expR 

 
w któ rej expL i expR są  wyraż eniami całkowitymi. 
 
W  celu  utworzenia  wyniku  operacji,  zmienne  expL  i  expR  poddaje  się  konwersjom  do  typu  wspólnego,  a 
następnie  każ dy  bit  wyniku  tworzy  się  z  odpowiadają cych  sobie  bitó w  argumentó w  wyznaczają c  ich  sumę  
logiczną  modulo 2

 
Uwaga: Suma logiczna modulo 2 pary bitó w ma wartość  1 tylko wó wczas gdy bity są  różne
 

int fix = 6;                  //    00 ... 110 
const int mask = ’\x3’;       //    00 ... 011 
fix ^= mask;              
cout << fix;                  // 5 (00 ... 101) 

 
Wykonanie  operacji  na  zmiennej  fix  powoduje  zanegowanie  tych  wszystkich  jej  bitó w,  któ re  w  mask  są  
jedynkowe.
 
 
 

Operator | 

 
Operacja sumy bitó w ma postać  
 

 

expL expR 

 
w któ rej expL i expR są  wyraż eniami całkowitymi. 
 
W  celu  utworzenia  wyniku  operacji,  zmienne  expL  i  expR  poddaje  się  konwersjom  do  typu  wspólnego,  a 
następnie  każ dy  bit  wyniku  tworzy  się  z  odpowiadają cych  sobie  bitó w  argumentó w  wyznaczają c  ich  sumę  
logiczną 
.  
 
Uwaga: Suma logiczna pary bitó w ma wartość  0 tylko wó wczas gdy oba bity są  zerowe
 

int fix = 5;                  //    00 ... 101 
const int mask = ’\x3’;       //    00 ... 011 
fix |= mask;              
cout << Fix;                  // 7 (00 ... 111) 

 
Wykonanie operacji na zmiennej fix powoduje ustawienie tych wszystkich jej bitó w, któ re w mask są  jedynkowe. 
 
 

Operator << 

 
Operacja przesunięcia bitó w w lewo ma postać  
 

 

expL << n 

 
w któ rej expL i n są  wyraż eniami całkowitymi. 
 
W celu utworzenia wyniku operacji, zmienną  expL poddaje się promocji, a następnie każ dy bit wyniku tworzy 
się z bitó w tej nowej zmiennej po przesunięciu ich o n pozycji w lewo
 

background image

 

65

Uwaga: Podczas przesuwania  w lewo bity najbardziej znaczą ce są  odrzucane, a na pozycje najmniej znaczą ce 
wchodzą  bity 0
 

int fix = 7;                  // 00 ... 0111 
fix <<= 2; 
cout << fix;                  // 28 (00 ... 011100) 

 
Bity zmiennej fix przesunięto o 2 pozycje w lewo. 
 
 

Operator >> 

 
Operacja przesunięcia bitó w w prawo ma postać  
 

 

expL >> n 

 
w któ rej expL i n są  wyraż eniami całkowitymi. 
 
W  celu  utworzenia  wyniku operacji, zmienną  expL poddaje się  promocji,  a  następnie każ dy  bit  wyniku tworzy 
się z bitó w tej nowej zmiennej po przesunięciu ich o n pozycji w prawo
 
Uwaga: Podczas przesuwania w prawo bity najmniej znaczą ce są  odrzucane
 

int fix = 15;                 // 00 ... 01111 
fix >>= 2; 
cout << fix;                  // 3 (00 ... 011) 

 
Bity zmiennej fix przesunięto o 2 pozycje w prawo. 
 

background image

 

66

Dodatek E 

 
 
 
 
 
 
 

Operacje wejścia-wyjścia 

 
 
 
 
Większość  operacji wejścia-wyjścia moż na wykonać  za pomocą  operatorów. Do specjalnych celó w przydają  się 
niekiedy funkcje wejścia-wyjścia. 
 
 

Funkcje get i put 

 

inp.get(chr) 

Wprowadza  ze  strumienia  inp  najbliż szy  znak  (w  tym  znak  odstępu)  i  jego  kod  przypisuje  zmiennej  chr  typu 
char. Dostarcza odnośnik do inp
 

 

out.put(chr) 

Wyprowadza do strumienia out znak o kodzie chr. Dostarcza odnośnik do out
 

#include <iostream.h> 
#include <fstream.h> 
#include <string.h> 
 
int main(void) 

    ifstream inp; 
    inp.open("Data.txt", ios::in); 
    if(!inp.is_open()) 
        return -1; 
 
    char chr; 
    while(inp.get(chr)) 
        cout.put(chr); 
 
    return 0; 

 
Program kopiuje na konsolę zawartoś ć pliku Data.txt. Kopiowanie odbywa się znak-po-znaku. 
 
 

Funkcje read i write 

 

inp.read(ptr, len) 

Wprowadza  ze  strumienia  inp  cią g  len  najbliż szych  znakó w  i  ich  kody  umieszcza  w  tablicy  znakowej  o 
elemencie wskazywanym przez ptr. Dostarcza odnośnik do inp
 

out.write(ptr, len) 

Wyprowadza  do  strumienia  out  cią g  len  znakó w  z  tablicy  znakowej,  począ wszy  od  elementu  wskazywanego 
przez ptr. Dostarcza odnośnik do out
 
inp.gcount() 

background image

 

67

Dostarcza liczbę znakó w wprowadzonych za pomocą  ostatnio wywołanej funkcji read albo getline
 

#include <iostream.h> 
#include <fstream.h> 
 
const int Size = 10; 
 
int main(void) 

    ifstream inp; 
    inp.open("Data.txt", ios::in); 
    if(!inp.is_open()) 
        return -1; 
 
    char buf[Size]; 
    while(true) { 
        inp.read(buf, Size); 
        int len = inp.gcount(); 
        if(len > 0) 
            cout.write(buf, len); 
        if(len < Size) 
            break; 
    } 
 
    return 0; 

 
Program kopiuje na konsolę zawartoś ć pliku Data.txt. Kopiowanie odbywa się porcjami po Size znakó w. 
 
 

Funkcja getline 

 

inp.getline(ptr, len) 

Wprowadza ze strumienia inp jeden wiersz, ale nie więcej niż  len-1 najbliż szych znakó w, a ich kody, bez kodu 
’\n’, ale z dodatkowym kodem 0, umieszcza w tablicy znakowej o elemencie wskazywanym przez ptr. Dostarcza 
odnośnik do inp
 

#include <iostream.h> 
#include <fstream.h> 
 
const int Size = 100; 
 
int main(void) 

    ifstream inp; 
    inp.open("Data.txt", ios::in); 
    if(!inp.is_open()) 
        return -1; 
 
    char buf[Size]; 
    while(inp) { 
        inp.getline(buf, Size); 
        int len = inp.gcount(); 
        if(len > 0) 
            cout << buf << endl; 
    } 
 
    return 0; 

 
Program kopiuje na konsolę zawartoś ć pliku Data.txt. Kopiowanie odbywa się wierszami. 
 
 

Funkcje peek i putback 

 

inp.peek() 

Dostarcza kod najbliż szego znaku strumienia inp, ale znaku ze strumienia nie wprowadza (sic!). 

background image

 

68

 

inp.putback(chr) 

Cofa do strumienia inp znak o kodzie chr. Dostarcza odnośnik do inp
 

#include <iostream.h> 
#include <fstream.h> 
#include <ctype.h> 
 
const int Size = 100; 
 
int main(void) 

    ifstream inp; 
    inp.open("Data.txt", ios::in); 
    if(!inp.is_open()) 
        return -1; 
 
    while(inp) { 
        char chr; 
        inp >> chr; 
        inp.putback(chr); 
        if(chr == ’-’ || chr == ’+’ || isdigit(chr)) { 
            double num; 
            inp >> num; 
            cout << num << endl; 
        } else { 
            char buf[Size]; 
            inp >> buf; 
            cout << buf << endl; 
        } 
    } 
 
    return 0; 

 
Program wprowadza z pliku Data.txt zawarte w nim liczby i ł

ańcuchy, a następnie wyprowadza je na konsolę, 

każdy w osobnym wierszu. 
 
 

Funkcje tellg i tellp 

 
Funkcje  tellg  i  tellp  służ ą   do  określania  pozycji  pliku.  Pozycja  jest  daną   typu  streampos.  W  Visual  C++  typ 
streampos jest identyczny z typem int
 

inp.tellg() 

Dostarcza bież ą cą  pozycję pliku otwartego w trybie ios::in
 

inp.tellp() 

Dostarcza bież ą cą  pozycję pliku otwartego w trybie ios::out
 

#include <iostream.h> 
#include <fstream.h> 
 
int main(void) 

    ifstream inp; 
    inp.open("C:\\config.sys", ios::in); 
    if(!inp.is_open()) 
        return -1; 
 
    char chr; 
    while(inp >> chr) 
        ; 
    streampos pos = inp.tellg(); 
    cout << "Size = " << pos << endl; 
 
    return 0; 

background image

 

69

 
Program wyznacza rozmiar pliku config.sys.
 
 
 

Funkcje seekg i seekp 

 
Funkcje  seekg  i  seekp  służ ą   do  ustawiania  pozycji  pliku.  Nowa  pozycja  pliku  moż e  być   podana  względem 
począ tku pliku (ios::beg), względem pozycji bież ą cej (ios::cur), albo względem pozycji końcowej (ios::end). 
 

inp.seekg(pos)            // inp.seekg(pos, ios::beg) 
inp.seekg(pos, from) 

Ustawia plik otwarty w trybie ios::in w pozycji pos, liczonej względem from (ios::begios::curios::end). 
 

inp.seekp(pos)            // inp.seekp(pos, ios::beg) 
inp.seekp(pos, from) 

Ustawia plik otwarty w trybie ios::out w pozycji pos, liczonej względem from (ios::begios::curios::end). 
 

inp.seek(0); 

 
Instrukcja ustawia strumień w pozycji począ tkowej. 
 
 

Funkcja clear 

 
Funkcja clear służ y do ustawienia stanu strumienia. 
 

str.clear()               
str.clear(ios::badbit) 

Wywołanie bezargumentowe ustawia strumień str w stan dobry. Wywołanie z argumentem ios::badbit ustawia 
go w stan zły
 

#include <iostream.h> 
#include <fstream.h> 
 
int main(void) 

    ifstream inp; 
    inp.open("C:\\autoexec.bat", ios::in); 
    if(!inp.is_open()) 
        return -1; 
 
    char chr; 
    for(int i = 0; i < 3 ; i++) { 
        while(inp.get(chr)) 
            cout << chr; 
        inp.clear(); 
        inp.seekg(0); 
    } 
 
    return 0; 

 
Program ma na celu 3-krotne wyprowadzenie na konsolę zawartoś ci pliku autoexec.bat
 
Ponieważ po zakończeniu instrukcji while strumień inp znajduje się w stanie nie-dobrym, więc należy ustawić go 
w  stan  dobry.  W  przeciwnym  razie  wszystkie  operacje  wejś cia-wyjś cia  dotyczą ce  tego  strumienia  był

yby 

pomijane, a zawartoś ć pliku został

aby wyprowadzona tylko 1 raz. 

 
 

Operacje w pamię ci 

 

background image

 

70

Operacje  wejścia-wyjścia  mogą   dotyczyć   nie  tylko  plikó w,  ale  ró wnież   pamięci  operacyjnej.  Do  wykonywania 
operacji  w  pamięci  służ ą   obiekty  klas  istrstream  i  ostrstream,  zadeklarowanych  w  pliku  nagłó wkowym 
strstream.h
 
Argumentem  konstruktora  klasy  istrstream  jest  wskaźnik łańcucha.  Argumentami  konstruktora  klasy  ostrstream 
jest  wskaźnik  elementu  tablicy  znakowej  i  maksymalna  liczba  jej  elementó w, któ re mogą  być   uż yte w operacji 
wyjścia. 
 
Uwaga: Operacja wyjścia nie zapisuje znaku końca łańcucha. Należ y to wykonać  jawnie, na przykład za pomocą  
symbolu ends
 

#include <iostream.h> 
#include <strstream.h> 
 
int main(void) 

    char data[] = "10 20 30"; 
 
    istrstream(data) >> a >> b >> c; 
    char buf[100]; 
    ostrstream(buf, sizeof(buf)) << "Sum = " <<  
                                     a + b + c << ends; 
 
    cout << buf << endl; 
 
    return 0; 

 
 

Przetwarzanie wyrywkowe 

 
Wyrywkowo przetwarza się zazwyczaj pliki binarne. Plik binarny otwiera się w trybie  
 

 

ios::in | ios::out | ios::binary 

 
Operacje na pliku wykonuje się za pomocą  funkcji read i write
 

#include <iostream.h> 
#include <fstream.h> 
 
const char *const SrcName = "Data.txt"; 
const int Size = sizeof(int); 
const char *const TrgName = "Random"; 
 
int main(void) 
{     
    ifstream inp; 
    inp.open(SrcName, ios::in | ios::nocreate); 
    if(!inp.is_open()) { 
        cout << "Source failure" << endl; 
        return -1; 
    } 
 
    ofstream out; 
    out.open(TrgName, ios::out | ios::binary); 
    if(!out.is_open()) { 
        cout << "Target failure" << endl; 
        return -2; 
    } 
 
        // wprowadzanie 
    cout << endl << "reading ... " << endl; 
    int count = 0, tmp; 
    while(inp >> tmp) { 
        count++; 
        cout << tmp << endl; 
        out.write((char *)&tmp, Size); 

background image

 

71

    } 
    out.close(); 
    inp.close(); 
    if(count == 0) { 
        cout << "No data" << endl; 
        return -4; 
    } 
 
        // sprawdzanie 
    cout << endl << "checking ... " << endl; 
    inp.open(TrgName, ios::in | ios::binary | ios::nocreate); 
    if(!inp.is_open()) { 
        cout << "Check failure" << endl; 
        return -5; 
    } 
    while(inp.read((char *)&tmp, Size)) 
        cout << tmp << endl; 
    inp.close(); 
 
        // sortowanie 
    cout << endl << "sorting ... "; 
    fstream rio; 
    rio.open(TrgName, ios::in | ios::out | ios::binary); 
    if(!rio.is_open()) { 
        cout << "Sort failure" << endl; 
        return -6; 
    } 
 
    bool sorted = false; 
    while(!sorted) { 
        cout << endl; 
        sorted = true; 
        for(int i = 0; i < count-1 ; i++) { 
            rio.seekp(i * Size, ios::beg); 
            if(!rio) 
                goto Exit; 
            int num1, num2; 
                rio.read((char *)&num1, Size). 
                    read((char *)&num2, Size); 
            cout << num1 << " " << num2 << endl; 
            if(num2 < num1) { 
                rio.seekp(-2 * Size, ios::cur); 
                rio.write((char *)&num2, Size). 
                    write((char *)&num1, Size); 
                sorted = false; 
            } 
        } 
    } 
    Exit:; 
    if(!sorted) { 
        cout << "Seek error" << endl; 
        return -7; 
    } 
        // wyprowadzanie 
    cout << endl << "showing ... " << endl; 
    rio.seekp(0, ios::beg); 
    for(int i = 0; i < count ; i++) { 
        rio.read((char *)&tmp, Size); 
        cout << tmp << endl; 
    } 
    
    return 0; 

 
Program  tworzy  plik  binarny,  do  któ rego  zapisuje  dane  pochodzą ce  pliku  Data.txt.  Następnie  dane  sortuje  i 
wyprowadza. 
 
Ponieważ  funkcje  read  i  write  oczekują   argumentó w  typu  char *  i  int,  wskazana  zmiennych  cał

kowitych 

(np. &tmp) poddano jawnej konwersji do typu char *.