Адресна аритметика

Раније смо навели да се над показивачима могу изводити неке операције као што су:

  • додељивање вредности једног показивача другом,

  • поређење и одузимање два показивача,

  • поређење показивача са нулом,

  • сабирање и одузимање показивача и целобројног податка.

Додељивање вредности једног показивача другом

Да се подсетимо, када доделимо вредност једног показивача другом, оба показивача ће показивати на исти податак. Овде мораш водити рачуна да оба показивача морају бити истог типа, односно да указују на податке истог типа.

Анализирајмо детаљно пример (наредбе се извршавају сукцесивно, једна за другом):

int x = 100;
int *p1, *p2;

Овом наредбом дефинишемо скаларну променљиву x и две показивачке променљиве p1 и p2.

Изглед меморије након декларисања променљивих x,*p1,*p2 и доделе вредности променљивој x:

../_images/Picture10.png

Наредбом p1 = &x, показивачу p1 се додељује адреса променљиве x, сада он показује на x.

Изглед меморије након доделе адресе променљиве x показивачу р1, p1 = &x:

../_images/Picture11.png

Изразом p2 = p1 другом показивачу се додељује вредност првог показивача. Та вредност заправо представља адресу променљиве x, што значи да сада и p2 указује на x.

Изглед меморије након наредбе p2=p1:

../_images/Picture12.png

Већ смо причали о генеричким показивачима, па ћемо сада и њих укључити у причу. Ако је један показивач генеричког типа void, а други негенерички, треба обратити пажњу на редослед доделе.

На пример:

double m = 18.37;
double *p1;
void *p2;

Обрати пажњу на следећа правила:

  • Дозвољено је p2 = p1, јер је објашњење логично - помоћу генеричког податка не можемо приступити податку, па није битно ког је типа.

  • Није дозвољено негенеричком показивачу доделити вредност генеричког показивача, односно p1 = p2.

Наравно, ово су општа правила када је један показивач генерички. Проблем у програму би могао да решиш кастовањем, поступком којим смо већ појаснили: p1 = (double*)p2;

Следећи код би се успешно извршио:

#include<stdio.h>
main()
{
	double m = 18.37;
	double *p1;
	void *p2;
	p2 = &m;
	p1 = (double*)p2;
	printf("Vrednost promenljive m (preko pokazivaca p1) je %.2lf", *p1);
}

Поређење два показивача

Програмски језик С дозвољава поређење два показивача коришћењем оператора == и !=.

Поређење се врши да би се добио одговор да ли показивачи указују на исти податак у меморији или не.

У нашем примеру резултат израза if(p1 == p2) био би потврдан (израз враћа вредност 1). Наравно, израз p1 != p2 у овом случају враћа вредност 0.

Показивачи р1 и р2 имају исту вредност, односно указују на исту променљиву:

../_images/Picture13.png

Поређење показивача са нулом

Могуће је показивачу произвољног типа, укључујући и показивач типа void, доделити нулу или га поредити са нулом. Уколико показивач има вредност нула, то значи да не указује ни на који податак.

Врло често се за поређење користи симболичка константа NULL која се налази у библиотеци stdio.h и stdlib.h. Врло често ћеш ову константу користити у даљем изучавању програмирања, а посебно у базама података.

Анализирајмо поново наш пример:

int x = 100;
int *p1, *p2;
p1 = &x;

Наредбе if (p2 == 0) и if (p2 == NULL) су заправо идентичне, јер испитују да ли показивач указује на неки податак. У нашем случају показивач p2 не указује ни на један податак, за разлику од показивача p1.

Показивач р2 не показује ни на један податак:

../_images/Picture14.png

Употреби претходни пример и слику и одговори које ће вредности вратити следећи изрази:

Израз поређења

Решење

p2! = 0

0

p1 == NULL

0

p1! = 0

1

p2 == NULL

1

Сабирање и одузимање показивача и целобројног податка

Већ смо навели да је дозвољено сабирање и одузимање показивача и целобројног податка. Ово ћемо најлакше објаснити на примеру низа a[5]={7,12,36,4,18}.

Показивач p позиционираћемо на трећи елемент низа и то наредбом p = &a[2].

../_images/Picture15.png

Наредба p + 1 указује на следећи елемент, а p - 1 на претходни елемент низа:

../_images/Picture16.png

Уопшено говорећи, важи да израз p + i представља i-ти елемент иза елемента на који указује показивач, а p - i i-ти елемент испред елемента на који указује показивач.

Јединица мере при сабирању и одузимању показивача са целобројним податком јесте величина показиваног податка!

Уопштено говорећи, за израз p = p + х нова адреса р ће бити формирана као збир старе адресе p и x * sizeof (pokazani_tip).

Нека су дефинисани низ a[10] и показивач *p (истог типа, наравно) и нека показивач p упућује на a (p = &а). Ако је вредност p = 6487574 (децимално), наредбом p = p + 1 добићемо следеће вредности показивача p за различите типове података:

  • char: p = 6487575 – char заузима један бајт

  • short int: p = 6487576 – short int заузима два бајта

  • int, float: p = 6487578 – int, float заузимају четири бајта

  • double: p = 6487582 – double заузима осам бајтова

Анализирајмо сада следећи пример:

У низу путем показивача приступити елементима низа и у 4. елемент уписати вредност збира 2. и 5. елемента, а затим приказати измењене вредности низа.

На почетку треба одредити вредност показивача, на који елемент се позиционирамо. Нека то буде први елемент, р = a или p = &a[0]. Иако ће бити детаљније бити објашњено у лекцији „Показивачи и низови” запамти ове изразе:

  • &a[i]<=>a+i - адреса i-тог елемента низа

  • &a[0]<=>a - адреса првог елемента низа

  • a[i]=*(a+i) - вредност i-тог елемента низа

Сада знаш да другом елементу приступамо са p + 1, а петом са p + 4.

../_images/Picture17.png

Већ си научио да вредности на тим позицијама добијамо са *(p + 1), односно *(p + 4).

Израз за наш задатак би гласио:

*(p + 3) = *(p + 1) + *(p + 4)

Вредности измењеног низа би износиле:

../_images/Picture18.png

Решење:

#include<stdio.h>
main()
{
    int a[] = {7, 12, 36, 4, 18};
    int *p;
    p = a;
    *(p + 3) = *(p + 1)+ *(p + 4);
    printf("Novi izgled niza:\n");
    for (p = a; p < a + 5; p++)
    printf("%d", *p);
}

Анализирајмо сада претходни програм:

После измена елемента низа наредбом *(p + 3) = *(p + 1)+ *(p + 4) приступамо елементима низа на следећи начин: Наредбом p = a показивач показује на први елемент низа, а са *p чита вредност са те локације. Показивач наредбом p++ показује на следећи елемент низа, чита и штампа вредност са те локације и тако редом, све до последњег елемента низа што се дефинише наредбом p < a + 5.

Резултат извршавања програма:

Novi izgled niza:
7 12 36 30 18

Када приступаш елементима у меморији, мораш водити рачуна да приступаш само дефинисаном делу меморије, где постоје вредности. Јасно ти је да у нашем примеру наредба p + 7, на пример, нема смисла, јер вредност на тој позицији не постоји.

Реши претходни пример, али тако да се на почетку позиционираш на трећи елемент p = &a[2].

Pешење:

#include<stdio.h>
main()
{
    int a[] = {7, 12, 36, 4, 18};
    int *p;
    p = a + 2;
    *(p + 1) = *(p - 1) + *(p + 2);
    printf("Novi izgled niza:\n");
    for (p = a; p < a + 5; p++)
    printf("%d", *p);
}