Wstęp do programowania w języku C/Wskaźniki

From Studia Informatyczne

Spis treści

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 \frac{3}{2}n.

/*
 * 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],...,a[i-1]),
     *min = MIN(a[0],...,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],...,a[i-1]),
            *min = MIN(a[0],...,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];
  }
}