Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Lab4.doc
Скачиваний:
6
Добавлен:
21.09.2019
Размер:
146.94 Кб
Скачать

Вирази з вказівниками

В цілому, вирази в яких використовують вказівники, підпорядковуються загальним правилам, як і звичайні вирази в С/С++. Розглянемо особливості виразів з вказівниками.

Присвоєння вказівників.

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

#include <stdio.h>

int main(void)

{

int x;

int *p1, *p2;

p1 = &x; //р1 отримує адресу x

p2 = p1; //p2 присвоєно адресу x використавши р1

printf("Адреса p1: %p, адреса p2: %p", p1, p2);

//виводить адресу, яка зберігається в p1,p2

return 0;

}

Арифметичні дії з вказівниками

Стосовно вказівників можна застосовувати тільки дві арифметичні операції: додавання до них цілих чисел і віднімання. Для розуміння арифметичних дій з вказівниками припустимо, що p1 – це вказівник на ціле, який містить значення 1004. Нехай ціле має довжину 2 байти. Після виразу

p1++;

вміст p1 стане 1006, а не 1005. Кожен раз при збільшенні p1 вказівник буде вказувати на наступне ціле. Це саме стосується і зменшення.

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

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

p1 = p1 + 9;

приводить до того, що вказівник p1 вказує на 9-й елемент далі в порівнянні з тим, на який він вказував до присвоєння.

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

Порівняння вказівників

Можна порівнювати два вказівники. Наприклад, якщо є вказівники p та q. Тоді справедливо записати оператор:

if (p < q) printf ("p вказує на меншу адресу в пам’яті ніж q\n");

Звичайно, порівняння двох вказівників використовується тоді, коли вони вказують на один об’єкт. Як приклад можна навести таку програму. Нехай потрібно створити функцію, яка обслуговує стек для зберігання цілочисельних значень. Стек – це список побудований за принципом “першим прийшов — останнім вийшов”. Стек часто порівнюють з горою посуду на столі – найперша тарілка, покладена на стіл, забирається останньою. Стеки часто використовуються в компіляторах, інтерпретаторах, при створенні таблиць та в іншому системному програмному забезпеченні. Для створення стеку необхідно дві функції push() та pop(). Функція push() поміщує значення в стек, а pop() витягає його. Стек містить масив stack, в якому зберігається STACKSIZE елементів. Змінна tos містить адресу пам’яті вершини стеку і використовується для запобігання переповнення і вичерпування стеку. Після ініціалізації стеку дані функції можна використати для роботи з цілими числами. Далі показані дані функції разом з простою функцією main():

#include <stdio.h>

#include <stdlib.h>

#define STACKSIZE 50

void push (int i);

int pop(void);

int *p1, *tos, stack[STACKSIZE];

int main(void)

{

int value;

p1 = stack; //присвоює р1 початок стеку

tos = p1; //tos містить вершину стеку

do {

printf("Enter a number (-1 to quit, 0 to pop): ");

scanf("%d", &value);

if (value!=0) push (value);

else printf ("this is it %d\n", pop());

} while(value!=-1);

return 0;

}

void push(int i)

{

p1++;

if (p1==(tos + STACKSIZE)) {

printf("stack overflow");

exit(1);

}

*p1 = i;

}

int pop(void)

{

if (p1==tos) {

printf("stack underflow");

exit(1); }

p1 --;

return *(p1+1);

}

Як push() так і pop() перевіряють вказівник р1 для знаходження помилок переповнення або вичерпування стеку. В push() р1 порівнюється з кінцем стеку, який отримуємо шляхом додавання, а в pop() p1 порівнюється з tos, щоб переконатися що стек не пустий.

В pop() в операторі return необхідно круглі дужки. Якщо їх забрати, то оператор буде виглядати так:

return *p1 + 1;

і він буде видавати значення, яке міститься по адресі р1, збільшене на 1, а не значення за адресою р1+1. Слід бути дуже уважним при використанні круглих дужок для досягнення правильного порядку обчислення виразів з вказівниками.

Часто вказівники використовуються для організації черг. Черга — це лінійний список, побудований за принципом “першим прийшов — першим вийшов” (звичайна черга чекання біля каси тощо). В чергу елементи додаються в кінці списку, а вилучаються — на початку. Аналогічно до стеків можна організувати послідовне зберігання черги у масиві. При реалізації черги в масиві d довжиною M, її перший елемент розміщується на початку d. Для фіксації значень індексів початку і кінця черги використовуються цілі змінні a і b з початковими значеннями a=0; b=–1. Вилучення елемента з непустої черги здійснюється оператором v=d[a++]; додавання елемента в чергу — оператором d[++b]=v; . При роботі слід перевіряти, чи черга не стала порожньою: a>b; та чи ми не досягли кінця масиву: b=M-1.

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