====== Wskaźniki i tablice ======
===== Arytmetyka na wskaźnikach =====
Wskaźniki można wykorzystać do pokazywania elementów tablicy. Kolejne elementy tablicy umieszczone są w pamięci pod kolejnymi adresami, dzięki czemu zwiększając wartość zmiennej wskaźnikowej możemy przemieszczać się do kolejnych elementów tablicy. Obrazuje to poniższa grafika, gdzie kolejne elementy tablicy ''t'' wskazywane są przez zmienną wskaźnikową ''p'', która ustawiona jest na adres początku tablicy.
{{zajecia:pp1_2020_1:wskaznik-tab.png?600}}
Poniższy przykład demonstruje użycie zmiennej wskaźnikowej ''int *p'' do wskazywania kolejnych elementów tablicy ''t''.
Uruchomienie programu spowoduje wypełnienie tablicy ''t'' wartością 42 i wypisanie wyniku.
#include
int main()
{
int t[10];
int *p; // deklaracja zmiennej wskaznikowej p
int i=0;
p = &(t[0]); // ustawienie wskaznika p na poczatek tablicy t
while( i < 10)
{
*p = 42; // wstawiamy 42 w miejsce wskazywane przez p
p++; // wskaznik przesuwamy na kolejny element
i++;
}
for(i=0; i<10; i++)
printf("%d\n", t[i]);
}
Analogicznie, jeżeli zmienna wskaźnikowa będzie zmniejszana ''p=p-1'' zaowocuje to ustawieniem wskaźnika na podrzeni element tablicy.
Mając zaś dwie zmienne wskaźnikowe ''p1'' i ''p2'' ustawione na elementy tej samej tablicy to możliwe jest również porównywanie tych wskaźników.
Demonstruje to poniższy przykład, w którym zadeklarowane są dwie zmienne wskaźnikowe ''int *p'' oraz ''int *k'', które ustawione są odpowiednio na początek i na koniec tablicy. Wskaźnik ''p'' wędruje po kolejnych elementach tablicy aż do momentu spotkania się ze wskaźnikiem ''k''. Zwróć uwagę, że do przeglądania elementów tablicy nie jest wykorzystywana zmienna całkowita ''i'' do indeksowania tablicy.
#include
int main()
{
int t[10];
int *p, *k; // deklaracja zmiennej wskaznikowej p
p = &(t[0]); // ustawienie wskaznika p na poczatek tablicy t
k = &(t[10]); // ustawienie wskaznika k na koniec tablicy t
while( p < k) // dopoki adres p jest mniejszy od adresu k
{
*p = 42; // wstawiamy 42 w miejsce wskazywane przez p
p++; // wskaznik przesuwamy na kolejny element
}
for(p=&(t[0]); p
Podsumowując, na wskaźnikach dozwolone są operacje:
* dodawania i odejmowania liczby całkowitej do wartości wskaźnika:, np: ''p+1'', ''p+n'', ''p-10'', odpowiada to zwiększeniu lub zmniejszeniu adresu wskaźnika a w efekcie wskazanie kolejnego elementu tablicy
* zwiększanie lub zmniejszanie wartości wskaźnika o wartość całkowitą, np: ''p=p+1'', ''p=p-100'', ''p++''
* porównywanie wartości dwóch wskaźników pod warunkiem, że pokazują na elementy tej samej tablicy, np: ''p
#include
int main()
{
int t[10];
int i=0;
while( i < 10)
{
*(t+i) = 42; // zapis *(t+i) znaczy to samo co t[i]
i++;
}
for(i=0; i<10; i++)
printf("%d\n", *(t+i));
}
Podsumowując:
* **Tablica jest zmienną wskaźnikową**
* Zmienna tablicowa zawiera adres początku tablicy, tzn. ''tab'' to to samo co ''&(tab[0])''
* Adres ''i''-tego elementu tablicy ''&(tab[i])'' to ''tab+i''
* Dostęp do ''i''-tego elementu tablicy ''tab[i]'' to ''*(tab+i)''
* Adresu przypisanego do zmiennej tablicowej nie można modyfikować, tzn. niedozwolona jest operacja ''tab%%++%%''
===== Ćwiczenie: odwracanie kolejności liczb =====
Napisz program, który odwróci kolejność liczb umieszczonych w tablicy. Wszystkie operacje na elementach tablicy zaimplementuj stosując wskaźniki. Nie stosuj indeksowania tablic za pomocą liczb całkowitych typu ''tablica[i]''.\\
**Dane wejściowe programu**: liczba całkowita ''n'' określająca długość sekwencji liczb oraz sekwencja ''n'' liczb rzeczywistych\\
**Wynik programu**: sekwencja liczb w odwrotnej kolejności.
Przykładowy wynik działania programu:
Podaj rozmiar tablicy: 5
Podaj 5 liczb
4.3
2
1
3
13
Liczby w odwotnej kolejnosci:
13.000000
3.000000
1.000000
2.000000
4.300000
===== Tablice jako argumenty funkcji =====
Zmienna tablicowa będąca argumentem funkcji również jest zmienną wskaźnikową. Wartością argumentu jest adres początkowego elementu tablicy. Argument funkcji, który jest tablicą (np. ''float tab[]''), często deklaruje się wprost w postaci zmiennej wskaźnikowej (''float *tab'').
Przykładowo poniższa deklaracja
void funkcja(int tab[])
{
tab[0] = 1;
}
jest równoważna temu
void funkcja(int *tab)
{
tab[0] = 1;
}
oraz temu
void funkcja(int *tab)
{
*tab = 1;
}
Skoro tablice są wskaźnikami to wyjaśnia dlaczego funkcje mogą modyfikować ich zawartość, gdy przekazane są w argumentach funkcji. Zmienna lokalna wewnątrz funkcji nie zawiera kopii tablicy przekazanej w argumencie lecz jedynie kopię adresu tablicy (wskaźnik początku tablicy) a dzięki temu adresowi ma dostęp do oryginalnej tablicy przekazanej do funkcji.
Adresy (wskaźniki) mogą też być wartościami zwracanymi z funkcji za pomocą instrukcji ''return''.
===== Przykład: funkcja wczytująca zawartość tablicy =====
#include
#define MAX 100
/* Funkcja wczytuje n liczb rzeczywistych do tablicy t[].
* Funkcja zwraca adres tablicy t[]*/
float* wczytaj(float *t, int n)
{
int i;
printf("Podaaj %d liczb\n", n);
for(i=0; i < n; i++)
{
printf("tab[%d]= ", i);
scanf("%f", t+i);
}
return t;
}
/* Funkcja wyswietla n pierwszych elementow tablicy t[] */
void wypisz(float *t, int n)
{
int i;
for(i=0; i
===== Ćwiczenie - Odwracanie kolejności =====
Dopisz do powyższego przykładu dodaj funkcję o nazwie ''odwroc()'' realizującą odwracanie elementów w tablicy w taki sposób aby poniższa instrukcja była poprawna i powodowała wypisanie sekwencji ''n'' liczb w odwrotnej kolejności.
wypisz(odwroc(wczytaj(tablica, n), n));
**Argumenty funkcji odwroc**
* tablica liczb rzeczywistych
* liczba całkowita ''n'' określająca ilość elementów w tablicy
**Wartość zwracana**: funkcja zwraca adres tablicy, w której elementy zostały odwrócone (adres tablicy danej w pierwszym argumencie)
===== Ćwiczenie - Sortowanie przez wybieranie =====
Zaimplementuj funkcję o nazwie ''maks'', która zwraca adres elementu tablicy o największej wartości spośród ''n'' liczb rzeczywistych.
**Argumenty funkcji**
* tablica liczb rzeczywistych
* liczba całkowita ''n'' określająca ilość elementów w tablicy
**Wartość zwracana**: adres tego elementu tablicy, który ma największa wartość
Zaimplementuj program, który wykorzysta funkcję ''maks'' do posortowania ciągu liczb rzeczywistych w kolejności od największej do najmniejszej za pomocą algorytmu przez wybieranie:
Algorytm sortowania: dla ''i=0, 1, 2, .., n-1''
- wyszukaj maksymalną wartość z tablicy spośród elementów od ''i'' do końca tablicy
- zamień wartość minimalną, z elementem na pozycji ''i''
===== Zadanie - Norma wektora =====
Zaimplementuj program, który przeprowadza normalizację wektora $n$-wymiarowego zgodnie z poniższą specyfikacją.
Zdefiniuj dwie funkcje, pierwsza o nazwie ''norma()'' a druga o nazwie ''unormuj()''.
Funkcja ''norma'' wyznacza normę (długość) wektora określonego przez $n$ współrzędnych.\\
**Argumenty funkcji**: funkcja ''norma'' posiada 2 argumenty:
* tablica liczb rzeczywistych zawierająca współrzędne wektora $\vec{x}$
* liczba całkowita określająca ilość współrzędnych wektora
**Wartość zwracana**: liczba rzeczywista równa normie wektora.
Normę wektora $\vec{x}$ wyznaczamy ze wzoru:
$$ ||\vec{x}|| = \sqrt{x_1^2 + x_2^2 + \ldots + x_n^2} $$
Funkcja ''unormuj'' zamienia wektor $\vec{x}$ na wektor jednostkowy (unormowany). \\
**Argumenty funkcji**: funkcja ''unormuj'' posiada 2 argumenty:
* tablica liczb rzeczywistych zawierająca współrzędne wektora $\vec{x}$
* liczba całkowita określająca ilość współrzędnych wektora
**Wartość zwracana**: adres początku tablicy zawierającej współrzędne unormowanego wektora (adres tablicy danej w pierwszym argumencie)
Wektor jednostkowy uzyskujemy poprzez podzielenie współrzędnych $x_i$ przez wartość normy
$$\hat{x}_i = \frac{x_i}{\sqrt{x_1^2 + x_2^2 + \ldots + x_n^2}} \qquad \text{dla} \qquad i=1, 2, \ldots, n $$
Napisz program, który wykorzysta funkcje zdefiniowane w tym zadaniu do wyznaczenia współrzędnych unormowanego wektora $n$-wymiarowego. Współrzędne wektora przechowywane są w tablicy. **Wszystkie operacje na elementach tablicy wykonaj posługując się wskaźnikami, NIE stosuj indeksowania tablic liczbami całkowitymi typu ''tablica[i]''**.
Program powinien umożliwiać normalizację wektorów nawet do 100 współrzędnych.
**Dane wejściowe programu**
* liczba całkowita ''n'' określająca ilość współrzędnych wektora.
* sekwencja ''n'' liczb rzeczywistych - kolejne współrzędne wektora
**Wynik programu**: wartość normy wprowadzonego wektora oraz współrzędne tego wektora po unormowaniu
**Przykład**: dla następujących danych wejściowych
n=3
Podaj wspolrzedne wektora
1.0
2.5
-4
program wypisze wynik
Norma wektora:
4.821825
Wektor jednostkowy:
0.207390
0.518476
-0.829561