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<stdio.h> 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 jednak taki adres umieścić w zmiennej wskaźnikowej i wykorzystać aby zyskać dostęp 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<stdio.h> 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<stdio.h> 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 ustawionego 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 fun, która przyjmuje w argumencie wskaźnik zmiennej (int * a) a następnie odnosząc się przez dany adres podstawią nową wartość.

#include<stdio.h> void fun(int *a) { *a = 42; } int main() { int a = 1; fun( &a ); printf("a = %d\n", a); }

Wynik działania programu:

a = 1
a = 42

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.

Zadanie - 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

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

Zadanie - układ równań

Zaimplementuj funkcję rozwiązującą układ równań z 2 niewiadomymi.

Argumenty funkcji: liczby rzeczywiste a, b, c, d, e, f definiujące układ równań oraz adresy (wskaźniki) zmiennych x, y, do których zostanie wstawiony wynik.
Wartość zwracana z funkcji: liczba całkowita równa:

-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.

Napisz program, który wczyta 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ń.

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

Zadanie - dominanta

Zaimplementuj funkcję o naziw diminanta, która zwróci dominantę oraz liczbę wystąpień dominanty dla podanego ciągu liczb rzeczywistych. Dominanta to wartość najczęściej dominują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.

Argumenty funkcji dominanta: tablica t liczb rzeczywistych, liczba całkowita n określająca ilość liczb w tablicy t, wskaźnik do zmiennej x, która po zakończeniu działania funkcji będzie zawierała wartość dominującą z elementów tablicy t. W przypadku, gdy tablica zawiera więcej niż jedną wartość dominującą zwracana jest tylko jedna z nich.
Wartość zwracana z funkcji: liczba całkowita określająca ilość wystąpień dominanty

Napisz program, który wczyta z klawiatury n liczb rzeczywistych i korzystając z zaimplementowanej funkcji wyznaczy wartość dominującą a następnie wypisze wynik.

Przykład działania:

Ile liczb?
5
Podaj liczby:
5
-3
5
5
4
Diminanata to 5
Ilosc wystapien 3