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
.
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<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 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<stdio.h> 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.
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ń()
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
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:
t
zawierająca sekwencję liczb całkowitych, n
określająca ilość liczb w tablicy t
, 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:
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:
n
określająca ilość elementów w sekwencji liczbn
liczb całkowitychWynik działania:
Przykład działania:
Ile liczb? 5 Podaj liczby: 5 -3 5 5 4 Diminanta 5 Ilosc wystapien 3
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:
a
, b
, c
, d
, e
, f
definiujące układ równań x
, y
, do których zostanie wstawiony wynikWartość zwracana z funkcji: liczba całkowita o wartości 0, 1 lub -1:
x
i y
nie są modyfikowanex
i y
nie są modyfikowanex
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:
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