Wstęp do programowania w języku C/Wskaźniki: Różnice pomiędzy wersjami
Nie podano opisu zmian |
m Zastępowanie tekstu – „,...,” na „,\ldots,” |
||
(Nie pokazano 3 wersji utworzonych przez jednego użytkownika) | |||
Linia 3: | Linia 3: | ||
bytów. | bytów. | ||
Każda zmienna w programie zajmuje jeden lub więcej bytów w pamięci. | Każda zmienna w programie zajmuje jeden lub więcej bytów w pamięci. | ||
Adres (indeks) pierwszego z tych bytów jest | |||
* Adres (indeks) pierwszego z tych bytów jest ''adresem'' zmiennej. | |||
* ''Wskaźnik'' (ang. pointer) to adres w pamięci. | |||
* ''Zmienna wskaźnikowa'' to zmienna, na której przechowuje się adresy. | |||
Oto przykłady deklaracji zmiennych wskaźnikowych. | Oto przykłady deklaracji zmiennych wskaźnikowych. | ||
<Source> | <Source> | ||
Linia 12: | Linia 14: | ||
double *p_d; | double *p_d; | ||
</Source> | </Source> | ||
Wartościami zmiennej | Wartościami zmiennej p_i są wskaźniki (adresy) do obiektów typu int. | ||
Wartościami zmiennej | Wartościami zmiennej p_c są wskaźniki (adresy) do obiektów typu char. | ||
Wartościami zmiennej | Wartościami zmiennej p_d są wskaźniki (adresy) do obiektów typu double. | ||
Podstawowymi operatorami wykorzystywanymi w działaniach na zmiennych | Podstawowymi operatorami wykorzystywanymi w działaniach na zmiennych | ||
wskaźnikowych są | wskaźnikowych są ''operator pobrania adresu'' & i odwołania do | ||
zmiennej wskazywanej | zmiennej wskazywanej *. Jeśli ''x'' jest zmienną, to ''&x'' jest adresem | ||
tej zmiennej. Operator | tej zmiennej. Operator * umożliwia dostęp do obiektu wskazywanego | ||
przez wskaźnik. | przez wskaźnik. | ||
<Source> | <Source> | ||
Linia 31: | Linia 33: | ||
*p = 6; /* wartoscia i jest teraz 6 */ | *p = 6; /* wartoscia i jest teraz 6 */ | ||
</Source> | </Source> | ||
Tak długo jak p wskazuje na i, | Tak długo jak p wskazuje na i, *p jest synonimem i. | ||
Operator | |||
Operator * jest operatorem odwrotnym" do &, tzn.: | |||
j = *&i; | |||
jest równowżne | jest równowżne | ||
j = i}; | |||
Ostrzeżenie: | |||
Nigdy nie stosuj operatora | Ostrzeżenie: | ||
w szczególności nie wykonuj przypisania | Nigdy nie stosuj operatora * do nie zainicjowanej zmiennej wskaźnikowej, | ||
w szczególności nie wykonuj przypisania *p = ...; jeżeli ''p'' nie | |||
jest zainicjalizowana. | jest zainicjalizowana. | ||
==Przypisywanie wskaźników== | ==Przypisywanie wskaźników== | ||
<Source> | <Source> | ||
int i, j, *p, *q; | int i, j, *p, *q; | ||
p = &i; | |||
q = p; /* obie zmienne p i q wskazuja na i */ | |||
*p = 1; | |||
printf("%d\n",*q); /* wypisuje wartosc zmiennej i, czyli 1 */ | |||
*q = 2; | |||
printf("%d\n",*p); /* wypisuje wartosc zmiennej i, czyli 2 */ | |||
</Source> | </Source> | ||
Przypisanie | Przypisanie ''q = p'' nie jest tym samym, co ''*q = *p''. | ||
<Source> | <Source> | ||
p = &i; | |||
q = &j; | |||
i = 1; | |||
/* p i q wskazuja na rozne zmienne */ | |||
*q = *p; | |||
/* wartoscia j jest teraz 1 */ | |||
</Source> | </Source> | ||
Linia 116: | Linia 119: | ||
jest porównywany z dotychczasowym maksimum lub minimum, i w razie | jest porównywany z dotychczasowym maksimum lub minimum, i w razie | ||
potrzeby jedna z tych wartości jest aktualizowana. W tym algorytmie, | potrzeby jedna z tych wartości jest aktualizowana. W tym algorytmie, | ||
w pesymistycznym przypadku wykonuje się | w pesymistycznym przypadku wykonuje się ''2n-1'' porównań między | ||
elementami ciągu wejściowego. W algorytmie drugim zawsze bierzemy | elementami ciągu wejściowego. W algorytmie drugim zawsze bierzemy | ||
dwa kolejne elementy i porównujemy je ze sobą. Następnie większy | dwa kolejne elementy i porównujemy je ze sobą. Następnie większy | ||
z nich porównujemy z dotychczasowym maksimum, a mniejszy z dotychczasowym | z nich porównujemy z dotychczasowym maksimum, a mniejszy z dotychczasowym | ||
minimum, aktualizując je, gdy jest taka potrzeba. Ta prosta sztuczka | minimum, aktualizując je, gdy jest taka potrzeba. Ta prosta sztuczka | ||
pozwala na zredukowanie liczby porównań do co najwyżej | pozwala na zredukowanie liczby porównań do co najwyżej <math>\frac{3}{2}n</math>. | ||
<Source> | <Source> | ||
/* | /* | ||
* Nazwa programu: maxmin1.c | * Nazwa programu: maxmin1.c | ||
*/ | */ | ||
Linia 178: | Linia 173: | ||
for ( i = 1; i < n; i++) | for ( i = 1; i < n; i++) | ||
/* Nzm.: | /* Nzm.: | ||
*max = MAX(a[0], | *max = MAX(a[0],\ldots,a[i-1]), | ||
*min = MIN(a[0], | *min = MIN(a[0],\ldots,a[i-1]). | ||
*/ | */ | ||
if ( a[i] > *max ) | if ( a[i] > *max ) | ||
Linia 190: | Linia 185: | ||
/* | /* | ||
* Nazwa programu: maxmin2.c | * Nazwa programu: maxmin2.c | ||
*/ | */ | ||
Linia 253: | Linia 240: | ||
for ( i = 2; i < n-1; i += 2){ | for ( i = 2; i < n-1; i += 2){ | ||
/* Nzm.: | /* Nzm.: | ||
*max = MAX(a[0], | *max = MAX(a[0],\ldots,a[i-1]), | ||
*min = MIN(a[0], | *min = MIN(a[0],\ldots,a[i-1]). | ||
*/ | */ | ||
Aktualna wersja na dzień 21:57, 15 wrz 2023
Wprowadzenie
Z punktu widzenia programisty pamięć komputera to jednowymiarowa tablica bytów. Każda zmienna w programie zajmuje jeden lub więcej bytów w pamięci.
- Adres (indeks) pierwszego z tych bytów jest adresem zmiennej.
- Wskaźnik (ang. pointer) to adres w pamięci.
- Zmienna wskaźnikowa to zmienna, na której przechowuje się adresy.
Oto przykłady deklaracji zmiennych wskaźnikowych.
int *p_i;
char *p_c;
double *p_d;
Wartościami zmiennej p_i są wskaźniki (adresy) do obiektów typu int. Wartościami zmiennej p_c są wskaźniki (adresy) do obiektów typu char. Wartościami zmiennej p_d są wskaźniki (adresy) do obiektów typu double.
Podstawowymi operatorami wykorzystywanymi w działaniach na zmiennych wskaźnikowych są operator pobrania adresu & i odwołania do zmiennej wskazywanej *. Jeśli x jest zmienną, to &x jest adresem tej zmiennej. Operator * umożliwia dostęp do obiektu wskazywanego przez wskaźnik.
int i, *p; /* p na nic nie wskazuje */
...
p = &i; /* p wskazuje na i*/
i = 5;
printf("%d\n", *p);
/* zostanie wypisana wartosc zmiennej
wskazywanej przez p, czyli 5 */
*p = 6; /* wartoscia i jest teraz 6 */
Tak długo jak p wskazuje na i, *p jest synonimem i.
Operator * jest operatorem odwrotnym" do &, tzn.:
j = *&i;
jest równowżne
j = i};
Ostrzeżenie: Nigdy nie stosuj operatora * do nie zainicjowanej zmiennej wskaźnikowej, w szczególności nie wykonuj przypisania *p = ...; jeżeli p nie jest zainicjalizowana.
Przypisywanie wskaźników
int i, j, *p, *q;
p = &i;
q = p; /* obie zmienne p i q wskazuja na i */
*p = 1;
printf("%d\n",*q); /* wypisuje wartosc zmiennej i, czyli 1 */
*q = 2;
printf("%d\n",*p); /* wypisuje wartosc zmiennej i, czyli 2 */
Przypisanie q = p nie jest tym samym, co *q = *p.
p = &i;
q = &j;
i = 1;
/* p i q wskazuja na rozne zmienne */
*q = *p;
/* wartoscia j jest teraz 1 */
Zastosowanie wskaźników do przekazywania parametrów
Przekazywanie wskaźników do zmiennych jako parametrów funkcji umożliwia zmienianie wartości tych zmiennych podczas wykonywania funkcji. Oto przykład funkcji dekomponującą liczbę rzeczywistą na część całkowitą i ułamkową.
void dekom(float x, int *czesc_calk, float *czesc_ulamk)
{
*czesc_calk = (int) x;
*czesc_ulamk = x - *czesc_calk;
}
Prototypem funkcji dekomp może być zarówno
void dekom(float x, int *czesc_calk, float *czesc_ulamk);
jak i
void dekom(float, int *, float *);
Oto przykład wywołania funkcji dekom.
...
int i;
float f;
...
dekom(3.14, &i, &f);
/* wartoscia i jest 3,
wartoscia f jest .14 */
Wskaźniki można zwracać jako wartości funkcji:
int *max(int *a, int *b)
{
if (*a > *b)
return a;
else
return b;
}
Oto przykładowe użycie funkcji max:
int *p, x, y;
...
p = max(&x,&y);
Jednoczesne znajdowanie maksimum i minimum w ciągu.
Przedstawimy dwa algorytmy znajdowania maksimum i minimum w ciągu liczb. W pierwszym algorytmie wejściowy ciąg jest przeglądany od lewej do prawej, po jednym elemencie. Aktualnie oglądany element jest porównywany z dotychczasowym maksimum lub minimum, i w razie potrzeby jedna z tych wartości jest aktualizowana. W tym algorytmie, w pesymistycznym przypadku wykonuje się 2n-1 porównań między elementami ciągu wejściowego. W algorytmie drugim zawsze bierzemy dwa kolejne elementy i porównujemy je ze sobą. Następnie większy z nich porównujemy z dotychczasowym maksimum, a mniejszy z dotychczasowym minimum, aktualizując je, gdy jest taka potrzeba. Ta prosta sztuczka pozwala na zredukowanie liczby porównań do co najwyżej .
/*
* Nazwa programu: maxmin1.c
*/
#include <stdio.h>
#define Max_d 100
/* maksymalna dlugosc ciagu wejsciowego */
void max_min(int a[], int n, int *max, int *min);
/* znajduje max i min w tablicy
a[0..n-1] i ich wartosci
zwraca na zmiennych *max i *min
*/
main()
{
int d, najw, najm;
/* d - dlugosc wejsciowego ciagu
najw, najm - najwieksza i najmniejsza
wartosc w ciagu wejsciowym.
*/
int ciag[Max_d]; /* ciag wejsciowy*/
int i;
printf("Dlugosc ciagu wejsciowego (0<.<%d) : ",Max_d);
scanf("%d", &d);
for (i = 0; i < d; i++)
scanf("%d", &ciag[i]);
max_min(ciag, d, &najw, &najm);
printf("Max = %d, Min = %d\n", najw, najm);
return 0;
}
void max_min(int a[], int n, int *max, int *min)
{
int i;
*max = a[0];
*min = a[0];
for ( i = 1; i < n; i++)
/* Nzm.:
*max = MAX(a[0],\ldots,a[i-1]),
*min = MIN(a[0],\ldots,a[i-1]).
*/
if ( a[i] > *max )
*max = a[i];
else if ( a[i] < *min )
*min = a[i];
}
/*
* Nazwa programu: maxmin2.c
*/
#include <stdio.h>
#define Max_d 100
/* maksymalna dlugosc ciagu wejsciowego */
void max_min(int a[], int n, int *max, int *min);
/* znajduje max i min
w tablicy a[0..n-1] i ich wartosci
zwraca na zmiennych *max i *min
*/
main()
{
int d, najw, najm;
/* d - dlugosc wejsciowego ciagu
najw, najm - najwieksza i najmniejsza
wartosc w ciagu wejsciowym.
*/
int ciag[Max_d]; /* ciag wejsciowy*/
int i;
printf("Dlugosc ciagu wejsciowego (0<.<%d) : ",Max_d);
scanf("%d", &d);
for (i = 0; i < d; i++)
scanf("%d", &ciag[i]);
max_min(ciag, d, &najw, &najm);
printf("Max = %d, Min = %d\n", najw, najm);
return 0;
}
void max_min(int a[], int n, int *max, int *min)
{
int i, l_min, l_max;
if ( n == 1 ){
*max = a[0];
*min = a[0];
}
else{
if ( a[0] < a[1] ){
*min = a[0];
*max = a[1];
}
else{
*min = a[1];
*max = a[0];
}
for ( i = 2; i < n-1; i += 2){
/* Nzm.:
*max = MAX(a[0],\ldots,a[i-1]),
*min = MIN(a[0],\ldots,a[i-1]).
*/
if ( a[i] < a[i+1] ){
l_min = a[i];
l_max = a[i+1];
}
else{
l_min = a[i+1];
l_max = a[i];
}
if ( *max < l_max )
*max = l_max;
if ( *min > l_min )
*min = l_min;
}
/* n jest nieparzyste, nalezy jeszcze sprawdzic
ostatni element
*/
if ( i == (n - 1) )
if ( *max < a[i] )
*max = a[i];
else if ( *min > a[i] )
*min = a[i];
}
}