Deo zbornika Uvod u svet programiranja

Strukture i funkcije u jeziku C

Email Twitter LinkedIn Facebook Google

Jedine dopuštene operacije na strukturama su kopiranje i dodjeljivanje strukturi kao cjelini, označujući joj adresu operatorom & i pristupajući njenim članovima. Kopiranje i dodjeljivanje uključuju prenošenje argumenata na funkcije kao i vraćanje vrijednosti od strane funkcija. Strukture se na mogu upoređivati. Struktura se dade inicijalizirati nizom članova konstantnih vrijednosti, a automatska struktura se može također inicijalizirati dodjeljivanjem.

Ispitajmo strukture opisujući funkcije za rad s točkama i trokutima.

Postoje različiti prilazi ovom problemu. Ili ćemo prenositi članove strukture posebno ili čitavu strukturu, a možemo prenositi pokazivače na strukturu. Svako od nabrojanih mogućih rješenja ima dobre i loše strane.

Prenos članova strukture

Prva funkcija, makepoint, uporabit će dva cijela broja, te vratiti strukturu point

/* makepoint : stvara točku od x i y komponente */
struct point makepoint(int x, int y)
{
        struct point temp;
        temp.x = x;
        temp.y = y;
        return temp;
}

Primijetimo kako nema proturječnosti između imena argumenata i člana s istim imenom. Čak štoviše, uporaba istog imena naglašava njihovu međusobnu vezu. Sada možemo uporabiti funkciju makepoint radi dinamičke inicijalizacije bilo koje strukture ili stvaranja strukture argumenata funkcija

struct rect screen;
struct point middle;
struct point makepoint(int, int);
screen.pt1 = makepoint(0, 0);
screen.pt2 = makepoint(XMAX, YMAX);
middle = makepoint((scrren.pt1.x + scrren.pt2.x) / 2, (screen.pt1.y + screen.pt2.y) / 2);

Slijedeći korak je skup funkcija koje primjenjuju linearnu algebru točaka. Primjerice,

/* addpoint : zbraja dva vektora */
struct point addpoint()
{
        p1.x += p2.x;
        p1.y += p2.y;
        return p1;
}

Ovdje su strukture i argumenti funkcije i povratne vrijednosti. Zgodnije nam se čini uvećanje komponenti u član p1, nego uporaba eksplicitne privremene varijable koja bi istakla da su parametri strukture preneseni po vrijednosti.

Prenos čitave strukture

Slijedeći primjer je funkcija ptinrect koja provjerava da li je točka unutar pravokutnika, uz pretpostavku da pravokutnik ima svoju lijevu i donju stranu, bez gornje i desne

/* ptinrect : vraća vrijednost 1 ako je točka p unutar pravokutnika, a 0 ako nije */
int ptinrect(struct point p, struct rect r)
{
        return p.x >= r.pt.x && p.x < r.pt2.x && p.y >= r.pt1.y && r.pt2.y;
}

Ovo podrazumijeva da je pravokutnik prikazan u standardnom obliku koji uvjetuje da su koordinate člana pt1 manje od onih u članu pt2. Funkcija koju ćemo sada napisati vraća pravokutnik u kanonski oblik

#define min(a, b) ((a)(b) ? (a) : (b))
#define max(a, b) ((a)(b) ? (a) : (b))

/* canonrect : determinira koordinate pravog kuta */
struct rect canonrect(struct rect r)
{
        struct rect temp;
        temp.pt1.x = min(r.pt1.x, r.pt2.x);
        temp.pt1.y = min(r.pt1.y, r.pt2.y);
        temp.pt2.x = max(r.pt1.x, r.pt2.x);
        temp.pt2.y = max(r.pt1.y, r.pt2.y);
        return temp;
}

Prenos pokazivača

Ako veliku strukturu treba predati funkciji, zapravo je učinkovitije prenijeti pokazivač negoli preslikati cijelu strukturu (i time je umnožiti u memoriji). Pokazivači na strukture djeluju jednako pokazivačima na obične varijable. Deklaracija

struct point *pp;

govori da je pp pokazivač na strukturu tipa point. Ako pp pokazuje na point strukturu, tada je *pp struktura, (*pp).x i (*pp).y su članovi. Pogledajmo slijedeći primjer

struct point origin, *pp;
pp = &origin;
printf("origin is (%d, %d)\n", (*pp).x, (*pp).y);

Mala zagrada je obvezna u konstrukciji (*pp).x, jer je prioritet strukture člana operatora . veći od *. Izraz *pp.x ima značenje kao *(pp.x) što nije dopušteno jer je x član, a ne pokazivač. Pokazivači na strukture se rabe prilično često, pa je uvedena i specijalna sintaksa radi jasnoće koda. Ako je p pokazivač na strukturu, tada se izraz

p->član_strukture

odnosi na taj član. Naredbu za ispis iz prethodnog primjera mogli smo, dakle, napisati kao

printf("origin is (%d, %d)\n", pp->x, pp->y);

I . i -> se pridružuju slijeva nadesno, pa tako ako imamo

struct rect r, *rp=r;

ova četiri izraza su ekvivalentna

r.pt1.x
rp->pt1.x
(r.pt1).x
(rp->pt1).x

Operatori struktura . i ->, zajedno sa malim zagradama za pozive funkcija, te uglatim zagradama za indekse, jesu na vrhu hijerarhije prioriteta, pa su zato tijesno povezani. Npr. ako imamo deklaraciju

struct
{
        int len;
        char *str;
} * p;

onda

++p->len;

uvećava vrijednost člana len, a ne strukture p, jer se predmnijeva zagrada ++(p->len). Zagradama se izraz dade izmijeniti. Tako izrazom (++p)->len uvećavamo strukturu p prije pridruživanja člana, a izrazom (p++)->len uvećavamo strukturu p nakon toga (pri čemu je zagrada nepotrebna).

Analogno tome, izraz *p->str jest vrijednost na koju član str pokazuje. Tako će izraz *p->str++ povećati vrijednost člana str po pristupanju tom članu (slično izrazu *s++), izraz (*p->str)++ uvećava sve ono na što član str pokazuje, te izraz *p++->str uvećava strukturu p nakon pristupanja podatku na koji pokazuje član str.

Izvor: Brian Kernighan i Dennis Ritchie, Programski jezik C