====== Wskaźniki ======
Wskaźnik (//pointer//) to adres, który określa jednoznacznie pozycję danych w pamięci komputera.
Każda zmienna zajmuje określone miejsce w pamięci i jest umiejscowiona w pamięci pod pewnym adresem.
Tak jak adres na kopercie listu wskazuje jednoznacznie lokalizację adresata tak wskaźnik pokazuje miejsce w pamięci, gdzie możemy odnaleźć wskazywaną zmienną. Adres zmiennej uzyskujemy za pomocą operatora referencji '&'.
**Adres zmiennej i operator ''&''**
Operator referencji ''&'' zwraca adres zmiennej. Jest to operator jednoargumentowy, tzn. działa na jedną wartość stojącą z prawej strony, np.: ''&x'' wyznacza adres zmiennej ''a''.
#include
int main()
{
int a = 42;
printf("adres zmiennej a %p\n", &a);
}
Przykładowy wynik działania programu:
adres zmiennej a 0x7fff8e6a47e4
Adres jest dodatnią liczbą całkowitą, którą wygodnie jest przedstawiać w systemie szesnastkowym.
Jednak zazwyczaj nie ma potrzeby prezentowania tej wartości. Możemy taki adres przypisać do zmiennej wskaźnikowej i korzystać z tej zmiennej gry potrzebujemy dostępu do wskazywanego obszaru pamięci.
**Deklaracja zmiennej wskaźnikowej **
Zmienna wskaźnikowa to zmienna, która przechowuje adres do zmiennej pewnego typu.
W poniższym przykładzie znajduje przykład który zawiera deklarację zmiennej wskaźnikowej ''w'', która będzie przechowywała adres zmiennej typu ''int''. W instrukcji ''w = &a'' następuje przypisanie wartości ''&a'' do zmiennej ''w''.
#include
int main()
{
int a = 42;
int *w;
w = &a;
printf("w = %p\n", w);
printf("adres zmiennej a %p\n", &a);
}
Przykładowy wynik działania programu:
w = 0x7fff0c2c954c
adres zmiennej a 0x7fff0c2c954c
W podobny sposób deklaruje się zmienne wskaźnikowe, które mogą przechowywać adresy zmiennych dowolnego typu a nawet adresy innych zmiennych wskaźnikowych.
Przykłady deklaracji zmiennych wskaźnikowych:
int *a; // wskaźnik zmiennej typu int
float *b; // wskaźnik zmiennej typu float
char *c // wskaźnik zmiennej typu char
int *d[10]; // tablica 10-cio elementowa wskaźników typu int
float **e; // wskaźnik zmiennej typu float* (wskaźnik do wskaźnika)
**Dostęp do adresu - operator ''*''**
Operator dereferencji ''*'' służy do wydobycia wskazanej przez wskaźnik wartości. To również operator jednoargumentowy a instrukcja ''*x'' zwraca wartość wskazywaną, przez adres zawarty w zmiennej ''x'', tzn. zmienna ''x'' musi być zmienną wskaźnikową i zawiera poprawny adres pewnej innej zmiennej. Zwróć uwagę, że symbol ''*'' używany jest również jako operator mnożenia '' x * y '', jednak mnożenie jest operacją na dwóch argumentach, zaś operator dereferencji działa zawsze na jedną wartość.
Operator dereferencji ''*'' udostępnia wartość z danego adresu, dzięki czemu możemy nie tylko odczytać wartość wskazywanej zmiennej ale także zmodyfikować jej wartość. Przykład:
#include
int main()
{
int a = 42;
int *w;
w = &a;
printf("a = %d\n", a);
printf("*w = %d\n", *w);
*w = 13;
printf("a = %d\n", a);
printf("*w = %d\n", *w);
}
Wynik działania programu:
a = 42
*w = 42
a = 13
*w = 13
**Uwaga:** uważaj na to aby zmienna wskaźnikowa zawsze zawierała poprawny adres. Sprawdź, co się stanie jeżeli w powyższym przykładzie usuniemy instrukcję ''w = &a''. Brak przypisania poprawnego adresu w zmiennej ''w'' doprowadzi do katastrofy w momencie wykonania instrukcji ''*w = 13''.
**Wskaźnik argumentem funkcji **
Jednym z najważniejszych zastosowań wskaźników jest ich wykorzystanie w argumentach funkcji.
Adres zmiennej przekazany w argumencie funkcji pozwala tej funkcji zmodyfikować wartość wskazywanej zmiennej, tzn. funkcja jest w stanie podstawić nową wartość do wskazywanej zmiennej.
Poniższy przykład prezentuje definicję funkcji ''zwieksz'', która przyjmuje w argumencie wskaźnik zmiennej (''int * a'') a następnie odnosząc się przez dany adres zwiększa wartość o 1.
#include
void zwieksz(int *a)
{
*a = *a + 1;
}
int main()
{
int a = 1;
zwieksz( &a );
printf("a = %d\n", a);
}
Wynik działania programu:
a = 2
W taki sam sposób działa funkcja ''scanf("%d", &x)'', która w drugim argumencie MUSI mieć adres zmiennej ''x'' do której podstawi wartość wczytaną z terminala.
===== Ćwiczenie - zamiana wartości =====
Napisz funkcję ''zamień()'', która zamienia wartości 2 zmiennych podanych w argumentach.\\
Przetestuj działanie funkcji w programie, który wczyta 2 liczby do zmienneych a nastepnie zamieni ich wartości korzystając z funkcji ''zamień()''
===== Ćwiczenie - pierwiastki równania kwadratowego =====
Zaimplementuj funkcję o nazwie ''**pierwiastki**'', która wyznacza miejsca zerowe równania kwadratowego. Parabola jest określona przez trzy wartości rzeczywiste ''a'', ''b'' i ''c'' równaniem
\[ f(x) = ax^2 + bx + c \]
Parabola może posiadać dwa miejsca zerowe, jedno miejsce zerowe lub może nie posiadać miejsc zerowych. Zadana funkcja ''parabola'' zwraca informację o liczbie miejsc zerowych (0, 1 lub 2) oraz dwie wartości rzeczywiste ''x1'' oraz ''x2'' stanowiące miejsca zerowe. \\
**Argumenty funkcji pierwiastki**: liczby rzeczywiste ''a'', ''b'', ''c'' definiujące równanie kwadratowe oraz dwa adresy (wskaźniki) ''x1'' oraz ''x2'', pod które zostaną wstawione wartości obu miejsc zerowych. W przypadku braku miejsc zerowych, zmienne wskazywane przez wskaźniki nie są modyfikowane. \\
**Wartość zwracana funkcji**: liczba całkowita (0, 1 lub 2) określająca liczbę miejsc zerowych równania kwadratowego
Napisz program, który pobierze od użytkownika 3 liczby rzeczywiste ''a'', ''b'' oraz ''c'' a następnie, korzystając z funkcji ''pierwiastk()'' wyznaczy miejsca zerowe równania kwadratowego zdefiniowanego podanymi współczynnikami. Wynikiem działania programu jest komunikat informujący o liczbie miejsc zerowych oraz wartości tych miejsc zerowych.
**Przykład działania programu**
Podaj wsp. paraboli a, b i c :
1 1 1
Brak miejsc zerowych
Podaj wsp. paraboli a, b i c :
1 -4 4
Jedno miejsce zerowe: 2.000000
Podaj wsp. paraboli a, b i c :
1 0 -4
Dwa miejsca zerowe: x1=2.000000, x2=-2.000000
===== Ćwiczenie - dominanta =====
Zaimplementuj funkcję o nazwie **diminanta()**, która wyznacza wartość dominującą (modę) oraz liczbę wystąpień dominanty dla podanego ciągu liczb całkowitych. Dominanta to wartość najczęściej występująca, np. dla ciągu liczb 1, 2, 1, 3, 1, 5 wartością dominującą jest 1 i występuje ona w ciągu 3 razy. Inny przykład, w ciągu 1, 2, 3, 4 każda z wartości występuje równie często, więc każda jest dominantą.
**Argumenty funkcji dominanta**:
* tablica ''t'' zawierająca sekwencję liczb całkowitych,
* liczba całkowita ''n'' określająca ilość liczb w tablicy ''t'',
* wskaźnik do zmiennej ''x''. Wskazywana zmienna po zakończeniu działania funkcji będzie zawierała wartość dominującą w sekwencji liczb z tablicy ''t''. W przypadku, gdy tablica zawiera więcej niż jedną wartość dominującą to zwracana jest tylko jedna (dowolna) z nich.\\
**Wartość zwracana z funkcji**:
* liczba całkowita określająca ilość wystąpień dominanty\\
Napisz program, który korzystając z funkcji ''dominanta()'' wyznaczy wartość dominującą z podanej przez użytkownika sekwencji liczb. Zakładamy, że program powinien działać poprawnie dla sekwencji liczb zawierającej do 1000 elementów.
**Dane wejściowe programu:**
* liczba całkowita ''n'' określająca ilość elementów w sekwencji liczb
* sekwencja ''n'' liczb całkowitych
**Wynik działania:**
* program wypisuje komunikat o znalezionej wartości dominującej oraz o liczbie wystąpień.
Przykład działania:
Ile liczb?
5
Podaj liczby:
5
-3
5
5
4
Diminanta 5
Ilosc wystapien 3
===== Zadanie - Układ równań =====
Zaimplementuj funkcję rozwiązującą układ równań z 2 niewiadomymi.
\[
\begin{cases}
ax + by = c\\
dx + ey = f\\
\end{cases}
\]
**Argumenty funkcji**: funkcja posiada 8 argumentów:
* liczby rzeczywiste ''a'', ''b'', ''c'', ''d'', ''e'', ''f'' definiujące układ równań
* adresy (wskaźniki) zmiennych ''x'', ''y'', do których zostanie wstawiony wynik
**Wartość zwracana z funkcji**: liczba całkowita o wartości 0, 1 lub -1:
* -1 gdy układ jest sprzeczny (brak rozwiązań), zmienne ''x'' i ''y'' nie są modyfikowane
* 0 gdy układ jest niejednoznaczny (posiada wiele rozwiązań), zmienne ''x'' i ''y'' nie są modyfikowane
* 1 gdy istnieje jednoznaczne rozwiązanie, do zmiennych wskazywanych przez ''x'' i ''y'' umieszczane jest rozwiązanie układu.
Funkcja rozwiązuje układ równań metodą wyznaczników\\
**Metoda wyznaczników**\\
Oblicz wyznaczniki
$$W = a \cdot e - b \cdot d \qquad W_x = c \cdot e - f \cdot b \qquad W_y = a \cdot f - c \cdot d $$
Rozwiązanie:
* gdy $W \neq 0$ to istnieje rozwiązanie $ x = \frac{W_x}{W}, \, y = \frac{W_y}{W}$
* gdy $W = W_x = W_y = 0$ to istnieje nieskończenie wiele rozwiązań (równania zależne)
* gdy $W = 0$ i $W_x \neq 0$ lub $W_y \neq 0$ to brak rozwiązań, układ sprzeczny
----
Napisz program, który wykorzysta funkcję zdefiniowaną wg. powyższej specyfikacji do rozwiązania dowolnego układu równań.
Program wczytuje 6 liczb rzeczywistych stanowiących współczynniki określające układ równań a następnie, korzystając z powyższej funkcji, wyznaczy rozwiązanie układu równań
i wypisuje wynik na ekranie\\
**Przykład działania:**
Podaj wspolczynniki ukladu rownan:
3 5 17 2 -3 5
Rozwiazanie x=4.000000, y=1.000000
Podaj wspolczynniki ukladu rownan:
-1 -1 3 1 1 -3
Uklad rownan jest nioznaczony
Podaj wspolczynniki ukladu rownan:
1 2 3 1 2 4
Uklad rownan jest sprzeczny
Rozwiązanie (plik źródłowy) umieść w Moodle pod adresem https://moodle.umk.pl/WFAIIS/mod/assign/view.php?id=6320