background image

Ć

wiczenia 12 VI 2012 

 
REKORDY (STRUKTURY) 
 
W  tablicach  mieliśmy  do  czynienia  z  elementami  tego  samego  typu,  natomiast  tutaj  możemy  łączyć  różne 
typy danych. Matematycznie, możemy to potraktować jako iloczyn kartezjański, tzn. 
Mając dane zbiory Z

1

, ..., Z

n

 możemy utworzyć iloczyn kartezjański Z

1

 x...xZ

n

. Wówczas element ma postać: 

(z

1

,...,z

n

).  Każdy  taki  ciąg  nazywamy  rekordem.  W  tym  przypadku  można  wybrać  operator  pozwalający 

pobrać  poszczególne  wartości.  Operator  ten  wybiera  z  rekordu  (z1,...,zn)  np.  i-tą  składową.  W  tablicy 
mogliśmy  za  pomocą  pętli  for  oraz  indeksu  (liczba  całkowita)  wykonać  potrzebne  operacje,  natomiast  w 
przypadku rekordu, identyfikatory stanowią nieuporządkowany zbiór różnych nazw, nie tworząc ustalonego 
typu  danych.  Składowe  rekordów  należy  nazywać  oddzielnie  nie  mogąc  się  do  nich  odwoływać  kolejno. 
Rekordy  warto  stosować  wówczas,  gdy  chcemy  operować  złożoną  strukturą  danych  mając  do  niej  dostęp 
przez jedną zmienną. Struktury są deklarowane ze słowem kluczowym struct. Deklaracja ma postać: struct 
nazwa; 
Natomiast jej definicja: struct nazwa { /*...*/ }; 
Zauważmy,  że  nazwa  jest  nazwą  nowo  zdefiniowanego  typu.  Na  końcu  struktury  dajemy  średnik  po 
nawiasie klamrowym. 
 
Przykład 
 
#include <cstdlib> 
#include <iostream> 
 
using namespace std; 

background image

 
struct pracownik  { 
        char *tytul_stopien; 
        char *nazwisko; 
        int wiek, liczba_publikacji;  }; 
int main() { 
 

pracownik prac; 

 

prac.tytul_stopien ="Dr"; 

 

prac.nazwisko = "Jan Nowak"; 

 

prac.wiek = 35; 

 

prac.liczba_publikacji = 32; 

 

cout <<"info1 : "<<prac.tytul_stopien <<endl; 

 

cout <<"info2  : "<<prac.nazwisko << endl; 

 

cout <<"info3  : "<<prac.wiek<< endl; 

 

cout <<"info4  : "<<prac.liczba_publikacji <<endl; 

 

 

    system("PAUSE"); 
    return EXIT_SUCCESS; 

 
Szczególną  uwagę  przy  korzystaniu  z  rekordów  należy  zwrócić  na  zgodność  typów.  Każda  deklaracja 
struktury wprowadza nowy, niepowtarzalny typ, np. 
struct sI { int n ; }; 
struct sII { int m ; }; 
są to dwa różne typy, stąd w deklaracjach 

background image

sI a,b ; 
sII c ; 
zmienne a oraz b są tego samego typu sI, ale c już nie!  
Wobec tego przypisania 
a=b; 
b=a; 
są poprawne!  
a = c; 
nie jest poprawne! 
Natomiast przypisania składowych rekordów o tych samych typach są poprawne, np. 
a.i = b.j; 
 
WSKAŹNIKI 
 
Do tej pory deklarowaliśmy np. 
int i; 
rezerwując miejsce w pamięci dla zmiennej i typu całkowitego. 
Wskaźnik  (ang.  pointer)  jest  to  specjalny  rodzaj  zmiennej,  w  której  zapisany  jest  adres  w  pamięci 
komputera,  tzn.  wskaźnik  wskazuje  miejsce,  gdzie  zapisana  jest  jakaś  informacja  (stąd  nazwa  zmienna 
wskaźnikowa). 
Adres  to  pewna  liczba  całkowita,  w  sposób  jednoznaczny  definiująca  położenie  pewnego  obiektu  (np. 
liczby,  znaku,  struktury  czy  tablicy)  w  pamięci  komputera.  Wskaźnik  ma  ścisłą  kontrolę  typów  i  z  tego 
powodu nie tylko wskazuje miejsce zajmowane przez zmienną, ale także ile bajtów ta zmienna potrzebuje,  
Definicja zmiennej typu wskaźnikowego ma następującą strukturę: 
typ *identyfikator_zmiennej; 

background image

 
Przykład:  
int *i; (zapis może być różny: int    *i1; int    *     i2; int*     i3; int*i4;)   
float *x; 
float *x,y; 
 
Jak  już  wspominaliśmy,  operator  &  służy  do  pobrania  adresu  miejsca  w  pamięci,  gdzie  istnieje  dana 
zmienna, jest nazywany referencją a operator * jest nazywany operatorem dereferencji lub wskaźnikiem. 
 
Przykład 
 
#include <cstdlib> 
#include <iostream> 
 
using namespace std; 
 
int main() 

   int i=3;   
        int *j;  
        j=&i; //przypisanie zmiennej, która przechowuje adres adresu zmiennej i 
cout<<"wartosc liczby j="<<*j<<endl;  
        cout<<"adres liczby i ="<<&i<<endl; 
 
    system("PAUSE"); 

background image

    return EXIT_SUCCESS;   
 }   
 
Inicjalizacja wskaźnika może być wykonana następująco: 
int *wsk = NULL;  

(NULL oznacza element nie istniejący) 

int *wsk = 0;  
 
Przy  tworzeniu  i  używania  wskaźników  łatwo  można  popełnić  błąd.  Jeżeli  zaniedbamy  przypisanie 
wskaźnikowi adresu. 
int *wskaznik_numer; 
*wskaznik_numer = 1532;  
Jest to błąd, ponieważ nie wiemy co oznacza komórka o adresie 1532, może się okazać, iż adres ten jest już 
zajęty przez program, wówczas nie będzie można zapisać nic w miejsce wskazane przez wskaznik_numer 
(jest on dość trudny do wykrycia).  
 
TABLICE-WSKAŹNIKI 
 
Przykład: 
 
#include <cstdlib> 
#include <iostream> 
 
using namespace std; 
 
int main() 

background image

{        int x[15]; 
int k; 
cout<<"podaj rozmiar swojej tablicy"<<endl; 
cin>>k; 
 
        for (int i=0; i<k; i++) 
                x[i]=rand()%100; 
        for (int i=0; i<k; i++) 
                cout<<"x "<<i<<"= "<<*(x+i)<<"*   "<<x[i]<<endl; 
 
    system("PAUSE"); 
    return EXIT_SUCCESS; 

 
Zadanie 10.1 (domowe): 
 
Posługując się tablicą z rekordami – zaprojektuj listę towarów strukturze: dostawca (rekord), rodzaj towaru 
(art. papiernicze, zabawki, etc.), ilość, cena, dzień tygodnia, w którym realizowana jest dostawa, itd. Dla 
wszystkich przypadków wprowadź przykładowe dane. 
 
SORTOWANIE BĄBELKOWE 
 
Porównywane są kolejne elementy tablicy i jeśli elementy są w nieodpowiedniej kolejności, to są one 
zamieniane miejscami. Jeśli sortowana struktura danych posiada k elementów to aby sprawdzić wszystkie 
elementy musi być wykonane k-1 porównań. W najmniej korzystnym przypadku element, który powinien 

background image

znaleźć się na początku tablicy znajduje się na końcu. Ponieważ przy jednym sprawdzeniu element może się 
przesunąć tylko o jedno miejsce to musimy wykonać k-1 sprawdzeń listy, czyli zostanie wykonanych (k-1)

2

 

porównań. W trakcie zamiany wartości w tablicy wykorzystywana jest zmienna pomocnicza, nazwijmy ją 
np. tmp. 
 
Przykład: 
 
int i, j, k, x, tmp; 
int tablica[10]; 
 
cout << "Podaj ilość liczb : \n"; 
cin>>k; 
cout << "Podaj elementy swojej tablicy: \n"; 
 
for (i=0; i<k; i++) 
cin >>tablica[i]; 
 
for (i=0;i<k; i++) 
for (j=0;j<k; j++) 
if (tablica[j]>tablica[j+1]) 
{  
tmp = tablica[j];  
tablica[j] = tablica[j+1]; 
tablica[j+1] = tmp;  

background image

 
for (i=0; i<=4; i++) /* wyświetlanie posortowanych liczb */  
cout << tablica[i] << " , "<<endl;  
 
system("pause"); 
return 0; 

 
TABLICE DYNAMICZNE 

 

Ich pamięć i wymiar są alokowane podczas pracy programu. Dynamiczna alokacja tablicy jednowymiarowej 

może wyglądać (przy użyciu słowa kluczowego new) następująco: 

 
int * tablica; 
tablica = new int[rozmiar]; 
 
gdzie rozmiar jest wyrażeniem typu int. 
lub 
 
int *tablica= new int [rozmiar]; 
 
Każdą zadeklarowaną tablicę należy „zlikwidować” tzn. zwolnić zajętą przez nią pamięć: 
 

background image

delete [] tablica; 
 

Pamięć komputera nie jest nieograniczona. Trzeba się z tym liczyć i sprawdzać czy operacja się powiodła 

 
float *wsk; 
wsk=new float[8192]; 
if(!wsk)                //czyli if(wsk==NULL) 

 

cout<< ”Pamiec wyczerpana”;} 

 
Przykład: Dynamiczna alokacja tablicy jednowymiarowej: 
 
#include <iostream> 
using namespace std; 
 
main() 

  int *T;         // tworzymy wskaźnik 
  int i,n; 
  cin >> n;        // odczytujemy ilość komórek 
  T = new int[n];  // tworzymy tablicę dynamiczną o n komórkach 
  for(i = 0; i < n; i++) 
       cin >> T[i];   // wczytujemy kolejne komórki 
     

background image

  for(i = 0; i < n; i++)// wypisujemy odczytaną tablicę 
       cout << endl << "T[" << i << "] = " << T[i]; 
  cout << endl; 
   
  system("PAUSE"); 

 
Zadanie 10.2 (domowe): 
 
Zadeklarować dynamiczną jednowymiarową tablicę elementów typu rzeczywistego, wypełnić ją danymi a 
następnie: posortować tablicę rosnąco, malejąco, podać wartość mediany. 
 
Przykład:  Dynamiczna alokacja tablicy dwuwymiarowej: 
 
#include <iostream> 
using namespace std; 
 
main() 

   int **tablica; 
   int l_wierszy=5,l_kolumn=6; 
   tablica=new int*[ l_wierszy]; 
 
   for(int i=0;i< l_wierszy;i++) 
      tablica[i]=new int[l_kolumn]; 

background image

 
   //I teraz już mamy tablicę tablica[5][6];i możemy ją wypełnić 
    
   for(int i=0;i< l_wierszy; i++) 
 

for(int j=0;j< l_kolumn; j++) 

 

 

cin>>tablica[i][j]; 

 
   cout<<tablica[3][4]; 
 
   for(int i=0;i< l_wierszy;i++)// zwolnienie pamięci 
        delete []tablica [i];  
   delete []tablica; 

 
Funkcja  (czasami  zwana  procedurą)  to  fragment  programu,  któremu  nadano  odrębną  nazwę  (powinna 
mówić  o  jej  działaniu),  dzięki  czemu  może  być  wykonywany  poprzez  podanie  nazwy  oraz  ewentualnych 
argumentów. Argumentami są natomiast dane przekazywane do funkcji.  
Budowa funkcji: 
typ_zwracanej_wartosci_z_funkcji nazwa_funkcji( typ_argumentu_1 nazwa_argumentu_1 ,…, 
typ_argumentu_m nazwa_argumentu_m ). 
 
{ 
    return zwracana_wartosc

;

 


 

background image

Słowem kluczowym return regulujemy, co ma zostać zwrócone przez funkcję. 
 
Wywoływanie funkcji 
 
Polega na wpisaniu jej nazwę i przekazaniu wartości do funkcji:  
nazwa_funkcji( wartosc_argumentu_1 ,…, wartosc_argumentu_m ); 
 
Przykład (bez argumentów i zwracania wartości) 
 
#include <cstdlib> 
#include <iostream> 
 
using namespace std; 
 
int gwiazdki(int ile)                                 
{                                                     
 

int i ; 

 

for(i = 0 ; i < ile ; i++) 

 

 

 

cout << " * " ; 

 

 

return 13 ;                                      

}              
 
int   main() 

background image


 

int m = 100 ; 

 

cout << "Zaczynamy" << endl ; 

 

 

 

m = gwiazdki(20) ;                

 

cout << "\nNa wyjsciu zmiana wartosci i m = " << m <<endl;                  

 

 

system("PAUSE"); 
    return EXIT_SUCCESS;  

 
Przykład 2 (tym razem ze zwracaniem wartości) 
 
I sposób realizacji 
 
#include <cstdlib> 
#include <iostream> 
 
using namespace std; 
 
int kukulka(int ile)                              // 
{                                                  // 
 

int i ; 

 

for(i = 0 ; i < ile ; i++) 

 

background image

 

 

cout << "Ku-ku ! " ; 

 

 

return 77 ;                                   // 

}  
/**************************************************/ 
int   main() 

 

int m = 20 ; 

 

cout << "Zaczynamy" << endl ; 

 

 

 

m = kukulka(5) ;             // 

 

cout << "\nNa koniec m = " << m ;               // 

 

 

system("PAUSE"); 
    return EXIT_SUCCESS;  

 
II sposób realizacji 
 
#include <cstdlib> 
#include <iostream> 
 
using namespace std; 
 
int gwiazdki(int ile);            

background image

 
int   main() 

 

int m = 100 ; 

 

cout << "Zaczynamy" << endl ; 

 

 

 

m = gwiazdki(20) ;                

 

cout << "\nNa wyjsciu zmiana wartosci i m = " << m <<endl;                  

 

 

system("PAUSE"); 
    return EXIT_SUCCESS;  

 
 
int gwiazdki(int ile)                                 
{                                                     
 

int i ; 

 

for(i = 0 ; i < ile ; i++) 

 

 

 

cout << " * " ; 

 

 

return 13 ;                                      


 

background image

Czas życia zmiennych w funkcjach 
 
Zmienne,  które  zostały  utworzone  w  funkcji  są  tymczasowe,  pojawiają  się  do  użycia  przy  każdorazowym 
wywołaniu funkcji a znikają po jej opuszczeniu. Zmienne te nie zachowują niczego z poprzedniego.  
 
Przykład 3 
 
#include <cstdlib> 
#include <iostream> 
 
using namespace std; 
 
float mnozenie_liczb() 
{    float a, b; 
cin >> a; 
cin >> b; 
    return a * b; 

 
int   main() 

 

 cout << "Prosze podac dwie liczby: "; 

    float wynik = mnozenie_liczb(); 
   cout << "Wynik dzialania wynosi: " << wynik << endl;                
 

 

background image

system("PAUSE"); 
    return EXIT_SUCCESS;  

 
Zadanie 10.3: 
 
Napisać program, który odwołując się do funkcji potega (zdefiniowanej przed programem) dla liczb 
całkowitych z określonego przedziału, policzy wszystkie potęgi od 2 do 5. 
 
Zadanie 10.4: 
 
Napisać program, który odwołując się do funkcji silnia (zdefiniowanej przed programem) dla wczytanej 
liczby naturalnej policzy silnię.