wskaznikologia.c

/*
 * plik ma na celu prezentację ciekawych zagadnień z programowania w języku C / C++
 * prezentuje metody bazujące na wykorzystaniu wskaźników
 *
 * czytanie należy zaczęć od funkcji main
 * a w miarę kolejnych wywołań wczytywać się w wcześniej zdeklarowane procedury
 *
 */

#include <stdio.h>

/* ta funkcja nic nie zwraca (void), natomiast odbiera wskaźnik do zmiennej i operuje na niej
   w odrożnieniu od funkcji przyjmującej po prostu zmienna x zmiana wartości tej zmiennej
   dokonywana w tej funkcji będzie widoczna po zakończaniu wykonywania tej funkcji */
void funkcja_1 ( int *x ) {
  *x=(*x) + 1;  /* pod wartosc pod wskaznikiem x podstawiam watrosc z pod wskaznika x powiększoną o 1 */
}

/* funkcja ta przyjmuje tablice i modyfikuje jej drugi element */
void funkcja_2 ( int tab_x[] ) {
  tab_x[2] = 55;

  /* moglibysmy rowniez zwracać (przez return) wskażnik do tablicy
     nie byłoby natomiast sensu zwracać w funkcji wskaźnika do zmiennej zadeklarowanej w niej
     gdyż po zakończeniu funkcji pamieć w niej rezerwowana jest zwalniana
     i zwracalibyśmy adres do pamieci w której mogą być już zupelnie inne dane */
}


/* funkcja pomocznicza - dodaje dwie liczby i konweruje zwracana wartosc do double */
double suma (int a, int b) {
  return (double)(a+b);
}

/* zmienna globalna która zostanie zainicjalizowana przed uruchomieniem main (o tym mówi static) */
static double mnoznik = 3;
// uwaga: zmienne globalne są alokowane w obszarze pamięci procesu a nie na stosie
//        jak ma to miejsce ze zmiennymi lokalnymi

/* ta funkcja przyjmuje jako argument wskaźnik do funkcji przyjmującej dwa argumenty int i zwracającej double */
typedef double (*pomocnicza)(int, int);
double funkcja_3 (double (*f)(int, int)) {
  /* możemy definiować także po prostu wskażniki do funkcji,
     co więcej możemy do tego wykorzystać definiowane samodzielnie typy */
  pomocnicza f2 = f;
  return mnoznik * f(2, 5) + f2(1,1);
}

/* przykładowa strukturka ... */
typedef struct struct_dane dane;
struct struct_dane {
  int liczba;
  char tablica[2];
};

/* funkcja przyjmująca wskaźnik do struktury */
void funkcja_4 (dane *a) {
  /* to jest to samo co: double funkcja_3 (struct struct_dane *a) i wtedy nie potzrzebujemy typedef */
  printf( "mamy dwa sposoby odwolania sie do elementów struktury przekaznej przez wskaźnik:");
  printf( " %d == %d\n", a->liczba, (*a).liczba );
  printf( "i dwa sposoby odwolania sie do adresów elementów tej struktury:");
  printf( " %p == %p\n", &(a->liczba), &((*a).liczba) );
}



/* funkcja startowa przyjmujaca argumenty wywołania programu (tablica argv)
   ich liczbę (argc) a także zmienne środowiskowe (tablica envp)
   oraz zwracającą kod powrotu 0 - sukces, inne - porażka */
int main (int argc, char *argv[], char *envp[]) {

printf("\nWSKAZNIKI\n\n");

  int a=5; /* zmienna całkowita o wartości 5 */
  int *wsk=0; /* wartością wsk jest adres w pamięci ... (w tym wypadku wskazuje na adres zmiennej typu integer),
                 jeżeli void *.... to nie określamy (nie wiemy jeszcze na jakiego typ danych on wskazuje) */

  wsk=&a;
    /* do naszego wskaźnika wpisujemy adres (& oznacza operację pobrania adresu zmiennej) */

  printf("Wartosc a wynosi: %d \n", a);
    /* wypisywanie - zobacz: man 3 printf */
  printf("Wartosc pod wskaznikiem: %d \n", *wsk);
    /* * oznacza operator wyluskania, pobiera wartość zmiennej z pod podanego adresu */
  printf("Adres zmiennej a %p \n", wsk);

  // STAŁOŚĆ WSKAŹNIKÓW
  const int* wskaznik_na_stala=wsk;
  // takiemu wskaźnikowi możemy zmienić zmienną na którą pokazuje,
  // ale nie możemy zmienić wartości tej zmiennej
  
  int* const wskaznik_o_stalym_adresie=wsk;
  // takiemu wskaźnikowi nie możemy zmienić zmiennej na którą pokazuje
  // ale przy jego pomocy możemy zmieniać wskazywaną zmienną
  
  const int* const wskaznik_o_stalym_adresie_na_stala=wsk;
  // temu wskaźnikowi nie możemy zmienić adresu ani przy jego pomocy zmienić wartości wskazywanej
  

printf("\nTABLICE A WSKAZNIKI\n\n");

  /* tablica to w rzeczywistosci wskaźnik do pierwszego jej elementu ... */
  int tablica[5]; /* zadeklarowalismy tablicę o pięciu elementach typu int (indeksowanych 0 ... 4) */
  tablica[0]=50; tablica[1]=51; tablica[2]=52; tablica[3]=53; tablica[4]=54;

  wsk=tablica;
    /* do naszej zmiennej wskaźnikowej wpiszemy adres naszej tablicy (nie potrzebujemy tutaj używać &) */

  for (a=0; a<5; a++) {
    printf("Element numer a=%d, wypisany *(tablica+a) wynosi: %d\n", a, *(tablica+a));
    printf("                    i jest to to samo co tablica[a]: %d\n", tablica[a]);
    printf("                    jak równierz wypisanie *(wsk++): %d\n", *(wsk++));
    printf("         jego adres to: %p\n", wsk);
  }


printf("\nFUNKCJE I WSKAZNIKI\n\n");

  int z = 0;
  printf("Po zainicjowalizowaniu zmiennej: %d\n", z);

  funkcja_1(&z);
  printf("Po wykonaniu funkcji: %d\n", z);

printf("\nFUNKCJE I TABLICE\n\n");

  funkcja_2(tablica); /* do tej funkcji przekaże tablicę - kompilator zamieni to na przekazanie wskaźnika */
  printf("po naszej kolejnej funkcji wartosc tablica[2] wynosi: %d %d\n", tablica[2], 2[tablica]);
  // zaskakujaca postac 2[tablica] jest rownowazna
  // bo operator a[b] jest tak naprawde rozumiany jako *(a+b)


printf("\nFUNKCJE I FUNKCJE\n\n");

  double x;
  x = funkcja_3( suma );
    /* jako argument funkcja_3 przekazujemy nazwe funkcji - suma, a zwracaną wartość wpisujemy do x */
  printf("Po wykonaniu naszych funkcji mamy: %f\n", x);


printf("\nTABLICE DWUWYMIAROWE\n\n");

  /* deklarujemy tablicę dwuwymiarową i ją wypełniamy */
  int tablica_dwuwymiarowa[2][3];
  tablica_dwuwymiarowa[0][0]=0; tablica_dwuwymiarowa[0][1]=1; tablica_dwuwymiarowa[0][2]=2;
  tablica_dwuwymiarowa[1][0]=10; tablica_dwuwymiarowa[1][1]=11; tablica_dwuwymiarowa[1][2]=12;

  /* przypisujemy jej adres do naszego wskaźnika
     tym razem użyliśmy * jest to podyktowane tym iz tak naprawde tablica dwuwymiarowa to tablica tablic,
     co zatem idzie pod adresem wskazywanym przez nazwe tablicy jest wskaznik do tablicy odpowiadajcej
     pierwszemu wierszowi (a zarazem pierwszemu elementowi w tym wierszu)*/
  wsk = *tablica_dwuwymiarowa;

  printf("Wywolanie tablica_dwuwymiarowa[i][j] daje: %d\n", tablica_dwuwymiarowa[1][2]);
  printf("     i to samo daje: wsk[i*SZEROKOSC+j]: %d\n", wsk[1 * 3 + 2]);
    /* taki sposób odwołania przydaje się przy przekazywaniu do funkcji tablic dwuwymiarowych
       gdzie SZEROKOSC to ilość kolumn - zakres drugiego argumentu 
    
    to dziala dlatego ze zazwyczaj i tak calosc takiej tablicy jest alokowan w jednym kawalku,
    ale w ohgolnosci nalezy uwazac bo kolejne wiersze tablicy wielowymiarowej
    nie musza skaldac sie na ciagly obszar pamieci ... */


printf("\nTABLICE WSKAZNIKOW i WSKAZNIKI DO WSKAZNIKOW\n\n");

  int *wskazniki[2];
  wskazniki[0] = tablica;
  wskazniki[1] = &a;

  printf("Element trzeci tablicy siedzacej pod wskazniki[0] to: %d\n", wskazniki[0][3]);
  printf("Ale trzeba UWAZAC bo odwołanie do trzeciego elementu wskazniki[1] nie ma sensu: %d\n", wskazniki[1][3]);
  printf("  sens ma natomiast *wskazniki[1]: %d ale nie wskazniki[1]: %p\n", *wskazniki[1], wskazniki[1]);
  printf("  podobnie *wskazniki[0]: %d i wskazniki[0]: %p\n", *wskazniki[0], wskazniki[0]);
  printf("  natomiast sens posiada **wskazniki (czyli wskaznik do wskaznika, wyluskujacy");
  printf("    zawartosc komurki ktorej adres zapisany jest pod adresem w wskazniki): %d\n", **wskazniki);

  /* wskazniki do wskaznikow mozna tez po prostu eklarowac */
  int **wskwsk;
  wskwsk = wskazniki + 1;
  printf("Natomiast po podwujnym wyluskaniu wskwsk = wskazniki + 1;, gdzie int **wskwsk; mamy: %d\n", **wskwsk);
  printf("  czyli to samo co *wskazniki[1]\n");
  

printf("\nSTRUKTURY i WSKAZNIKI DO NICH\n\n");

  dane moja_struktura;
  moja_struktura.liczba = 23;
  funkcja_4(&moja_struktura);
  printf("Co troche moze dziwic ale sama nazwa struktury nie jest traktowana jak wskaznik");
  printf(" lecz jak wyluskanie pierwszego jej elementu: %d\n", moja_struktura);

  /* w tym miejscu należy trochę przyszłościowo wspomnieć o wskaźnikach do klas i metod w C++,
   jak już wiadomo wskaźnik zna rozmiar elementu na który wskazuje - dzięki temu działa artmetyka wskażnikowa,
   ale mechanizm ten może być zdradliwy przy dziedziczonej chierarchii klas - możemy używać wskaźnika na klasę
   bazową celem odwoływania się do klas pochodnych, ale taki wskaźnik ma nieprawdziwą wielkość klasy
   (ma wielkość klasy bazowej a nie pochodnej) pozatym w przypadku dziedziczenia zdradliwe może być
   też porównywanie wartości wskaźników celem sprawdzenia czy wskazują na ten sam obiekt */


  return 0; /* zwracam kod powrotu i zakańczam wykonywanie programu */
}

XHTML generated by highlight (http://www.andre-simon.de/) from wskaznikologia.c



Copyright (c) 1999-2015, Robert Paciorek (http://www.opcode.eu.org/), BSD/MIT-type license


Redystrybucja wersji źródłowych i wynikowych, po lub bez dokonywania modyfikacji JEST DOZWOLONA, pod warunkiem zachowania niniejszej informacji o prawach autorskich. Autor NIE ponosi JAKIEJKOLWIEK odpowiedzialności za skutki użytkowania tego dokumentu/programu oraz za wykorzystanie zawartych tu informacji.

This text/program is free document/software. Redistribution and use in source and binary forms, with or without modification, ARE PERMITTED provided save this copyright notice. This document/program is distributed WITHOUT any warranty, use at YOUR own risk.

Valid XHTML 1.1 Dokument ten (URL: http://www.opcode.eu.org/programing/c_cpp/wskaznikologia.c) należy do serwisu OpCode. Autorem tej strony jest Robert Paciorek, wszelkie uwagi proszę kierować na adres e-mail serwisu: webmaster@opcode.eu.org.
Data ostatniej modyfikacji artykulu: '2014-01-07 19:27:39 (UTC)' (data ta może być zafałszowana niemerytorycznymi modyfikacjami artykułu).