• Nie Znaleziono Wyników

Podstawy Programowania Zajęcia laboratoryjne 7 I rok Bioinformatyki Politechniki Poznańskiej

N/A
N/A
Protected

Academic year: 2022

Share "Podstawy Programowania Zajęcia laboratoryjne 7 I rok Bioinformatyki Politechniki Poznańskiej"

Copied!
8
0
0

Pełen tekst

(1)

Podstawy Programowania Zajęcia laboratoryjne 7

I rok Bioinformatyki Politechniki Poznańskiej

Rok akademicki 2020/2021

1 Wskaźniki

Wskaźnik to specjalny rodzaj zmiennej, w której zapisany jest adres w pamięci komputera. Oznacza to, że wskaźnik wskazuje miejsce, gdzie zapisana jest jakaś informacja (np. zmienna typu liczbowego czy struktura).

Adres pamięci to pewna liczba całkowita, jednoznacznie definiująca położenie pewnego obiektu w pamięci kompu- tera. Tymi obiektami mogą być np. zmienne, elementy tablic czy nawet funkcje. Jeżeli str jest obiektem typu char, a pstr ma być wskaźnikiem, który wskazuje na ten obiekt, to taki wskaźnik można zdefiniować następujący sposób:

1 c h a r s t r ;

2 c h a r ∗ p s t r ; // ∗ o p e r a t o r o d w o l a n i a s i e do danych wskazywanych p r z e z w s k a z n i k 3 p s t r = &s t r ; //& o p e r a t o r p o b r a n i a a d r e s u ( z m i e n n e j , s t r u k t u r y , t a b l i c y i t p . )

Deklaracja wskaźnika pojawia się już w powyższym przykładzie, poprzez podanie typu, operatora dereferencji

∗ i nazwy zmiennej wskaźnikowej. W poniższym przykładzie zarówno wskaźnik jak i tablica są zmiennymi typu wskaźnikowego.

1 typ ∗ w s k a z n i k ;

2 typ t a b l i c a [ r o z m i a r ] ;

Przypisanie adresu do wkaźnika:

1 w s k a z n i k = inny_wskaznik ; 2 w s k a z n i k = &zmienna ;

Odczyt wartości z adresu / zapis pod adresem:

1 zmienna = ∗ w s k a z n i k ; 2 ∗ w s k a z n i k = w a r t o s c ;

Działania na wskaźnikach:

1 w s k a z n i k++;

2 wskaznik −−;

3 w s k a z n i k + i n d e k s ; 4 w s k a z n i k [ i n d e k s ] ;

1

(2)

Operator wyłuskania (*zmienna) ma wysoki priorytet, szczególnie z operatorami ++ i −−. Zwróć uwagę, że poniższe działania można łączyć:

1 w s k a z n i k = inny_wskaznik + i n d e k s ; 2 zmienna = ∗ ( w s k a z n i k + i n d e k s ) ; 3 zmienna = ∗ w s k a z n i k++;

4 w s k a z n i k [ i n d e k s ] ;

Rysunek 1: Operacje na wskaźnikach

Wskaźniki typu void: w C wskaźniki dowolnego typu mogą być przydzielane do wskaźników typu void i od- wrotnie, natomiast w C++ wymagana jest jawna konwersja:

1 v o i d ∗ wsk ;

2 c h a r ∗ s t r = " Tekst "; 3 wsk = (c h a r∗ ) s t r ;

2 Przykłady do przetestowania i zrozumienia

Wskaźniki, zapoznanie się z użyciem wskaźników na poniższych przykładach (kod 1.1 w materiałach na stronie):

1 #i n c l u d e <s t d i o . h>

2

3 i n t main ( ) 4 {

5 i n t temp ;

6 i n t ∗ w s k a z n i k ; 7 temp = 1 0 0 ; 8

9 p r i n t f (" L i c z b a w z m i e n n e j TEMP: %d\n ", temp ) ; 10 w s k a z n i k = &temp ; // p r z y p i s u j e m y w s k a z n i k o w i

11 // a d r e s z m i e n n e j TEMP

12 ∗ w s k a z n i k = 2 0 0 ; // pod a d r e s w s k a z n i k a

(3)

13 // p r z y p i s u j e m y w a r t o s c 200

14 p r i n t f (" L i c z b a wskazywana p r z e z w s k a z n i k : %d\n " , ∗ w s k a z n i k ) ; 15 p r i n t f (" L i c z b a w z m i e n n e j TEMP: %d\n ", temp ) ;

16

17 r e t u r n 0 ;

18 }

Kolejny przykład (kod 1.2 w materiałach na stronie):

1 #i n c l u d e <s t d i o . h>

2

3 i n t main ( ) 4 {

5 i n t l i c z b a = 6 5 ;

6 p r i n t f (" Wartosc z m i e n n e j : %d\n ", l i c z b a ) ; 7 p r i n t f (" Adres z m i e n n e j : %p\n ", &l i c z b a ) ; 8

9 r e t u r n 0 ;

10 }

Wskaźniki i tablice, w języku C wskaźniki i tablice w wielu przypadkach traktowane są tak samo. Elementy tablicy podczas uruchamiania programu przypisywane są do kolejnych adresów w pamięci. Taki sposób alokacji pa- mięci dla tablic pozwala na odnalezienie danego elementu tablicy za pomocą indeksów lub za pomocą wskaźników.

Przykład (kod 2 w materiałach na stronie):

1 #i n c l u d e <s t d i o . h>

2

3 i n t main ( ) 4 {

5 i n t t ab [ 4 ] = { 2 0 , 4 0 , 5 0 , 1 0 0 } ;

6 i n t ∗ wsk ; // w s k a z u j e na m i e j s c e w p a m i e c i

7 wsk = &t a b [ 0 ] ; // p r z y p i s u j e m y a d r e s na p i e r w s z y e l e m e n t t a b 8

9 p r i n t f (" Element p i e r w s z y t a b l i c y −−> %d\n " , ∗ wsk ) ; // i w y s w i e t l z a w a r t o s c ∗ wsk 10

11 wsk = wsk + 1 ; // wez a d r e s a k t u a l n y + 1

12 p r i n t f (" Element d r u g i t a b l i c y −−> %d\n ", ∗ wsk ) ; 13

14 wsk++;

15 p r i n t f (" Element t r z e c i t a b l i c y −−> %d\n ", ∗ wsk ) ; 16

17 wsk++;

18 ∗ wsk = 2 0 0 ;

19 p r i n t f (" Element c z w a r t y t a b l i c y −−> %d\n " , ∗ wsk ) ; 20

21 r e t u r n 0 ;

22 }

Zrealizuj poniższe zadania:

Zad 1. Początkowo zadeklaruj tablicę kilku elementów, niech rozmiar będzie stałą (np. const int size = 3 ), wyświetl dla każdego elementu: numer indeksu i wartość tablicy. Następnie chcemy mieć tablicę, która może przechowywać wskaźniki dla typu int. Zatem deklarujemy zmienna wskaźnikową ptr jako tablicę wskaźników dla typu int, o roz- miarze size (tak aby każdy element w ptr zawierał wskaźnik do wartości int). Przypisujemy adresy i wyświetlamy dla całej tablicy: numer indeksu i wartość elementu wskazywanego przez wskaźnik.

(4)

Zad 2. Wykorzystaj tablice z poprzedniego zadania, zadeklaruj wskaźnik i przypisz adres na pierwszy element tablicy. Następnie w pętli dla całej tablicy wyświetl adres i wartość elementu wskazywanego przez wskaźnik (wyko- rzystaj inkrementację wskaźnika ptr++).

Wskaźniki i funkcje, w języku C parametry do funkcji przekazywane są zawsze przez wartość. Co znaczy, że wewnątrz funkcji operujemy jedynie na kopiach zmiennych. Przekazując do funkcji zamiast zwykłych zmiennych wskaźniki do tych zmiennych, mamy możliwość modyfikacji oryginalnych wartości przechowywanych przez zmienne w ciele funkcji. Przykład (kod 3 w materiałach na stronie):

1 #i n c l u d e <s t d i o . h>

2

3 v o i d sum (i n t ∗k , i n t ∗ l , i n t ∗ s ) ; 4

5 i n t main ( ) 6 {

7 i n t a , b ;

8 i n t suma ;

9

10 p r i n t f (" Podaj l i c z b e a : ") ; 11 s c a n f ("%d ",&a ) ;

12

13 p r i n t f (" Podaj l i c z b e b : ") ; 14 s c a n f ("%d ",&b ) ;

15

16 sum(&a ,&b,&suma ) ;

17 p r i n t f (" Suma : (%d + %d ) = %d\n ", a , b , suma ) ; 18

19 r e t u r n 0 ;

20 } 21

22 v o i d sum (i n t ∗k , i n t ∗ l , i n t ∗ s ) 23 {

24 ∗ s = ( ∗ k + ∗ l ) ; 25 }

Zrealizuj poniższe zadanie:

Zad 3. Napisz funkcję swap, która zamienia element a i b (a na b, b na a).

Funkcja zwracająca wskaźnik, czyli funkcja zwracająca wskaźnik do funkcji wywołującej. W przypadku ta- kich funkcji należy zachować szczególną ostrożność ponieważ zmienne lokalne funkcji NIE działają poza funkcją.

Mają zasięg tylko wewnątrz funkcji. Przykład (kod 4 w materiałach na stronie):

1 #i n c l u d e <s t d i o . h>

2

3 i n t ∗ w i e k s z a (i n t ∗a , i n t ∗b ) ; 4

5 v o i d main ( ) 6 {

7 i n t a = 7 ;

8 i n t b = 7 7 ;

9 i n t ∗p = w i e k s z a (&a , &b ) ; 10 p r i n t f ("%d j e s t w i e k s z a ", ∗ p ) ; 11 }

12

13 i n t ∗ w i e k s z a (i n t ∗a , i n t ∗b )

(5)

14 {

15 i f( ∗ a > ∗b )

16 r e t u r n a ;

17 e l s e

18 r e t u r n b ;

19 }

Wskaźnik na wskaźnik, normalnie wskaźnik zawiera adres zmiennej. Gdy definiujemy wskaźnik na wskaźnik to pierwszy wskaźnik, zawiera adres drugiego wskaźnika, który wskazuje na adres zmiennej. Przykład (kod 5 w materiałach na stronie):

1

2 #i n c l u d e <s t d i o . h>

3

4 i n t main ( ) {

5

6 i n t zmienna ;

7 i n t ∗ p t r ;

8 i n t ∗∗ p p t r ; 9

10 zmienna = 6 6 ;

11 p t r = &zmienna ; // w s k a z n i k w s k a z u j e na a d r e s z m i e n n e j

12 p p t r = &p t r ; // w s k a z n i k w s k a z u j e na a d r e s wskaznika , w s k a z u j a c e g o na a d r e s z m i e n n e j 13

14 p r i n t f (" w a r t o s c z m i e n n e j : %d\n ", zmienna ) ;

15 p r i n t f (" w a r t o s c z m i e n n e j na k t o r a w s k a z u j e w s k a z n i k ∗ p t r : %d\n ", ∗ p t r ) ;

16 p r i n t f (" w a r t o s c z m i e n n e j na k t o r a w s k a z u j e w s k a z n i k na w s k a z n i k ∗∗ p t r %d\n ", ∗∗ p p t r ) ; 17

18 r e t u r n 0 ;

19 }

3 Dynamiczna alokacja pamięci

Czasami z góry nie wiadomo ilu elementowa tablica będzie potrzebna. Deklaruje się wówczas zmienną wskaźnikową pożądanego typu i wywołuje funkcje alokacji pamięci (o żądanym rozmiarze). Funkcja ta zwraca adres pamięci.

Należy to wpisać do wskaźnika na którym można później już normalnie działać (np. wskaznik[indeks] = wartosc;).

Istotne jest aby pamiętać, że zaalokowaną pamięć należy zwolnić jeśli nie jest już potrzebna. Niezwolnione obszary pamięci, czytanie i zapisywanie pod niedozwolone adresy to częste i poważne błędy. Funkcje alokujące i zwalniające pamięć malloc() i free() w C zostały zastąpione w C++ przez operatory new oraz delete.

Dynamiczna alokacja pamięci, użycie funkcji malloc() pozwala bezpośrednio przydzielenie pamięci dla wskaź- nika, deklaracja:

1 i n t ∗ p t r ;

2 p t r = (i n t∗ ) m a l l o c ( n ∗ s i z e o f(i n t) ) ; Powyższy zapis może być jednolinijkowy:

1 i n t ∗ p t r = (i n t∗ ) m a l l o c ( n ∗ s i z e o f(i n t) ) ; gdzie:

• (int*) - rzutowanie na typ wskaźnika dla jakiego przydzielmy pamięć, ponieważ funkcja malloc zwraca zawsze void*

(6)

• (sizeof(int)) - argumentem dla funkcji malloc jest wielkość obszaru pamięci jaki chcemy przydzielić. Tu jest to rozmiar jednego inta, dlatego aby poprawnie przydzielić pamięć mnożymy przez n-elementów.

Przykład dynamicznej alokacji pamięci dla liczby całkowitej (kod 6 w materiałach na stronie):

1 #i n c l u d e <s t d i o . h>

2 #i n c l u d e < s t d l i b . h>

3

4 i n t main ( ) 5 {

6 i n t ∗ p t r ;

7 p t r = (i n t∗ ) m a l l o c (s i z e o f(i n t) ) ; // a l o k a c j a d l a j e d n e j l i c z b y c a l k o w i t e j 8

9 i f( p t r==NULL)

10 {

11 p r i n t f (" \n P r z y d z i e l e n i e p a m i e c i n i e j e s t mozliwe ") ;

12 r e t u r n 0 ;

13 }

14

15 p r i n t f (" Podaj w a r t o s c z m i e n n e j : ") ; 16 s c a n f ("%d ", p t r ) ;

17 p r i n t f (" \ nWartosc t o : %d ", ∗ p t r ) ; 18 f r e e ( p t r ) ;

19 p r i n t f (" \ nWartosc po z w o l n i e n i u p a m i e c i : %d " , ∗ p t r ) ; 20

21 r e t u r n 0 ;

22 }

Przykład dynamicznej alokacji pamięci dla n liczb całkowitych (kod 7 w materiałach na stronie):

1 #i n c l u d e <s t d i o . h>

2 #i n c l u d e < s t d l i b . h>

3

4 i n t main ( ) 5 {

6 i n t n ;

7 i n t sum = 0 ;

8 p r i n t f (" I l e elementow c h c e s z sumowac : ") ;

9 s c a n f ("%d ", &n ) ; // a l o k a c j a d l a n l i c z b , w i e l k o s c z d e f i n i o w a n a p r z e z u z y t k o w n i k a 10

11 i n t ∗ p t r = (i n t∗ ) m a l l o c ( n ∗ s i z e o f(i n t) ) ; // dynamiczna a l o k a c j a p a m i e c i 12

13 i f( p t r == NULL) // z a b e z p i e c z e n i e

14 {

15 p r i n t f (" Nie d o s z l o do a l o k a c j i p a m i e c i . ") ;

16 r e t u r n 0 ;

17 }

18

19 p r i n t f (" Podaj e l e m e n t y : ") ; // w c z y t a n i e elementow i sumowanie 20 f o r(i n t i = 0 ; i < n ; ++i )

21 {

22 s c a n f ("%d " , p t r + i ) ; 23 sum += ∗ ( p t r + i ) ;

24 }

25

26 p r i n t f (" Suma = %d " , sum ) ; 27

28 f r e e ( p t r ) ; // z w o l n i e n i e p a m i e c i !

(7)

29 30

31 r e t u r n 0 ;

32 }

4 Zadania

Zad 1. Napisz program definiujący zmienną typu int oraz wskaźnik do zmiennej typu int. Program powinien wczytać z klawiatury wartość i podstawić ją do zmiennej stosując wskaźnik i operator adresu.

Zad 2. Na podstawie poniższego fragmentu kodu (fragment kodu w osobnym pliku) napisz program, który wyświetli tablicę "jakas_tablica". A następnie poprzez wskaźnik wsk element tablicy o indeksie 7 zostanie zmieniony na 77 i ponownie wyświetli tablicę.

1 i n t j a k a s _ t a b l i c a [ 1 0 ] ; // d e k l a r a c j a t a b l i c y

2 i n t ∗ wsk ; // d e k l a r a c j a w s k a z n i k a typu i n t

3 wsk = j a k a s _ t a b l i c a ; // wsk w s k a z u j e 1− s z y e l e m e n t 4 wsk = &j a k a s _ t a b l i c a [ 0 ] ; // t o j e s t d o k l a d n i e t o samo

5 // co i n s t r u k c j a w c z e s n i e j

6 wsk++; // t e r a z wskazujemy na 2− g i e l e m e n t t a b l i c y 7 ( ∗ wsk)++; // a t e r a z t e n 2− g i e l e m e n t zwiekszamy o 1

Zad 3. Zadeklaruj tablice 10-elementów i wyświetl dla każdego elementu jego adres (ptr) oraz wartość elementu wskazywanego przez wskaźnik (∗ptr). Wartości wyświetlaj od największego indeksu do najmniejszego (wykorzystaj dekrementację wskaźnika ptr−−).

Zad 4. Wskaźniki można porównywać wykorzystując operatory relacyjne. Wykorzystaj zadanie 2 z punktu wskaźniki i tablice i zmodyfikuj je następująco: dopóki adres, na który wskazuje zmienna wskaźnikowa jest mniejszy lub równy ostatniemu elementowi tablicy to: wyświetlaj adres oraz wartość elementu wskazywanego przez wskaźnik a następnie zwiększ zmienną wskaźnikową.

Zad 5. Utwórz funkcję, która zwraca średnią arytmetyczną liczb. Funkcja ta jako parametry przyjmuje wskaźnik na tablicę i rozmiar tablicy.

Zad 6. Napisz program, który realizuje następujące funkcje dla tablic jednowymiarowych (alokowanych dynamicz- nie):

• Dodaje odpowiadające sobie elementy tablic – suma dwóch wektorów

• Mnoży odpowiadające sobie elementy tablic

• Wyznacza różnicę pomiędzy maksymalnym a minimalnym elementem tablicy

(8)

Zad 7. Napisz program, który prosi użytkownika o podanie n zbiorów składających się z k liczb typu double, a następnie:

• Zapisuje te dane w tablicy o wymiarach nxk

• Oblicza średnią dla każdego zbioru z osobna

• Oblicza średnią dla wszystkich wartości

• Znajduje największą spośród wartości

• Wyświetla wyniki

Cytaty

Powiązane dokumenty

Napisz funkcję UstalLiczbeCalkowita, która zwraca jako wartość wskaźnik do zmiennej typu int.. Zadaniem funkcji jest zaalokowanie zmiennej oraz ustalenie jej wartości

Na początku podajmy komunikat, do czego jest nasz program (kategoria wygląd).. W kolejnym kroku z kategorii czujniki wybierzemy

Na początek musimy stworzyd dwie zmienne, które będą pamiętad liczby wprowadzone przez użytkownika.. Musimy w kategorii zmienne kliknąd w przycisk &gt; Utwórz zmienną

Zagadnienia: specyfikacja funkcji, operacje wejścia i wyjścia na plikach, for- matowane wejście i wyjście, struktury sterujące, rekurencja, nie- bezpieczeństwa rekurencji,

• by zapisać znak do pliku wskazywanego przez plik należy użyć funkcji int putc(int znak, FILE *plik), która zwraca wartość tego znaku lub EOF jako sygnał wystąpienia

• jeśli liczba wartości jest mniejsza od rozmiaru, uzupełnia się przez 0. ➔ Operacji na napisach

 dynamiczne alokowanie pamięci najczęściej stosowane jest dla tablic, których rozmiar znany jest dopiero w trakcie wykonania programu. int* tab_int = NULL; // tab_int[0]=1; -

int usun_element_listy( // funkcja zwraca kod sukcesu lub błędu el_list** Glowa_wsk, // lista - identyfikowana przez wskaźnik el_list* Element // wskaźnik do