русс | укр

Мови програмуванняВідео уроки php mysqlПаскальСіАсемблерJavaMatlabPhpHtmlJavaScriptCSSC#DelphiТурбо Пролог

Компьютерные сетиСистемное программное обеспечениеИнформационные технологииПрограммирование


Linux Unix Алгоритмічні мови Архітектура мікроконтролерів Введення в розробку розподілених інформаційних систем Дискретна математика Інформаційне обслуговування користувачів Інформація та моделювання в управлінні виробництвом Комп'ютерна графіка Лекції


Покажчики та масиви


Дата додавання: 2014-11-28; переглядів: 909.


У C існує тісний зв'язок між покажчиками і масивами, настільки тісний, що покажчики і масиви повинні обговорюватись одночасно. Будь-яку дію, що можна здійснити через індексацію масиву, можна також через покажчики. Версія з покажчиками буде дещо швидшою, але, принаймні для непосвячених, також трохи важчою у розумінні.

Оголошення

int a[10];

означує масив розміром у 10 елементів, тобто блок з 10-и суміжних об'єктів з назвами a[0],a[1], ..., a[9].

+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+

a: | | | | | | | | | | |

+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+

a[0] a[1] a[9]

Позначення a[i] означає i-ний елемент масиву. Якщо pa, це покажчик на ціле, оголошений як

int *pa;

тоді присвоєння

pa = &a[0];

змушує pa вказувати на елемент з індексом нуль масиву a, тобто pa міститиме адресу a[0].

pa:

+-----+

| |

+---|-+

v

+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+

a: | | | | | | | | | | |

+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+

a[0] a[1] a[9]

Тепер присвоєння

x = *pa;

копіює вміст a[0] до x.

Якщо pa вказує на певний елемент масиву, тоді певна річ, що pa+1 вказуватиме на наступний елемент, тоді як pa+n вказуватиме на елемент n після pa, тоді як pa-n — на елемент n попереду. Таким чином, якщо pa вказує на a[0], тоді

*(pa+1)

посилається на вміст a[1], pa+n є адресою a[n], а *(pa+n) - вмістом a[n].

pa: pa+1: pa+2:

+-----+ | |

| | | |

+---|-+ | |

v v v

+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+

a: | | | | | | | | | | |

+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+

a[0] a[1] a[9]

Ці зауваження дійсні, незалежно від типу або розміру змінних масиву a. Зміст виразу «додати 1 до покажчика», і взагалі вся арифметика покажчиків зводиться до того, що pa+1 вказує на наступний об'єкт, тоді як pa+n вказує на n-ний об'єкт поза pa.

Відповідність між індексацією й арифметикою покажчиків дуже близька. За визначенням, значення змінної або виразу типу "масив" - це адреса елемента нуль масиву. Таким чином, після присвоєння

pa = &a[0];

pa й a матимуть абсолютно однакові значення. Оскільки назва масиву являється лише синонімом місцезнаходження початкового елемента, присвоєння pa=&a[0] можна з таким самим успіхом написати як

pa = a;

Швиде несподіванішим, з першого погляду, може здатися той факт, що посилання на a[n]можна також записати як *(a+n). Обчислюючи a[n], C сама перетворює його в *(a+n) — ці дві форми еквівалентні. Застосовуючи оператор & до обох частин цієї еквівалентності, ми дійдемо висновку, що &a[n] й a+n також однакові: a+n є адресою n-ного елемента після a. З іншого боку, якщо pa є покажчиком, він міг би використовуватись з індексом: pa[n], що рівнозначно*(pa+n). Одним словом, вираз масив-та-індекс рівнозначний тому, що записано як покажчик і зміщення.

Існує одна відмінність між назвою масиву і покажчиком, яку слід пам'ятати. Справа в тім, що покажчик — це змінна, тож pa=a і pa++ дозволено. Проте назва масиву не є мінною, тож конструкції на зразок a=pa або a++ заборонено.

Коли функції передано назву масиву, що передається насправді, це місцезнаходження першого елемента. Всередині викликаної функції цей аргумент стає локальною змінною, тож назва масиву, як параметр, насправді являється покажчиком — тобто змінною, що містить адресу. Ми можемо використати цей факт для написання іншої версії strlen — функції, що обчислює довжину ланцюжка.

/* strlen: повертає довжину ланцюжка s */

int strlen(char *s)

{

int n;

 

for (n = 0; *s != '\0', s++)

n++;

return n;

}

Оскільки s — це покажчик, то приріст його дозволено; s++ не матиме жодного впливу на символьний ланцюжок функції, яка викликала strlen — лише здійснює приріст приватної копії покажчика в strlen. Це означає, що всі виклики на зразок

strlen("hello, world"); /* ланцюжкова стала */

strlen(array); /* char array[100]; */

strlen(ptr); /* char *ptr; */

працюватимуть.

Формально, при визначенні функції, параметри

char s[];

i

char *s;

рівнозначні. Ми надаємо перевагу останньому, оскільки він відвертіше вказує на те, що ця змінна є покажчиком. Коли назву масиву передано функції, остання може, залежно від того як їй зручніше, вважати, що їй передано або масив, або покажчик, і опрацьовувати його відповідно. Вона навіть може вживати обидві нотації, якщо це здається їй слушним і зрозумілим.

Можливо також передати функції лише частину масиву, вказавши покажчиком на початок частини масиву. Наприклад, якщо a — це масив, то

f(&a[2])

і

f(a+2)

обидва, передають функції f адресу частини масиву, яка починається з a[2]. У самій f, оголошення параметрів може звучати як

f(int arr[]) { ... }

або

f(int *arr) { ... }

Тож, що стосується f, сам факт того, що параметр посилається на частину більшого масиву не має значення.

Якщо впевнені, що елементи існують, можливо також вказати індекс у зворотньому напрямку у масиві: p[-1], p[-2] і так далі. Це синтаксично допустимо, і вказує на елементи попередуp[0]. Звичайно, забороняється посилатися на об'єкти поза межами масиву.


<== попередня лекція | наступна лекція ==>
Покажчики й аргументи функцій | Арифметика адрес


Онлайн система числення Калькулятор онлайн звичайний Науковий калькулятор онлайн