PB071: Úvod do jazyka C - 3

Pole, ukazatele

Pole

Pole je seskupení určitého počtu dat téhož typu, jeho prvky se rozlišují indexem. Úvodní prvek pole má index 0. indexy se zapisují do hranatých závorek.

float a[5]; vytvoří 5 proměnných typu float: a[0], a[1] ... a[4] (NE a[5]!!!)

Pole lze přímo v deklaraci inicializovat, potom není nutné uvádět jeho velikost:

char odpovedi[]={'a','y','n'};

K zamyšlení: je to totéž jako char odpovedi[]="ayn"; ???

V ANSI C musí být rozměr pole konstantní, proto se často pro jeho rozměr zavádí makro, aby bylo možné ho snadno změnit:

#define RADEK 81
char nadpis[RADEK];

V C99 může být mez pole proměnná, pokud je pole deklarováno lokálně ve funkci:

int pocet;
scanf("%d",&pocet);
double x[pocet],y[pocet];

Globální pole však i v C99 musí mít rozměr konstantní - ;konstanta, konstantní výraz (případně definovaný makrem).

Vícerozměrná pole:

#define RADKY 10
#define SLOUPCE 8
...
int table[RADKY][SLOUPCE]; /* rozměry musí být konstantní */
...
for(i=0; i<RADKY; i++)
  for(j=0; j<SLOUPCE; j++)
    { ...
      ... table[i][j] ...  /* POZOR: nikoli table[i,j] !!! */
                           /* zde pracujeme s prvkem o indexech i,j */
    } 
... 

Ukazatele (pointery)

Ukazatele jsou jako nunčaky. Dobrá zbraň - ale když to s nimi neumíš, jsi v ...

Ukazatel je v podstatě abstraktnější ztvárnění adresy. Ukazatel na x je tedy obvykle realizován jako adresa, kde je uloženo x.

Používají se operátory:

* dereference, získání hodnoty, na niž ukazuje ukazatel
& reference, získání ukazatele na objekt (jeho adresy)

int i, *p; // *p je int, tedy p je ukazatel na int
i=123; p=&i; *p=789; // teď má i hodnotu 789

Prázdný ukazatel

Ukazatel může také ukazovat "nikam". Jeho hodnota je potom dána standardním makrem NULL (obvykle je to nula). Ukazatel NULL nelze dereferencovat.

Ukazatelová aritmetika

Jsou-li p, q ukazatele na objekty téhož typu a i celočíselná hodnota, jsou povoleny operace:
     p+i p-i p-q
p+1
neukazuje o 1 bajt dále, ale o 1 objekt dále! Tedy, ukazuje-li p na i-tý prvek pole typu double, na následující prvek ukazuje p+1 a nikoli p+sizeof(double)

Ukazatelová aritmetika má smysl, pokud je jednoznačně definováno uspořádání příslušných objektů v paměti. Prakticky je to zaručeně splněno, pokud jde o ukazatele na prvky téhož pole.

Z předchozího plyne, že je-li deklarováno char text[40]; pak text+2 představuje stejný řetězec jako text, avšak bez prvních dvou znaků.

Vztah polí a ukazatelů

A je ve většině kontextů totéž jako &A[0]
A+i
je totéž jako &A[i]
*(A+i)
je totéž jako A[i] - a totéž jako *(i+A) neboli i[A] !!!

Textová konstanta je také pole znaků, proto i ji lze použít jako pole či ukazatel a aplikovat  na ni ukazatelovou aritmetiku:

printf("neprospel"+2*(toupper(kod)=='P'));

Pokud proměnná kod obsahuje velké nebo malé písmeno P, vypíše se prospel, jinak neprospel 

Implementace na PC

Rozlišují se ukazatele __near, __far a __huge

Ukazatel na nespecifikovaný typ

void *ukaz;

Přiřadit mu lze ukazatel jakéhokoli typu, nesmí být dereferencován. Ukazatelová aritmetika potom počítá výjimečně v bytech, ne v objektech

Restringované ukazatele (C99)

int * restrict uk;

Definujeme-li ukazatel jako restringovaný, dáváme tak překladači najevo, že k objektu, na nějž ukazuje, nebudeme přistupovat jinak, než přes tento ukazatel. To umožní překladači generovat optimální kód - ovšem běda nám, když vyslovený závazek nedodržíme - překladač to nebude hlídat.

Příklad:

Standardní funkce strcpy vyžaduje, aby se pole definovaná prvním a druhým parametrem v paměti nepřekrývala. Proto má v C99 parametry char * restrict a, char * restrict b

Pole ukazatelů

Potřebujeme-li pole konstantních řetězců, je možno deklarovat pole ukazatelů na char a inicializovat ho:

const char *tab[]={"nula","jedna","dva","tri","ctyri"};

tab[0] nyní představuje řetězec "nula" apod.

Výhodou proti dvourozměrnému poli char je to, že jednotlivé řetězce mohou mít různé délky.


Předchozí Předchozí přednáška Další Další přednáška Hlavní stránka Hlavní stránka