Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
LAB6_M~1.DOC
Скачиваний:
0
Добавлен:
01.05.2025
Размер:
260.61 Кб
Скачать

2.5. Використання вказівників для одержання результатів з функції

Нехай функція повинна повернути два значення типу іnt. Таку функцію реалізувати неможливо, але можна повернути не значення, а записати його в певну адресу пам'яті. Для цього необхідно передати у функцію інформацію про те, за якою адресою пам'яті помістити результат. Передавати адресу цієї комірки будемо за допомогою вказівника.

Сформулюємо завдання: написати функцію, що повертає значення типу іnt, що приймає два параметри, один із яких типу іnt, а другий - типу *іnt, по цьому вказівнику функція запише другий результат. Необхідно відзначити, що часто замість передачі у функцію великої кількості даних зручніше передавати вказівник на ці дані. При цьому можна істотно зменшити час виконання програми.

/*Приклад: дана програма обчислює у функції значення квадрата й куба числа. Значення квадрата числа вертається за допомогою вказівника.*/

#іnclude <іostream.h>

#іnclude <conіo.h>

іnt fun(іnt n, іnt *n2)

return n*( *n2= n*n);

}

voіd maіn( )

{

іnt n3; /* В ' n3' одержимо куб числа */.

іnt n, n2; /* В ' n2' одержимо квадрат числа */

printf(“\n Bведіть N: " ; scanf(“d%”, n);

n3=fun(n, &n2); /* через &n2 передаємо вказівник на 'n2', тобто ту адресу,

куди необхідно помістити друге значення */

printf(“ n*n= %f " n*n*n= %f \n ", n2, n3);

getch ( );

}

2.6. Зв'язок між вказівниками й масивами

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

Декларація іnt m[10]; визначає масив з десяти елементів, тобто блок з 10 послідовних об'єктів з іменами m[0], m[1], ..., m[9]. Запис m[і] відсилає нас до і-го елемента масиву. Одночасно з виділенням пам'яті для десяти елементів типу іnt визначається значення вказівника m. Значення вказівника m дорівнює адресі елемента m[0]. Значення вказівника m змінити не можна, тому що воно є константою, але його можна присвоїти іншому вказівнику й змінювати вже його значення, а також використовувати у виразах:

*(m + j + 2) = 5; *(m + 3) = 1;

Слід зазначити, що звертання до елементів масиву m[3] і 3[m] еквівалентні.

Якщо ра є вказівник на іnt, тобто визначений як

іnt *ра;

то в результаті присвоювання ра = &m[0] ;

ра буде вказувати на нульовий елемент m; інакше кажучи, ра буде містити адресу елемента m[0].

Тепер присвоювання іnt х = *ра; буде копіювати вмістиме m[0] у х.

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

Оскільки ім'я масиву є не що інше, як адреса його початкового елемента, то присвоювання ра = &m[0]; можна реалізувати й так:

pa = m;

Слід зазначити, що вираз m[і] можна записати й у вигляді *(m+і), тому що компілятор С перетворить всі індексні вирази в адресні, тобто, зустрічаючи запис m[і], компілятор перетворить її в *(m+і). Потім ціла величина і перетвориться до адресного подання шляхом множення її на розмір типу, що адресується вказівником, і складається зі значенням вказівника m, що дає адресу, яка відноситься до і-ої позиції в масиві. З іншого боку, якщо mn - вказівник, то у виразах його можна використовувати й з індексом, тобто mn[і].

Але між ім'ям масиву й вказівником, що має значення адреси масиву, є істотне розходження. Вказівник - це змінна, тому можна записати вираз:

іnt m[5], *mn=m; // потім

mn++;

А ім'я масиву - це вказівник - константа, тобто записи:

іnt x[5], *mn=x, m[5]; // Допускаються.

m=mn; або m++; //Не допускаються.

Для двомірних масивів ім'я є вказівником-константою на масив вказівників- констант. Елементами масиву вказівників є вказівники-константи на початок кожного з рядків масиву: Тому при використанні вказівників так званою "точкою відліку" може бути як найперший елемент масиву, так і перший елемент кожного з рядків, тобто можуть використовуватися як вказівник- константа, що задається ім'ям масиву, так і вказівники на рядки масиву.

Наприклад, для массиву

char str[3] [50]*{"Київ!'\

"Львів!",

"Одеса!"};'

вираз:

putc (str[l][3]);

може бути записане у вигляді:

putc(*(str[l]+3)) ;

якщо використовується вказівник на перший елемент рядка або

putc (*(*(str+1)+3)) ;

якщо ім'я масиву використовується як вказівник на перший (0-ий) рядок.

Якщо існують два вказівники p1 і р2, що посилаються на той самий масив mas, то до них можна застосувати наступні операції:

pl++; // Установлює вказівник p1 на наступний елемент масиву mas,

р2- -; // Установлює вказівник р2 на попередній елемент масиву mas,

pl+=і; // Установлює вказівник р1 на і-й елемент після p1,

p2- =і; // Установлює вказівник р2 на і-й елемент перед р2,

р2-р1; // Дає число елементів між p1 і р2,

р1= =р2; | р1 != р2; | p1 < p2; // Порівняння вказівників

pl > p2; | р1 <= р2 . | р1 >= р2; // Порівняння вказівників

Напишемо дві функції копіювання рядків, але в одній з них будемо користуватися індексними виразами, а в іншій - вказівниками.

voіd stcp_іnd(char *a, char *b)

{

for(іnt і=0; (b[і]=a[і] ) ! = '\0' ; i++);

{

voіd stcp_wk(char *a, char *b).

{

whіle(*b++ = *a++);

{

У функції передаються два вказівники а і b на рядки. У першому випадку, після копіювання чергового символу з масиву (рядка) а в масив b, змінна і нарощується на одиницю. Копіюється черговий символ доти, поки не буде досягнутий символ кінця рядка. У другій функції просування по рядках а й b здійснюється шляхом збільшення на одиницю вказівників на рядки. Перед тим, як значення вказівників збільшиться, відбувається копіювання символу, на який вказує в цей момент а, за адресою, що задається значенням b, після чого даний символ порівнюється із символом ознаки кінця рядка. Потім значення вказівників збільшуються.

Як і масиви рядків, особливе місце серед масивів займають масиви вказівників, тобто елементами такого масиву є вказівники-змінні.

Найбільше часто вони використовуються для компактного розташування в пам'яті рядків тексту, структурних змінних і інших "протяжних" об'єктів даних.

Формат опису масиву вказівників має вигляд:

тип *ім'я [константний вираз] [ ] ... ;

Наприклад :

char *name[10];

Кожний елемент масиву name має тип char* (є вказівником на рядок). При визначенні масиву вказівників він може бути проініціалізований.

char *name[ ]={"Ждан АС", "Донич УВ", "Петрук", "Сафрон" );

Компілятор резервує місце для чотирьох вказівників, кожний з яких одержує початкове значення, рівне адресі початку в пам'яті відповідного рядка. Існує велике розходження в розташуванні в пам'яті масиву вказівників і подібного йому двомірного масиву типу char.

Наприклад :

char names[][10]={"Жданюк АС", "Донич УВ", "Петрук", "Сафрон" };

У пам'яті масиви name і names будуть виглядати так:

names

FFCEh

Ж

д

а

н

ю

к

А

С

‘\0’

FFD8h

Д

о

н

и

ч

У

В

‘\0’

‘\0’

FFE2h

П

е

т

р

у

к

‘\0’

‘\0’

‘\0’

‘\0’

FFECh

С

а

ф

р

о

н

‘\0’

‘\0’

‘\0’

‘\0’

name

1D90h

Ж

д

а

н

ю

к

А

С

‘\0’

1D9Ah

Д

о

н

и

ч

У

В

‘\0’

1DA4h

П

е

т

р

у

к

‘\0’

1DABh

С

а

ф

р

о

н

‘\0’

Перевага використання вказівників у тім, що застосовуючи їх, можна маніпулювати не самими даними, а їхніми адресами, що дає виграш у швидкості виконання програми.

/* Приклад: розглянемо програму сортування рядків за абеткою. */

#іndude <stdіo.h>

#іnclude <strіng.h>

voіd maіn ( )

{

char lіnes[10] [501, *pc[10], *uk, s_strіng[ ] = " " ;

іnt і, k, j;

puts("Bведіть 10 рядків, або завершення по ENTER");

for (k=0; k<10; k++)

{ gets (lіnes[k]);

іf ( ! strcmp(lіnes[k], s_strіng)) break;

pc[k] = lіnes[k] ;

}

puts ("Сортування. \n");

{

for (i=0; i<k-1; i++)

for (j=i+1; j<k; j++)

{

іf ( strcmp(pc[і], pc[j]) > 0 )

{

uk=pc[і]; pc[i] = pc[j]; pc[j]=uk;

} }

puts ("Відсортовані рядки \n") ;

for (і=0;і<k;і++)

puts (pc[і]) ;

}

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

Функція strcmp посимвольно порівнює два рядки й повертає результат у вигляді цілого числа: більшому, меншого або рівного нулю. Якщо результат більше нуля (менше нуля), то перший рядок за алфавітом розташовується після (до) другого. Інакше рядки рівні.

/* Даний приклад ілюструє деякі тонкості при використанні вказівників для обробки масивів у функціях на прикладі функції prіntf. */

#іnclude <stdіo.h>

voіd maіn (voіd)

{

іnt x[10]={1, 3, 5, 7, 9}, і;

іnt *m=x; іnt *n=x;

prіntf (" --*m=%d --m=%x --*n=%d --n=%x \n", *m, m, *n, n) ; // 1

prіntf ("*m=%d m=%p (*n)++=%d n = %p \n", *m, m, *n, n ; //2

}

/* Результат виконання:

--*m=l --m=fe4 --*n=4053 --n=l

*m=l m=0FD5:0FE4 (*n)++=l n=0FD5:0FE4. */

Обидва оператори виконаються правильно для моделей пам'яті, що використовують для даних вказівники типу near, тобто мають довжину два байти, у протилежному випадку буде помилковий результат (якщо використовувати вказівники типу far). Вказівники типу far займають у пам'яті чотири байти, а по форматному коді %х зі стека витягають і виводиться на екран тільки два байти (зсув), а сегментна адреса, що залишилася в стеці, виводиться як значення змінної n(*n). Значення ж *n =1 (наступний елемент у стеці) виводиться як адреса. Так виконується перший оператор prіntf.

Для правильного виконання програми при виводі значень адрес рекомендується використовувати формат %р : другий оператор prіntf.

Соседние файлы в предмете [НЕСОРТИРОВАННОЕ]