====== 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.
{{wskaznik-tab.png?400}}
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%%++%%''
===== Tablice jako argumenty funkcji =====
Skoro tablice są zmiennymi wskaźnikowymi to w deklaracji funkcji, której argumentem jest tablica można użyć deklaracji odpowiedniej zmiennej wskaźnikowej.
Przykładowo poniższa deklaracja
void funkcja(int tab[])
{
tab[0] = 1;
}
może być zapisana w takiej postaci
void funkcja(int *tab)
{
tab[0] = 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 - Odwracanie kolejności =====
Poniższy program implementuje funkcję ''odwroc'', która odwraca kolejność elementów w tablicy z liczbami rzeczywistymi. W implementacji tej funkcji wykorzystano wskaźniki ''p'' i ''k'' do wskazywania elementów tablicy. Funkcja zwraca adres tablicy, dzięki temu wynik działania tej funkcji może zostać bezpośrednio użyty w argumencie innej funkcji, np.: ''wypisz(odwroc(t, n), n)''
#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=0;
printf("Podaaj %d liczb\n", n);
while( i < n)
{
printf("tab[%d]= ", i);
scanf("%f", t+i);
i++;
}
return t;
}
/* Funkcja wyswietla n pierwszych elementow tablicy t[] */
void wypisz(float *t, int n)
{
int i=0;
for( i=0; i
Wynik działania programu:
Podaj rozmiar tablicy: 5
Podaj 5 liczb
tab[0]= 4.3
tab[1]= 2
tab[2]= 1
tab[3]= 3
tab[4]= 13
0 : 13.000000
1 : 3.000000
2 : 1.000000
3 : 2.000000
4 : 4.300000
===== Zadanie - Dominanta =====
Zaimplementuj program wyznaczający najczęściej pojawiająca się wartość (dominantę) wśród ''n'' podanych liczb całkowitych. Program wczytuje liczy całkowite podane przez użytkownika a następnie wypisuje wartość dominująca oraz liczbę wystąpień tej wartości. W implementacji nie używaj indeksowania tablicy za pomocą nawiasów ''[]'' oraz liczby całkowitej ''t[i]'' lecz użyj w tym celu wskaźników. \\
**Dane wejściowe programu**:
* liczba całkowita ''n'' nie większa od 1000 określająca ilość liczb w sekwencji
* sekwencja ''n'' liczb całkowitych
**Wynik działania**:
* wartość dominująca z podanej sekwencji liczb oraz liczba wystąpień dominanty. W przypadku, gdy występuje więcej niż jedna wartość dominująca, zwróć jedną z nich.
Przykładowe działanie programu:
Ile liczb (nie wiecej niz 1000) ?
10
Podaj 10 liczb calkowitych
3
6
-4
3
2
5
3
10
2
4
Liczba 3 wystepuje 3 razy
===== Zadanie - Tablice to wskaźniki =====
Zaimplementuj dowolne zadanie z poprzednich zajęć związane z użyciem tablic w taki sposób aby zamiast indeksowania tablicy za pomocą liczby całkowitej ''tab[i]'' zastosować wszędzie, gdzie to możliwe wskaźniki w celu manipulowania na elementach tablicy.
Lista zadań z poprzednich zajęć, które można wykorzystać:
* [[zajecia:pp1_2020_1:05a_funkcje_2#zadaniestatystyki|Zad. statystyki]]
* [[zajecia:pp1_2020_1:05a_funkcje_2#zadaniesortowanie_przez_wybieranie|Zad. sortowanie przez wybieranie]]
* [[zajecia:pp1_2020_1:04_tablice#zadaniesito_eratostenesa|Zad. sito Eratostenesa]]