Показивач (програмирање)Показивач (понекад поинтер, према енгл. pointer) представља промјенљиву специјалног типа у неким програмским језицима попут C-а, C++-а, Паскала итд. Показивач има сврху да чува адресу меморијске локације неке друге промјенљиве, константе или показивача. Основна својства показивача су:
Када узимамо или мијењамо вриједност елемента на којег показивач показује, кажемо да га дереференцирамо. Типови показивачаУ већини програмских језика који подржавају показиваче, показивачи се дијеле на типске и бестипске. Уз типске показиваче се веже информација о типу промјенљивих на које ће дати показивач моћи показивати, односно чије ће адресе моћи чувати. Када декларишемо типски показивач, он до краја свог вијека има исти тип. Бестипски показивачи немају одређен тип промјенљиве на које ће моћи показивати, те могу показивати на све промјенљиве подједнако. На уштрб тога, међутим, постоје одређена ограничења везана за ову врсту показивача:
Бестипски показивачи се најчешће користе у ситуацијама када одређена функција прихвата податке промјенљивог типа или када нам тип податка није унапријед познат. Референцирање и дереференцирањеРеференцирање је процес у којем показивачу додјељујемо адресу одређене меморијске локације. Након тога кажемо да показивач показује на ту меморијску локацију. Дереференцирање је процес у којем преко показивача који већ показује на неку меморијску локацију приступамо самој локацији, било ради читања њене вриједности, било ради њеног мијењања. Забрањено је, и најчешће узрокује прекид рада програма, дереференцирање показивача који:
Показивачка аритметикаПоказивачка аритметика се односи на посебан начин вршења рачунских операција сабирања и одузимања када међу операндима има и показивача. Тада често не важе уобичајена правила при рачунању, тј. адресе које показивачи чувају неће се сабирати и одузимати као обични бројеви. Показивачка аритметика за сврху има софистицирано руковање показивачима како би се добили одређени резултати. Помоћу показивачке аритметике, показивач се може кретати по одређеним меморијским локацијама, могу се мјерити удаљености једне меморијске локације од друге у зависности од типова показивача итд. Показивачи у програмском језику CТипови показивачаУ програмском језику C, показивачи се дијеле на типске и бестипске. Поред показивача на обичне промјенљиве, постоје и показивачи на функције. Показивачи на функције се могу просљеђивати другим функцијама као аргументи и преко њих се могу позивати функције на које показују, што им је и примарна примјена. ДекларацијаТипски показивачи се декларишу на сљедећи начин: tip_pokazivaca * ime_pokazivaca;
Прво се наводи тип промјенљиве на какве ће моћи показивати показивач, затим слиједи звјездица ( Бестипски показивачи се декларишу на сљедећи начин: void * ime_pokazivaca;
Умјесто типа овде се наводи кључна ријеч Показивачи на функције се декларишу на сљедећи начин: tip_rezultata_originalne_funkcije (*ime_pokazivaca)(tip_prvog_argumenta_funkcije, tip_drugog_argumenta_funkcije, ...);
Слиједи примјер за декларацију показивача на одређену функцију: /* декларација произвољне функције */
char * kopiraj(char * odrediste, char * izvoriste )
{
/* блок функције */
}
/* декларација показивача на овакву функцију (без иницијализације) */
char * (*pkopiraj)(char * odrediste, char * izvoriste );
Референцирање![]() Да бисмо додијелили одређену вриједност показивачкој промјенљивој, користимо уобичајен оператор додјеле: p1 = <<adresa>>;
Пошто је сврха показивача да чувају адресу неког другог податка, морамо да:
Слиједе примјери за сваку од ставки, редом: int * p1;
int a = 3;
p1 = 0x4CC7EC5C; /* изузетно је опасно и не препоручује се додјељивати адресе директно */
p1 = &a; /* користимо оператор & за добијање адресе од a да бисмо је додијелили промјенљивој p1 */
p1 = malloc(sizeof(int) ); /* користимо функцију malloc да алоцирамо простор величине једне цјелобројне промјенљиве. */
/* malloc враћа адресу новоалоцираног простора */
У случајевима када се жели показати да показивач не чува никакву адресу, тада му додјељујемо нулу помоћу уграђене константе int * p1 = NULL;
Код показивача на функције важи нешто другачија синтакса референцирања. Ако постоји функција pf = f;
ДереференцирањеДереференцирање је процес у којем преко показивача који већ показује на неку меморијску локацију приступамо самој локацији. За ову операцију се користи оператор звјездице ( int * p1;
int a;
p1 = &a; /* p1 нека показује на a */
*p1 = 10; /* оно на шта p1 показује (a), нека постане једнако 10 */
Показивачи на функције се деференцирају на нешто другачији начин. Заправо, код њих се не може говорити о правом дереференцирању, него радије само о позивању функције преко показивача. Наиме, оног тренутка када је нпр. показивач int saberi(int a, int b )
{
return a + b;
}
int main()
{
int (*psaberi)(int, int );
int x = 3, y = 4;
int z;
psaberi = saberi;
z = saberi(x, y ); /* позив оригиналне функције */
z = psaberi(x, y ); /* позив функције преко показивача */
}
Слиједи примјер просљеђивања показивача на функцију другој функцији, која је позива преко тог показивача: int saberi(int x, int y )
{
return x + y;
}
int pozovi(int (*psaberi)(int, int), int x, int y )
{
return psaberi(x, y ); /* позивамо функцију преко показивача, и просљеђујемо јој преостала два аргумента */
}
int main()
{
int z;
z = pozovi(saberi, 3, 4 ); /* први аргумент је само име функције, без заграда, а други су аргументи за сабирање */
return 0;
}
Показивачка аритметикаУ показивачкој аритметици програмског језика C не важе уобичајена правила при рачунању, тј. адресе које показивачи чувају неће се сабирати и одузимати као обични бројеви. Начин рачунања ће заправо зависити од тога који је тип промјенљиве на коју показивачи показују, односно који је тип самих показивача. Сабирање и одузимање показивача са цијелим бројем се врши по сљедећој формули: p1 ± n = p1 ± (n * sizeof(*p1));
Погледајмо сљедећи примјер: double x = 3.14; /* претпоставимо да се x налази на адреси 10000 */
double * px = &x; /* px добија вриједност 10000 */
printf("%d\n", px + 5 ); /* штампа се број 10000 + 5*sizeof(double) = нпр. 10000 + 5*8 = 10040 (не 10005) */
Показивачи се могу и међусобно одузимати, када важе слична правила као код показивача и цијелог броја. Важе сљедећа рестриктивна правила везана за показиваче:
ПримјенаУ програмском језику C, најважније примјене показивача су сљедеће:
Пренос аргумената функцији по референциУ програмском језику C, функције су такве да добијају увијек копије аргумената које јој просљеђујемо, а никад оригиналне аргументе. На овај начин функција не може имати спољашње ефекте јер све што добија од функције-позиваоца су копије оригиналних аргумената који остају нетакнути. Овакав начин преноса аргумената се популарно назива пренос аргумената по вриједности, јер функција добија практично само вриједности оригиналних аргумената али никад и директно њих саме. Проблеми се јављају, међутим, у случајевима када желимо да функција ипак измијени одређене промјенљиве које јој прослиједимо као аргументе. Та се потреба јавља нпр. када желимо функцију која ће имати више резултата, јер помоћу уобичајене кључне ријечи Погледајмо сљедећи примјер: void povecaj(int c )
{
c = c+1;
}
int main()
{
int x = 1;
povecaj(x );
printf("%d", x );
return 0;
}
Из претходног параграфа закључујемо да функција
Погледајмо како изгледа резултат измјена: void povecaj(int * pc )
{
*pc = *pc + 1; /* *pc представља оно на шта pc показује, тј. оно што се налази на адреси pc */
}
int main()
{
int x = 1;
povecaj(&x ); /* &x представља адресу од x */
printf("%d", x );
return 0;
}
Користећи пренос аргумената по референци, програмер такође штеди на копирању аргумената при позивању једне функције из друге. Ако је аргумент који преносимо велик податак, нпр. структура од више елемената чија укупна величина превазилази величину показивачке промјенљиве на датом систему, добро га је прослиједити по референци јер на тај начин копирамо мање података. Имплементација низова![]() У програмском језику C не постоје низови као уграђени типови. Напротив, они се имплементирају преко осталих уграђених типова и показивача. Ово важи и за једнодимензионалне и вишедимензионалне низове (матрице, коцке итд.). Сваки низ и C-у се састоји од два физичка дијела - елемената који сачињавају низ и једног показивача који показује на почетак тог низа, тј. његов први елемент (видјети слику десно). Дакле, када представимо низ на сљедећи начин: int a[10];
тада настаје Низови се у C-у имплементирају преко показивача захваљујући специфичној показивачкој аритметици. Ако је Ако идемо даље, и представимо матрицу реалних бројева
![]() Када користимо статичке декларације низова, оваква стања меморије се формирају аутоматски, али је такође могуће формирати их ручно, користећи обичну декларацију показивача и неку од уграђених C-ових функција за алоцирање меморије. Слиједи примјер: char ** alocirajMatricu(int m, int n )
{
char ** M;
int i;
M = malloc(m * sizeof(char *) ); /* алоцирамо меморију за низ показивача */
for (i = 0; i < m; i++ ) /* за сваки показивач у том низу алоцирамо по још један низ */
M[i] = malloc(n * sizeof(char) ); /* карактера да бисмо формирали матрицу. */
return M; /* враћамо показивач „на матрицу“. */
}
int main()
{
char ** M;
int m, n;
int i, j;
scanf("%d %d", &m, &n ); /* питамо корисника колика матрица је потребна */
M = alocirajMatricu(m, n ); /* позивамо функцију да нам алоцира простор те величине */
for (i = 0; i < m; i++ ) /* надаље се понашамо као према обичној матрици */
for (j = 0; j < n; j++ )
M[i][j] = 'x';
// овако алоцирану матрицу морамо обрисати ручно
for (i = 0; i < m; i++ )
free(M[i] );
free(M );
return 0;
}
Имплементација динамичких структураПосебан чланак: Динамичке структуре података Уопштено, динамичке структуре података представљају тип структуре која заузима тачно онолико меморије колико јој је неопходно, и може се ширити и смањивати по потреби. Најчешће спомињане динамичке структуре су бинарно стабло (или бинарно дрво), листа, стек, граф, ред, и други. У програмском језику C оне се имплементирају користећи цеовске структуре ( Показивачи у програмском језику ПаскалТипови показивачаУ програмском језику Паскал, показивачи се дијеле на типске и бестипске. Паскал не подржава показиваче на функције. ДекларацијаТипски показивачи се декларишу на сљедећи начин: ime_pokazivaca: ^tip_pokazivaca;
Прво се наводи име показивачке промјенљиве и двотачка (као код декларације осталих елементарних типова промјенљивих у Паскалу), затим слиједи капица ( Бестипски показивачи се декларишу на сљедећи начин: ime_pokazivaca: pointer;
Умјесто типа и капице, овде се наводи кључна ријеч РеференцирањеРеференцирање је процес у којем показивачу додјељујемо адресу одређене меморијске локације. Да бисмо додијелили одређену вриједност показивачкој промјенљивој, користимо један од следећа два начина:
Слиједе примјери за обје ставке, редом: var
p1: ^integer;
a: integer;
begin
p1 := @a; (* користимо оператор @ за добијање адресе од a да бисмо је додијелили промјенљивој p1 *)
new(p1) (* користимо команду new да алоцирамо простор величине једне цјелобројне промјенљиве (у складу са декларацијом показивача) *)
end.
У случајевима када се жели показати да показивач не чува никакву адресу, тада му додјељујемо нулу помоћу уграђене константе p := nil;
ДереференцирањеЗа ову операцију се користи оператор капице ( var
p1: ^integer;
a: integer;
begin
p1 := @a; (* p1 нека показује на a *)
p1^ := 10; (* оно на шта p1 показује (a), нека постане једнако 10 *)
end.
Показивачка аритметикаПоказивачка аритметика није подржана у стандардном Паскалу. Постоје поједине екстензије Паскала које дозвољавају показивачку аритметику, као што је GPC (Гну-ова екстензија Паскала). Показивачка аритметика Паскала је и тада једноставна и ограничена. Код ње важе уобичајена правила при рачунању, тј. адресе које показивачи чувају ће се сабирати и одузимати као обични бројеви, на тај начин додајући и одузимајући тачно задати број бајтова од адресе коју показивач чува. Погледајмо сљедећи примјер: var
x: real; (* претпоставимо да се x налази на адреси 10000 *)
px: ^real;
{$X+} (* коришћење екстензије која дозвољава показивачку аритметику *)
begin
x := 3.14;
px := @x; (* px добија вриједност 10000 *)
px := px+5; (* px добија вредност 10005 *)
end.
Команде inc(p1) <=> p1 := p1 + sizeof(^p1);
dec(p1) <=> p1 := p1 - sizeof(^p1);
Ово обезбјеђује нпр. итерирање по низу служећи се једним показивачем. ПримјенаУ програмском језику Паскал, показивачи немају превелику примјену, за разлику од програмског језика C. Паскал садржи низ као уграђени тип (користећи кључне ријечи Показивачи у осталим програмским језицимаАдаАда подржава само типске показиваче и конверзија између показивача различитих типова је подржана само у одређеном броју случајева. Сви показивачи се подразумијевано иницијализију на нулу ( C++C++, као језик настао на основу C-а, подржава цјелокупну синтаксу C-а везану за показиваче. C#У програмском језику C#, показивачи су подржани али само под одређеним условима: било који блок кода који садржи показиваче мора бити означен као „несигуран“, користећи кључну ријеч ФортранФортран подржава показиваче од верзије 90. Показивачи у Фортрану, међутим, представљају сложене типове података, који поред адресе циљне промјенљиве такође садрже и доњу и горњу границу низа (уколико показивач служи низ), податке о секцијама низа и друге помоћне податке. Види јошСпољашње везе |
Portal di Ensiklopedia Dunia