Jazyk C - smerník (pointer): Rozdiel medzi revíziami

Z Kiwiki
Skočit na navigaci Skočit na vyhledávání
(Vytvorená stránka „Kategória:Študijné materiály Kategória:Programovanie Kategória:Informatika Smerníky sú najdôležitejšou časťou jazyka C, ale aj najužitočnejšou a…“)
 
Riadok 155: Riadok 155:
 
delete uk;
 
delete uk;
 
</source>
 
</source>
 +
==Polia a ukazovatele==
 +
Pri alokácii pamäti pre ukazovateľ si nemusíme alokovať len pre daný dátový typ, ale môžeme si alokovať ľubovoľne veľkú oblasť pamäti (samozrejme s obmedzením veľkosti dostupnej pamäti). Majme:
 +
int *ui=new int[10];
 +
 +
*ui je ukazovateľ na typ int.
 +
*Alokovali sme si pamäť o veľkosti 10 integerov
 +
 +
'''Popis:'''
 +
*<nowiki>*</nowiki>ui predstavuje hodnotu prvého alokovaného integera
 +
*<nowiki>*</nowiki>(ui+1) predstavuje druhého alokovaného integera
 +
*Môžme teda tentu smerníkpoužiť nasledovne
 +
**cin>><nowiki>*</nowiki>ui>><nowiki>*</nowiki>(ui+1);
 +
**cout<<<nowiki>*</nowiki>ui<<<nowiki>*</nowiki>(ui+1);
 +
*ui môžeme chápať ako pole celých čísel o veľkosti 10
 +
*Ak je teda ui pole celých čísel o veľkosti 10, môžeme k nemu pristupovať ako ku klasickému jednorozmernému poľu:
 +
**cin>>ui[0]>>ui[1];
 +
**cout<<ui[0]<<ui[1];
 +
 +
===Dealokovanie ui===
 +
delete []ui;
 +
 +
==Viacrozmerné dynamicky alokovateľné polia==
 +
 +
Deklarujme nasledovné:
 +
int *pp=new int[4];
 +
 +
Vieme, že nasledujúce výrazy sú ekvivalentné:
 +
pp[0]==*pp
 +
pp[1]==*(pp+1)
 +
pp[2]==*(pp+2)
 +
pp[3]==*(pp+3)
 +
 +
===Dvojrozmerné dynamicky alokované pole===
 +
Definujme nasledovné:
 +
int n=10;
 +
int **M;
 +
M je smerník na smerník na pole ''n'' celých čísel.
 +
 +
Alokujme pre M pole n smerníkov na celé čísla:
 +
M=new int*[n];
 +
Po vykonaní tohto výrazu je M pole smerníkov na int. Do tohoto poľa ešte nemôžeme nič ukladať, pretože bunky tohto poľa sú smerníky a ešte sme si nealokovali pre ne miesta, kde budú uložené hodnoty. Pre každý prvok poľa M (prvky sú smerníky na int) si alokujme miesto o n integeroch:
 +
<source lang="c">
 +
for(int i=0;i<n;i++)
 +
  M[i]=new int[n];
 +
</source>
 +
Takto sme vytvorili dvojrozmerné pole celých čísel o veľkosti n×n. Zaujímavosťou tohto riešenie je že n je premenná a jej hodnotu môžme v programe meniť. Pri statických poliach musel byť rozmer konštantný.
 +
 +
Celý kód ešte raz:
 +
<source lang="c">
 +
  int **M=new int*[n];         // smerník na smerník  na int
 +
  for(int i=0 ; i<n ; i++) // M[] je pole smerníkov na typ int
 +
    M[i]=new int[n];         // každému prvku M[i] alokujeme pole integerov o veľkosti n
 +
</source>
 +
 +
===Príklad 2===
 +
Vytvorme program na nájdenie maximálneho prvku poľa<ref>Smerníky v jazyku C http://www.inet.sk/clanok/3682/programujeme-v-jazyku-c-smerniky-ii-polia</ref>:
 +
{| class="wikitable" cellpadding=5
 +
|-
 +
! Riešenie pomocou statických polí
 +
! Riešenie pomocou dynamicky akokovaných polí
 +
|-
 +
|
 +
<source lang="c">
 +
#include<stdio.h>
 +
int main(void)
 +
{
 +
    int i,j;
 +
    int pocet;
 +
    int max;
 +
 +
    int pole[100]; //predpokladáme, že maximum bude 100 čísel
 +
 +
    //vypýtame si počet zadávaných čísel
 +
 +
    printf("Zadajte pocet zadavanych cisel: ");
 +
    scanf("%d",&pocet);
 +
 +
    //načítame a uložíme jednotlivé čísla do pola
 +
 +
    for(i = 0;i<pocet;i++)
 +
    {
 +
        printf("Zadajte %d. prvok pola: ",i+1);
 +
        scanf("%d",&pole[i]); //teraz prístup cez index
 +
    }
 +
 +
    //jednoduchý cyklus na nájdenie maxima
 +
    max = pole[0];
 +
    for(i = 1;i<pocet;i++)
 +
    {
 +
        if (max<pole[i])
 +
        {
 +
            max = pole[i];
 +
        }
 +
    }
 +
    printf("n Maximalny prvok je : %d",max);
 +
}
 +
</source>
 +
|
 +
<source lang="c">
 +
#include<stdio.h>
 +
int main(void)
 +
{
 +
    int i,j;
 +
    int pocet;
 +
    int* max; // pozor!!! Tu je smerník!
 +
    int pole[100]; //predpokladáme, že maximum bude 100 čísel
 +
 +
    //vypýtame si počet zadávaných čísel
 +
 +
    printf("Zadajte pocet zadavanych cisel: ");
 +
    scanf("%d",&pocet);
 +
 +
    //načítame a uložíme jednotlivé čísla do pola
 +
 +
    for(i = 0;i<pocet;i++)
 +
    {
 +
        printf("Zadajte %d. prvok pola: ",i+1);
 +
        scanf("%d",pole+i); //teraz prístup cez smerníkovú aritmetiku
 +
    }
 +
 +
    //jednoduchý cyklus na nájdenie maxima
 +
 +
    max = pole; //zmena!!!
 +
    for(i = 1;i<pocet;i++)
 +
    {
 +
        if (*max<pole[i]) //aj tu sa porovnávajú hodnoty!!!
 +
        {
 +
            max = pole+i;
 +
        }
 +
    }
 +
    printf("n Maximalny prvok je : %d",*max);
 +
}
 +
</source>
 +
|}
  
 
=Odkazy=
 
=Odkazy=
 
<references/>
 
<references/>

Verzia zo dňa a času 22:13, 3. január 2010

Smerníky sú najdôležitejšou časťou jazyka C, ale aj najužitočnejšou a najzradnejšou. Pri správnom použití vedia veľmi uľahčiť život programátora, ale pri nesprávnom, nastane strach a hrôza. Preto poďme pekne poporiadku.

Definícia smerníka

Smerník
(anglicky pointer, česky ukazovatel) je obyčajná premenná, ktorá obsahuje adresu inej premennej.

S použitím smerníkov sa stretávame pri spracovaní

  • premenných,
  • polí,
  • reťazcov,
  • parametrov funkcií,
  • dynamických objektov,
  • funkcií (smerník na funkciu).

Definícia v jazyku C

  • Smerník ako dátový typ neexistuje
  • So smerníkom sa vždy spája nejaká premenná
    • smerník na int, smerník na double, smerník na char, atď. ...
  • Definujme ui ako ukazovateľ na typ int
    • int *ui;
  • Definujme uf ako ukazovateľ na typ float
    • float *uf;

Súvislosť operátora referencia (&) a dereferencia (*)

definujme nasledujúce:

int *ui;
int i;

Tieto 2 premenné nemajú nič spoločné. ui je smerník na int, i je premenná typu int.

*ui
hodnota, na adrese ui (ui je ukazovatel na int)
&i
adresa premennej i (i je premenná typu int)
ui
adresa v pamäti, kde je uložená hodnota smerníka

Príklad 1[1]

Bez použitia premennej x priamo pri výpočte zvýšte jej hodnotu o 10.

 1 #include<stdio.h>
 2 int main(void)
 3 {
 4     int x = 0 ; //do x sme vložili hodnotu 0
 5     int* smernik; // !!!smerník na int alebo smerník typu int
 6 
 7     smernik = &x; // do smerníka sme uložili adresu x
 8 
 9     printf("npred pricitanim:x = %d",x);
10     *smernik = *smernik + 10;
11             // na adresu ,kde je x ,sme pričítali 10
12             // * dereferenčný operátor
13             // & referenčný operátor
14 
15     printf("npo pricitani: x = %d",x);
16     getchar();
17 }

Analýza programu

Prvé dva riadky by mali byť jasné. Máme premennú typu int, preto potrebujeme vytvoriť smerník typu int.

Pozrime sa bližšie na riadok č. 5 :

smernik = &x;

Znaku & sa hovorí tiež referenčný operátor. Vďaka nemu získame adresu premennej x. Keby sme napísali smerník = x, tak by sme nezískali veľa. Do smerníka by sme si uložili namiesto adresy hodnotu premennej. V našom prípade by to bola 0. Takže smerník by nám ukazoval na adresu 0 a nie na adresu, kde je uložená hodnota premennej x.

Riadok č. 10:

*smernik = *smernik + 10

Znaku * sa hovorí tiež dereferenčný operátor. Vďaka tomuto operátoru získame obsah na adrese, na ktorú ukazuje. Využili sme to na ľavej strane výrazu: *smernik = *smernik + 10

Vďaka dereferenčnému operátoru vieme tiež zapísať hodnoty na adresu kam ukazuje. Využili sme to na pravej strane : *smernik = *smernik + 10.

Riadok *smernik = *smernik + 10; robí to isté ako x = x + 10;

Operátor & sa používa pre premenné. Konštanty a výrazy nemajú adresu, preto tento operátor s nimi nemôžme využiť.


Príklad chybných príkazov:

smernik = &10 ; // chyba 10 je konštanta => nemá adresu
smernik = &(x + 10) ; // chyba (x+10) je výraz
smernik = 1 ; // už sme spomínali, smerník by teraz ukazoval niekam na adresu 1

Nulový smerník NULL

  • Smerník nemôže mať hodnotu 0 (znamenalo by to, že odkazuje na adresu 0), ale môže mať hodnotu NULL.
  • Hodnota NULL je definovaná ako
    • #define NULL 0
    • #define NULL ((void *) 0)
  • Ak má smerník hodnotu NULL, tak ukazuje na nič

Z predchádzajúceho vyplýva jedna vec: smerník môžeme definovať aj na typ void

Smerník a pole

Pole je množina prvkov rovnakého typu, ktorá je označená spoločným názvom. Pole je uložené v operačnej pamäti spojite, pričom prvý prvok (index 0) má najnižšiu a posledný prvok najvyššiu adresu. Medzi poľami a smerníkmi existuje úzka súvislosť. Práca s poľom a jeho prvkami formou používania názvu poľa a indexov je jednoduchšia, názornejšia a možno povedať apriori akceptovateľná. Používanie smerníkov vedie k rýchlejšiemu behu programu, umožňuje dynamickú alokáciu pamäte a môže redukovať celkové požiadavky na pamäť.

Meno poľa je v skutočnosti symbolická konštanta, ktorej hodnota je smerník na umiestneni prvého prvku poľa. Teda pre pole data[] identifikátor data znamená to isté ako &data[0], data + i to isté ako &data[i], čo môžeme zapísať v tvare

data + i == &data[i]

Aplikáciou operátora indirekcie * na obe strany výrazu dostaneme

*(data+i) == data[i]

Ak sa i zvyšuje o 1, referencované miesto pamäte sa zvyšuje o počet bytov (dĺžku), daný veľkosťou daného typu.

Jazyk C netestuje hranice polí, čo umožňuje zápis i čítanie mimo rozsahu poľa, pravda, so všetkými z toho vyplývajúcimi dôsledkami. Za kontrolu dodržania rozsahu poľa je preto zodpovedný programátor.

Použitie smerníka a poľa si ilustrujeme časťou programu

 int *p, data[10];
 p=&data[0];

potom *p = data[0] a my môžeme namiesto data[0] v ľubovoľnom výraze používať *p, resp. pre prvok data[i] *(p + i), lebo

 p + i  == &data[i]
 *(p + i) == data[i]

Z uvedeného by sa mohlo zdať, že identifikátory data a p sú ekvivalentné. Je tu však jeden podstatný rozdiel, ktorý spočíva v tom, že data je symbolická smerníková konštanta, zatiaľ čo p je smerníková premenná. Preto operácie ako p++, p = data sú prípustné, zatiaľ čo data++, resp. data = p nie sú povolené. Majme napr.

 float array[100];
 fp = &array[0];

potom fp++ ukazuje na array[1]. Podobne fp = fp + 10 bude ukazovať na array[10]. Predpokladajme, že

  fp1 = &array[100];

Potom fp-- ukazuje na array[99], resp. fp1 = fp1 - 10 alebo fp1 -= 10 bude ukazovať na array[90].

Na smerníky (ak sa vzťahujú na prvky toho istého poľa) je možné aplikovať aj 4 relačné operátory (<, <=, >, >= ) a testovať ich rovnosť, resp. nerovnosť ( ==, != ). Použitie týchto operátorov ilustruje následujúci príklad pre nájdenie najväčšieho prvku v poli data.

Viac o smerníkovej aritmetike nájdete v [2].

Dynamické prideľovanie pamäti

Pomocou mechanizmu smerníkov si dokážeme alokovať orčitú veľkosť pamäti vo svojom programe.

Alokácia pamäti

Definujme si smerník na int nasleddovne:

int *ui;

Do takto novo vytvorenej premennej (resp. smerníka) nemôžeme priradiť žiadnu hodnotu, pretože pre daný smerník sme nealokovali miesto v pamäti, kde bude ukladať svoj obsah. Nasledujúci zápis je chybný!

*ui=3; // !!!chyba

Pokúšame sa do smerníka uložiť hodnotu 3, ale túto hodnotu nemáme ešte kde uložiť. Potrebujeme si alokovať miesto.

Operátor new

operátor new (namesto fukcie malloc jazyka C používame operátor new jazyka C++ kvôli jednoduchšej syntaxi) alokuje pre smerník potrebné miesto v pamäti. Pri neúspechu vracia hodnotu NULL.

 int *ui;  // definícia smerníka ui na int
 ui=new int;  // pre smerník ui si alokujeme miesto o veľkosti 1 int
 int *uk=new int; // zápis v jednom riadku

Dealokácia pamäti

Pamäť alokovaná operátorom new zostáva rezervovaná až do konca behu programu. Počas behu programu je môžeme uvoľniť použitím operátora delete. Použitie:

delete ui;
delete uk;

Polia a ukazovatele

Pri alokácii pamäti pre ukazovateľ si nemusíme alokovať len pre daný dátový typ, ale môžeme si alokovať ľubovoľne veľkú oblasť pamäti (samozrejme s obmedzením veľkosti dostupnej pamäti). Majme:

int *ui=new int[10];
  • ui je ukazovateľ na typ int.
  • Alokovali sme si pamäť o veľkosti 10 integerov

Popis:

  • *ui predstavuje hodnotu prvého alokovaného integera
  • *(ui+1) predstavuje druhého alokovaného integera
  • Môžme teda tentu smerníkpoužiť nasledovne
    • cin>>*ui>>*(ui+1);
    • cout<<*ui<<*(ui+1);
  • ui môžeme chápať ako pole celých čísel o veľkosti 10
  • Ak je teda ui pole celých čísel o veľkosti 10, môžeme k nemu pristupovať ako ku klasickému jednorozmernému poľu:
    • cin>>ui[0]>>ui[1];
    • cout<<ui[0]<<ui[1];

Dealokovanie ui

delete []ui;

Viacrozmerné dynamicky alokovateľné polia

Deklarujme nasledovné:

int *pp=new int[4];

Vieme, že nasledujúce výrazy sú ekvivalentné:

pp[0]==*pp
pp[1]==*(pp+1)
pp[2]==*(pp+2)
pp[3]==*(pp+3)

Dvojrozmerné dynamicky alokované pole

Definujme nasledovné:

int n=10;
int **M;

M je smerník na smerník na pole n celých čísel.

Alokujme pre M pole n smerníkov na celé čísla:

M=new int*[n];

Po vykonaní tohto výrazu je M pole smerníkov na int. Do tohoto poľa ešte nemôžeme nič ukladať, pretože bunky tohto poľa sú smerníky a ešte sme si nealokovali pre ne miesta, kde budú uložené hodnoty. Pre každý prvok poľa M (prvky sú smerníky na int) si alokujme miesto o n integeroch:

 
for(int i=0;i<n;i++)	
   M[i]=new int[n];

Takto sme vytvorili dvojrozmerné pole celých čísel o veľkosti n×n. Zaujímavosťou tohto riešenie je že n je premenná a jej hodnotu môžme v programe meniť. Pri statických poliach musel byť rozmer konštantný.

Celý kód ešte raz:

 
  int **M=new int*[n];	        // smerník na smerník  na int
  for(int i=0 ; i<n ; i++)	// M[] je pole smerníkov na typ int
    M[i]=new int[n];	        // každému prvku M[i] alokujeme pole integerov o veľkosti n

Príklad 2

Vytvorme program na nájdenie maximálneho prvku poľa[3]:

Riešenie pomocou statických polí Riešenie pomocou dynamicky akokovaných polí
 
#include<stdio.h>
int main(void)
{
    int i,j;
    int pocet;
    int max;

    int pole[100]; //predpokladáme, že maximum bude 100 čísel

    //vypýtame si počet zadávaných čísel

    printf("Zadajte pocet zadavanych cisel: ");
    scanf("%d",&pocet);

    //načítame a uložíme jednotlivé čísla do pola

    for(i = 0;i<pocet;i++)
    {
        printf("Zadajte %d. prvok pola: ",i+1);
        scanf("%d",&pole[i]); //teraz prístup cez index
    }

    //jednoduchý cyklus na nájdenie maxima
    max = pole[0];
    for(i = 1;i<pocet;i++)
    {
        if (max<pole[i])
        {
            max = pole[i];
        }
    }
    printf("n Maximalny prvok je : %d",max);
}
 
#include<stdio.h>
int main(void)
{
    int i,j;
    int pocet;
    int* max; // pozor!!! Tu je smerník!
    int pole[100]; //predpokladáme, že maximum bude 100 čísel

    //vypýtame si počet zadávaných čísel

    printf("Zadajte pocet zadavanych cisel: ");
    scanf("%d",&pocet);

    //načítame a uložíme jednotlivé čísla do pola

    for(i = 0;i<pocet;i++)
    {
        printf("Zadajte %d. prvok pola: ",i+1);
        scanf("%d",pole+i); //teraz prístup cez smerníkovú aritmetiku
    }

    //jednoduchý cyklus na nájdenie maxima

    max = pole; //zmena!!!
    for(i = 1;i<pocet;i++)
    {
        if (*max<pole[i]) //aj tu sa porovnávajú hodnoty!!!
        {
            max = pole+i;
        }
    }
    printf("n Maximalny prvok je : %d",*max);
}

Odkazy