Studijní materiály
EXTRA Přihlásit Kontakt

Manipulace s ukazateli v jazyce C

Ukazatel je proměnná, která místo hodnoty drží adresu jiné proměnné v paměti. Sám o sobě není k ničemu – užitečný je až tím, co se s ním dá dělat: dostat se na hodnotu, na kterou míří, posouvat ho po poli nebo ho předat funkci, která má něco změnit.

Získání adresy a dereference

Operátor & vrátí adresu proměnné, operátor * naopak z adresy udělá hodnotu, která na ní leží (tomu se říká dereference). Přes dereferenci se dá hodnota nejen číst, ale i měnit.

int cislo = 42;
int *ukazatel = &cislo;      // ukazatel míří na proměnnou cislo

printf("%d\n", *ukazatel);   // 42 – hodnota na dané adrese
printf("%p\n", (void *) ukazatel);   // samotná adresa

*ukazatel = 100;             // změní se přímo proměnná cislo
printf("%d\n", cislo);       // 100

Ukazatelová aritmetika

S ukazatelem se dá počítat: přičtení jedničky ho neposune o jeden bajt, ale o velikost typu, na který ukazuje. U pole int tedy p + 1 míří na další prvek, ne na další bajt.

int pole[5] = {10, 20, 30, 40, 50};
int *p = pole;               // jméno pole je adresa prvního prvku

printf("%d\n", *p);          // 10
p++;                         // posun o jeden int
printf("%d\n", *p);          // 20
printf("%d\n", *(p + 2));    // 40

Odečtením dvou ukazatelů do stejného pole dostaneš počet prvků mezi nimi:

int *zacatek = &pole[0];
int *konec = &pole[4];

printf("%td\n", konec - zacatek);   // 4

Ukazatele a pole

Zápis pole[i] je jen pohodlnější způsob, jak napsat *(pole + i). Obojí se přeloží úplně stejně, takže si můžeš vybrat, co je v dané situaci čitelnější.

int pole[3] = {1, 2, 3};

printf("%d\n", pole[1]);       // 2
printf("%d\n", *(pole + 1));   // 2 – totéž

Ukazatel na ukazatel

Ukazatel může mířit i na jiný ukazatel. Píše se se dvěma hvězdičkami a používá se hlavně u dvourozměrných polí nebo tam, kde má funkce změnit samotný ukazatel volajícího.

int cislo = 5;
int *p = &cislo;
int **pp = &p;

printf("%d\n", **pp);   // 5

Hodnota NULL

Ukazatel, který nikam nemíří, se nastavuje na NULL. Dereference takového ukazatele program spolehlivě shodí, proto se před každým použitím ukazatele z nejistého zdroje kontroluje, jestli NULL není.

int *p = NULL;

if (p != NULL) {
    printf("%d\n", *p);
} else {
    printf("Ukazatel nikam nemíří\n");
}

const a ukazatele

U ukazatele se dá zamknout hodnota, adresa, nebo obojí – záleží, kam se const napíše. Nejčastější je první varianta: funkce data jen čte a nesmí je změnit.

const int *a;         // nesmí se měnit hodnota, na kterou ukazatel míří
int *const b = &x;    // nesmí se změnit adresa, hodnota ano
const int *const c = &x;   // nesmí se měnit nic

Nejčastější chyby

Neinicializovaný ukazatel míří na náhodnou adresu a zápis přes něj poškodí paměť. Stejně nebezpečný je ukazatel na už uvolněnou paměť – po zavolání free se proto vyplatí nastavit ho na NULL.

int *p;          // ŠPATNĚ: míří kdovíkam
int *q = NULL;   // SPRÁVNĚ: jasně nikam

int *data = malloc(10 * sizeof(int));
free(data);
data = NULL;     // ať se přes něj nedá omylem sáhnout znovu