Wskaźniki

Transkrypt

Wskaźniki
Wskaźniki
Programowanie Proceduralne
1
Adresy zmiennych
Sterta
# include<s t d i o . h>
int a = 2 ;
int main ( )
{
int b = 3 ;
Stos
printf ( " adres zmiennej a %p\n" , &a ) ;
printf ( " adres zmiennej b %p\n" , &b ) ;
main()
b
0x7fff0be8dccc
return 0 ;
3
}
Dane
a
adres zmiennej a 0x601040
adres zmiennej b 0x7fff0be8dccc
Programowanie Proceduralne
0x601040
2
Kod
adresy pamięci
1
2
3
4
5
6
7
8
9
10
11
12
13
2
Wskaźniki
Wskaźnik (pointer )
int *wsk
adres zmiennej w pamięci (np. &a)
ws
kaznik
int a = 5 ;
printf ( "%p\n" , &a ) ;
Zmienna wskaźnikowa
zmienna przechowująca adres
int a
3
int ∗ wsk ;
wsk = &a ;
Typ wskaźnikowy
typ zmiennej znajdujący się pod wskazanym adresem (np. int)
Programowanie Proceduralne
3
Zmienna wskaźnikowa
Deklaracja
typ *identyfikator;
Przykład
int ∗ wa ;
float ∗ wx ;
char ∗ wz ;
void ∗w ;
int ∗t [ 1 0 ] ;
int ∗∗ ww ;
/∗ w s k a z n i k na zmienna t y p u i n t ∗/
/∗ w s k a z n i k na zmienna t y p u f l o a t ∗/
/∗ w s k a z n i k na zmienna d o w o l n e g o t y p u ∗/
/∗ t a b l i c a z m i e n n y c h w s k a z n i k o w y c h ∗/
/∗ w s k a z n i k na zmienna w s k a z n i k o w a ∗/
Programowanie Proceduralne
4
Operator pobrania adresu
Operator referencji & zwraca adres zmiennej
Przykład
1
2
3
4
5
6
7
8
9
int
int
int
int
a =10;
b =13;
∗ wa ;
∗ wb ;
wa = &a ;
wb = &b ;
wb = wa ;
Programowanie Proceduralne
5
Operator pobrania adresu
Operator referencji & zwraca adres zmiennej
Przykład
1
2
3
4
5
6
7
8
9
int
int
int
int
a =10;
b =13;
∗ wa ;
∗ wb ;
wa = &a ;
wb = &b ;
wa
wb
?
?
a
b
10
13
wb = wa ;
Programowanie Proceduralne
6
Operator pobrania adresu
Operator referencji & zwraca adres zmiennej
Przykład
1
2
3
4
5
6
7
8
9
int
int
int
int
a =10;
b =13;
∗ wa ;
∗ wb ;
wa = &a ;
wb = &b ;
wa
wb
a
b
10
13
wb = wa ;
Programowanie Proceduralne
7
Operator pobrania adresu
Operator referencji & zwraca adres zmiennej
Przykład
1
2
3
4
5
6
7
8
9
int
int
int
int
a =10;
b =13;
∗ wa ;
∗ wb ;
wa = &a ;
wb = &b ;
wa
wb
a
b
10
13
wb = wa ;
Programowanie Proceduralne
8
Operator dostępu do adresu
Operator dereferencji * daje dostęp do wskazanego adresu
Przykład
1
2
3
4
5
6
7
8
9
10
11
int
int
int
int
a =10;
b =13;
∗ wa = &a ;
∗ wb = &b ;
∗ wb = 5 ;
∗ wa = ∗ wb ;
wa = wb ;
∗ wb = ∗ wb + 1 ;
Programowanie Proceduralne
9
Operator dostępu do adresu
Operator dereferencji * daje dostęp do wskazanego adresu
Przykład
1
2
3
4
5
6
7
8
9
10
11
int
int
int
int
a =10;
b =13;
∗ wa = &a ;
∗ wb = &b ;
wa
wb
a
b
10
13
∗ wb = 5 ;
∗ wa = ∗ wb ;
wa = wb ;
∗ wb = ∗ wb + 1 ;
Programowanie Proceduralne
10
Operator dostępu do adresu
Operator dereferencji * daje dostęp do wskazanego adresu
Przykład
1
2
3
4
5
6
7
8
9
10
11
int
int
int
int
a =10;
b =13;
∗ wa = &a ;
∗ wb = &b ;
wa
wb
a
b
10
5
∗ wb = 5 ;
∗ wa = ∗ wb ;
wa = wb ;
∗ wb = ∗ wb + 1 ;
Programowanie Proceduralne
11
Operator dostępu do adresu
Operator dereferencji * daje dostęp do wskazanego adresu
Przykład
1
2
3
4
5
6
7
8
9
10
11
int
int
int
int
a =10;
b =13;
∗ wa = &a ;
∗ wb = &b ;
wa
wb
a
b
5
5
∗ wb = 5 ;
∗ wa = ∗ wb ;
wa = wb ;
∗ wb = ∗ wb + 1 ;
Programowanie Proceduralne
12
Operator dostępu do adresu
Operator dereferencji * daje dostęp do wskazanego adresu
Przykład
1
2
3
4
5
6
7
8
9
10
11
int
int
int
int
a =10;
b =13;
∗ wa = &a ;
∗ wb = &b ;
wa
wb
a
b
6
5
∗ wb = 5 ;
∗ wa = ∗ wb ;
wa = wb ;
∗ wb = ∗ wb + 1 ;
Programowanie Proceduralne
13
Uważaj na niezainicjowane zmienne wskaźnikowe.
wa
Przykład
int ∗ wa ;
∗ wa = 5 ;
NULL to adres 0
int ∗ wa ;
wa = 0 ;
wa = NULL ;
BANG!
/∗ s t d l i b . h ∗/
• Nigdy nie używaj operatora * na niezainicjowanej zmiennej.
• Wskazanie puste, adres 0, NULL - informacja, że wskaźnik nic
nie pokazuje
Programowanie Proceduralne
14
Wskaźniki jako argumenty funkcji
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
Sterta
# include<s t d i o . h>
void zwieksz ( int a )
{
a = a + 1;
}
Stos
int main ( )
{
int a = 3 ;
main()
a
zwieksz ( a ) ;
printf ( "%d\n" , a ) ;
3
Dane
return 0 ;
}
Kod
Programowanie Proceduralne
15
Wskaźniki jako argumenty funkcji
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
Sterta
# include<s t d i o . h>
void zwieksz ( int a )
{
a = a + 1;
}
Stos
zwieksz(3)
a
4
int main ( )
{
int a = 3 ;
main()
a
zwieksz ( a ) ;
printf ( "%d\n" , a ) ;
3
Dane
return 0 ;
}
Kod
Programowanie Proceduralne
16
Wskaźniki jako argumenty funkcji
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
Sterta
# include<s t d i o . h>
void zwieksz ( int a )
{
a = a + 1;
}
Stos
int main ( )
{
int a = 3 ;
main()
a
zwieksz ( a ) ;
printf ( "%d\n" , a ) ;
3
Dane
return 0 ;
}
Kod
Programowanie Proceduralne
17
Wskaźniki jako argumenty funkcji
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
Sterta
# include<s t d i o . h>
void zwieksz ( int ∗a )
{
∗a = ∗a + 1 ;
}
Stos
int main ( )
{
int a = 3 ;
main()
a
zwieksz(&a ) ;
printf ( "%d\n" , a ) ;
0x7fff0be8
3
Dane
return 0 ;
Kod
}
Programowanie Proceduralne
18
Wskaźniki jako argumenty funkcji
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
Sterta
# include<s t d i o . h>
void zwieksz ( int ∗a )
{
∗a = ∗a + 1 ;
}
Stos
zwieksz(0x7fff0be8)
a
int main ( )
{
int a = 3 ;
main()
a
zwieksz(&a ) ;
printf ( "%d\n" , a ) ;
0x7fff0be8
4
Dane
return 0 ;
Kod
}
Programowanie Proceduralne
19
Wskaźniki jako argumenty funkcji
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
Sterta
# include<s t d i o . h>
void zwieksz ( int ∗a )
{
∗a = ∗a + 1 ;
}
Stos
int main ( )
{
int a = 3 ;
main()
a
zwieksz(&a ) ;
printf ( "%d\n" , a ) ;
0x7fff0be8
4
Dane
return 0 ;
Kod
}
Programowanie Proceduralne
20
Wskaźniki jako argumenty funkcji
• poprzez wskaźnik (adres zmiennej) funkcja może zwrócić
dodatkową wartość
• scanf("%d", &x) ← funkcja modyfikuje zmienną x, stąd
argumentem musi być adres
http://xkcd.com
Programowanie Proceduralne
21
Znajdowanie minimum i maksimum
Problem: znajdź wartość minimalną i maksymalną w zbiorze liczb
Algorytm 1 Wyznaczanie maksimum i minimum
Dane wejściowe: ciąg n liczb {x1 , x2 , . . . , xn }
Wynik: wartość xmin i xmax , odpowiednio najmniejszy i największy
element podanego ciągu
1: xmin ← x1
2: xmax ← x1
3: dla każdego x ∈ {x2 , . . . , xn } wykonuj
4:
jeżeli x < xmin wykonaj
5:
xmin ← x
6:
jeżeli x > xmax wykonaj
7:
xmax ← x
8: zwróć xmin , xmax
Programowanie Proceduralne
22
Element minimalny i maksymalny
Przykład w C
1
2
3
4
5
6
7
8
9
10
11
12
13
void minmax ( float t [ ] , int n , float ∗ min , float ∗ max )
{
int i = 1 ;
∗ min=t [ 0 ] ;
∗ max=t [ 0 ] ;
while (
{
if (
if (
i =
}
i < n)
∗ min > t [ i ] ) ∗ min = t [ i ] ;
∗ max < t [ i ] ) ∗ max = t [ i ] ;
i + 1;
}
minmax1.c
Programowanie Proceduralne
23
Złożoność przeszukiwania
• Ilość porównań: 2(n − 1)
• Czy istnieje szybszy sposób?
• Dziel i zwyciężaj !
Programowanie Proceduralne
24
Algorytm 2 Wyznaczanie maksimum i minimum
Dane wejściowe: ciąg n liczb {x1 , x2 , . . . , xn }
Wynik: wartość xmin i xmax , odpowiednio najmniejszy i największy
element podanego ciągu
1: A ← ∅, B ← ∅
2: dla i = 1, 2, . . . , b n
2 c wykonuj
3:
jeżeli x2i−1 < x2i wykonaj
4:
A ← A ∪ {x2i−1 },
B ← B ∪ {x2i }
5:
w przeciwnym wypadku
6:
A ← A ∪ {x2i },
B ← B ∪ {x2i−1 }
7: jeżeli n jest nieparzyste wykonaj
8:
A ← A ∪ {xn },
B ← B ∪ {xn }
9: xmin ← min(A)
10: xmax ← max(B)
11: zwróć xmin , xmax
Programowanie Proceduralne
25
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
void minmax ( float t [ ] , int n , float ∗ min , float ∗ max )
{
int i = 2 ;
float tmin , tmax ;
if ( n==1 ) {
∗ min=t [ 0 ] ;
∗ max=t [ 0 ] ;
return ;
}
if ( t [0] < t [ 1 ] ) {
∗ min=t [ 0 ] ;
∗ max=t [ 1 ] ;
}
else {
∗ min=t [ 1 ] ;
∗ max=t [ 0 ] ;
}
Programowanie Proceduralne
26
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
while ( i < n−1 ) {
if ( t [ i]<t [ i +1]
tmin=t [ i ] ;
tmax=t [ i + 1 ] ;
}
else {
tmin=t [ i + 1 ] ;
tmax=t [ i ] ;
}
if ( ∗ max<tmax )
if ( ∗ min>tmin )
i = i + 2;
}
if ( i == n−1 ) {
if ( ∗ max<t [ i ] )
if ( ∗ min>t [ i ] )
}
) {
∗ max=tmax ;
∗ min=tmin ;
∗ max=t [ i ] ;
∗ min=t [ i ] ;
minmax2.c
Programowanie Proceduralne
27
Wskaźniki do elementów tablic
p = &t [ 0 ] ;
p
p
p
9
8
7
6
5
4
3
+
p+
+
p
+
p+
p+
2
1
p
p+
p+
p+
t[0] t[1] t[2] t[3] t[4] t[5] t[6] t[7] t[8] t[9]
Programowanie Proceduralne
28
Arytmetyka na wskaźnikach
1
2
3
4
5
6
7
8
9
int
a;
char b ;
int ∗ pa = &a ;
char ∗ pb = &b ;
printf ( " pa
printf ( " pa +1
printf ( " pb
printf ( " pb +1
=
=
=
=
%p
%p
%p
%p
% lu \n" ,
% lu \n" ,
% lu \n" ,
% lu \n" ,
pa
pa+1
pb
pb+1
,
,
,
,
pa ) ;
pa+1) ;
pb ) ;
pb+1) ;
pointer.c
Przykładowy wynik:
pa
pa+1
pb
pb+1
=
=
=
=
0x7fff44cb239c
0x7fff44cb23a0
0x7fff44cb239b
0x7fff44cb239c
140734347551644
140734347551648
140734347551643
140734347551644
Programowanie Proceduralne
29
Tablice a wskaźniki
Tablica jest wskaźnikiem !
i-ty element
adres i-tego elementu
Przykład
1
2
3
4
5
6
7
8
int t [ 5 ] ;
int ∗ wsk ;
t[i]
⇔ *(t+i)
&(t[i]) ⇔
t+i
t
1
t+
2
t+
3
t+
4
t+
t[0] t[1] t[2] t[3] t[4]
wsk
wsk=t ;
∗t = 5 ;
∗ ( t+2) = 6 ;
wsk = wsk + 1 ;
t = t + 1;
A d r e s t a b li c y j e s t s t aly
Źle !
Programowanie Proceduralne
30
Ponownie środek masy 2 punktów
float ∗ srodek ( float p1 [ ] , float p2 [ ] )
{
float sm [ 4 ] ;
z m ie
nna
int i =0;
lok
sm [ 3 ] = p1 [ 3 ] + p2 [ 3 ] ;
al n
a
while ( i<3)
{
sm [ i ]=( p1 [ 3 ] ∗ p1 [ i]+ p2 [ 3 ] ∗ p2 [ i ] ) / sm [ 3 ] ;
i = i + 1;
}
return sm ;
zwra
}
c
any
adres
z m ie n n ej lo
k aln ej
Programowanie Proceduralne
!
e
Źl
31
Ponownie środek masy 2 punktów
• Deklaracja const zmiennej wskaźnikowej - kompilator wykryje
próbę modyfikacji.
• Zmienna sm zawiera adres zmiennej, która zostanie
zmodyfikowana przez funkcję srodek().
void srodek ( const float p1 [ ] , const float p2 [ ] , float sm [ ] )
{
int i =0;
sm [ 3 ] = p1 [ 3 ] + p2 [ 3 ] ;
while ( i<3)
{
sm [ i ]=( p1 [ 3 ] ∗ p1 [ i]+ p2 [ 3 ] ∗ p2 [ i ] ) / sm [ 3 ] ;
i = i + 1;
}
}
Wskaźn
ik
Programowanie Proceduralne
32
Wskaźnik jako wartość zwracana z funkcji
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
float ∗ wczytaj ( float ∗t , int n )
{
float ∗p=t ;
printf ( " Wprowadz %d liczb \n" , n ) ;
while ( n >0 )
{
scanf ( "%f" , t ) ;
t = t + 1;
n = n − 1;
}
return p ;
}
int main ( )
{
float t [ 1 0 0 ] ;
float min , max ;
minmax ( wczytaj ( t , 1 0 ) , &min , &max ) ;
}
Programowanie Proceduralne
33
Rozszyfruj deklaracje
Przykładowe deklaracje funkcji
/∗ M i e j s c a z e r o w e p a r a b o l i ∗/
int pierwiastki ( float a , float b , float c , float ∗x1 , float x2 ) ;
/∗ W y s z u k i w a n i e b i n a r n e ∗/
int szukaj ( const int ∗t , int n , int x ) ;
/∗ S r o d e k masy u k l a d u punktow ∗/
struct punkt srodek ( const struct ogromna_chmura_punktow ∗u ) ;
/∗ Dekompozycja l i c z b y z m i e n n o p o z y c y j n e j ( math . h ) ∗/
float modf ( float num , float ∗i ) ;
/∗ A l o k a c j a p a m i e c i ( s t d l i b . h ) ∗/
void ∗ malloc ( int size ) ;
/∗ K o p i o w a n i e t a b l i c ( s t d l i b . h ) ∗/
void ∗ memcpy ( void ∗ dest , const void ∗ src , int count ) ;
/∗ O t w i e r a n i e p l i k u ( s t d i o . h ) ∗/
FILE ∗ fopen ( const char ∗ filename , const char ∗ mode ) ;
Programowanie Proceduralne
34
Przykład: Wyszukiwanie dominanty
Problem: znajdź wartość występującą najwięcej razy w zbiorze
8 1 -6 3 5 7 4 9 2 10 -4 88 6 3 1 3 332 2
Programowanie Proceduralne
35
Algorytm 3 Wyznaczanie dominanty (mody) - algorytm naiwny
Dane wejściowe: ciąg n elementów {x1 , x2 , . . . , xn }
Wynik: wartość dominanty xmoda oraz ilość wystąpień lmoda . Jeżeli
istnieje więcej niż jedna wartość dominującą to zwracana jest
pierwsza znaleziona.
1: lmoda ← 0
2: dla każdego x ∈ {x1 , . . . , xn } wykonuj
3:
k←0
4:
dla każdego y ∈ {x1 , . . . , xn } wykonuj
5:
jeżeli x = y wykonaj
6:
k ←k+1
7:
jeżeli k > lmoda wykonaj
8:
lmoda ← k
9:
xmoda ← x
10: zwróć xmoda , lmoda
Programowanie Proceduralne
36
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
int dominanta ( const int ∗t , int n , int ∗c )
{
int i , j , k , x ;
∗c = 0 ;
i =0;
while ( i<n )
{
k =0;
j =0;
while ( j<n )
{
if ( t [ i]==t [ j ] ) k = k + 1 ;
j = j + 1;
}
if ( k > ∗c )
{
∗c = k ;
x = t[i];
}
i = i + 1;
}
return x ;
}
dominanta1.c
Programowanie Proceduralne
37
Złożoność algorytmu
• ilość operacji rzędu n2
• jeżeli pewien element został aktualnie zaznaczony jako
dominujący to nie musimy powtarzać dla niego obliczeń
• zliczanie można rozpocząć od i + 1 miejsca, jeżeli wartość
dominująca pojawiła się wcześniej to już została policzona
• jeśli aktualna wartość dominująca ma k wystąpień, to
szukanie możemy przerwać na pozycji n − k w zbiorze
Programowanie Proceduralne
38
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
int dominanta2 ( const int ∗t , int n , int ∗c )
{
int i , j , k , x ;
∗c =0;
x=t [ 0 ] − 1 ;
i =0;
while ( i<n−∗c )
{
if ( t [ i ] ! = x )
{
k =1;
j=i +1;
while ( j<n )
{
if ( t [ i]==t [ j ] ) k = k + 1 ;
j = j + 1;
}
if ( k > ∗c )
{
∗c = k ;
x = t[i];
}
}
i = i + 1;
}
return x ;
}
dominanta2.c
Programowanie Proceduralne
39
Dynamiczny przydział pamięci
Alokacja pamięci
void *malloc(int rozmiar);
Funkcja malloc zwraca adres przydzielonego bloku pamięci lub
wartość 0 (NULL) w przypadku niepowodzenia.
Zwolnienie przydzielonej pamięci
void free(void *wskaznik);
Argumentem funkcji free jest adres uzyskany wcześniej z funkcji
malloc.
Funkcja malloc i free zadeklarowane są w pliku stdlib.h.
Programowanie Proceduralne
40
Przykład
# include< s t d l i b . h>
# include<s t d i o . h>
Sterta
int main ( )
{
float ∗t ;
3
t = malloc ( 4 ∗ sizeof ( int ) ) ;
if ( t == NULL ) {
printf ( " Blad alokacji pamieci .\ n" ) ;
exit ( 1 ) ;
}
∗t = 3 ;
Stos
main()
t
Dane
Kod
free ( t ) ;
adresy pamięci
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
return 0 ;
}
Programowanie Proceduralne
41
Podsumowanie
• Operator referencji (adresowania) &
&a to adres zmiennej a
• Operator dereferencji (wyłuskania) *
*b daje dostęp do wartości wskazywanej przez zmienną b
• Deklaracja zmiennej wskaźnikowej również zawiera znak *
int *wsk;
float* wsk2;
• Tablice to wskaźniki t[i] ⇔ *(t+i)
• Przekazanie wskaźnika do funkcji pozwala na modyfikację
zmiennej wskazywanej
• Uważaj na co wskazujesz !
Programowanie Proceduralne
42
Literatura dodatkowa
Maciej M. Sysło, „Algorytmy”, WSiP, Warszawa, 2002.
David Griffiths, Dawn Griffiths „Rusz głową! C.”, Helion,
Gliwice, 2013.
„Kurs programowania w C”, WikiBooks,
http://pl.wikibooks.org/wiki/C
Programowanie Proceduralne
43