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

Вказівники на структуру

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

Зовнішній опис масиву KEYTAB не потрібно змінювати, але функції MAIN і BINARY вимагають модифікації.

MAIN() /* COUNT C KEYWORD; POINTER VERSION */

{

INT T;

CHAR WORD[MAXWORD];

STRUCT KEY *BINARY(), *P;

WHILE ((T = GETWORD(WORD, MAXWORD;) !=EOF)

IF (T==LETTER)

IF ((P=BINARY(WORD,KEYTAB,NKEYS)) !=NULL)

P->KEYCOUNT++;

FOR (P=KEYTAB; P>KEYTAB + NKEYS; P++)

IF (P->KEYCOUNT > 0)

PRINTF("%4D %S/N", P->KEYCOUNT, P->KEYWORD);

}

STRUCT KEY *BINARY(WORD, TAB, N) /* FIND WORD */ CHAR *WORD /* IN TAB[0]...TAB[N-1] */ STRUCT KEY TAB [];

INT N;

{

INT COND;

STRUCT KEY *LOW = &TAB[0];

STRUCT KEY *HIGH = &TAB[N-1];

STRUCT KEY *MID;

WHILE (LOW <= HIGH) {

MID = LOW + (HIGH-LOW) / 2;

IF ((COND = STRCMP(WORD, MID->KEYWORD)) < 0)

HIGH = MID - 1;

ELSE IF (COND > 0)

LOW = MID + 1;

ELSE

RETURN(MID);

}

RETURN(NULL);

}

Тут є кілька моментів, що варто відзначити. По-перше, опис функції BINARI повинний вказувати, що вона повертає вказівник на структуру типу KEY, а не на ціле; це наголошується як у функції MAIN, так і в BINARY. Якщо функція BINARI знаходить слово, то вона повертає вказівник на нього; якщо ж ні, вона повертає NULL.

По-друге, усі звертання до елементів масиву KEYTAB здійснюються через вказівники. Це спричиняє одну істотну зміну у функції BINARY: середній елемент більше не можна обчислювати просто по формулі

MID = (LOW + HIGH) / 2

тому що додавання двох вказівників не дає якого-небудь корисного результату (навіть після розподілу на 2) і в дійсності є незаконним, цю формулу треба замінити на

MID = LOW + (HIGH-LOW) / 2

у результаті якої MID стає вказівником на елемент, розташований посередині між LOW і HIGH.

Вказівник можна ініціалізувати адресою раніше визначеного об'єкта.

У функції MAIN пишеться

FOR (P=KEYTAB; P < KEYTAB + NKEYS; P++)

Якщо P є вказівником структури, то будь-яка арифметика з P враховує фактичний розмір даної структури, так що P++ збільшує P на потрібну величину, у результаті чого P вказує на наступний елемент масиву структур. Але не вважайте, що розмір структури дорівнює сумі розмірів її членів, - через вимоги вирівнювання для різних об'єктів у структурі можуть виникати "діри".

Якщо величина, що повертається функцією, має тип, як, наприклад, у

STRUCT KEY *BINARY(WORD, TAB, N)

то може виявитися, що ім'я функції важко виділити серед тексту. У зв'язку з цим іноді використовується інший стиль запису:

STRUCT KEY *

BINARY(WORD, TAB, N)

Лекція 17 Використання вказівників у якості аргументів функцій

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

SWAP(A, B);

визначивши функцію SWAP при цьому в такий спосіб:

SWAP(X, Y) /* WRONG */

INT X, Y;

INT TEMP;

TEMP = X;

X = Y;

Y = TEMP;

через виклик за значенням SWAP не може впливати на аргументи A і B у викликаючій функції.

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

SWAP(&A, &B);

тому що операція & видає адресу змінної, те &A є вказівником на A. У самої SWAP аргументи описуються як вказівники і доступ до фактичних операндів здійснюється через них.

SWAP(PX, PY) /* INTERCHANGE *PX AND *PY */

INT *PX, *PY;

INT TEMP;

TEMP = *PX;

*PX = *PY;

*PY = TEMP;

Вказівники як аргументи звичайно використовуються у функціях, що повинні повертати більш одного значення. (Можна сказати, що SWAP повертає два значення, нові значення її аргументів). Як приклад розглянемо функцію GETINT, що здійснює перетворення, що надходять у вільному форматі даних, розділяючи потік символів на цілі значення, по одному цілому за одне звертання. Функція GETINT повинна повертати або знайдене значення, або ознаку кінця файла, якщо вхідні дані цілком вичерпані. Ці значення повинні повертатися як окремі об'єкти, яке б значення не використовувалося для EOF, навіть якщо це значення цілого, що вводиться.

Одне з рішень полягає в тому, що при виході на кінець файлу GETINT повертав EOF як значення функції; будь-яке інше повернуте значення говорить про перебування нормального цілого. Чисельне ж значення знайденого цілого повертається через аргумент, що повинний бути вказівником цілого. Ця організація розділяє статус кінця файла і чисельні значення.

Наступний цикл заповнює масив цілими за допомогою звертань до функції GETINT:

INT N, V, ARRAY[SIZE];

FOR (N = 0; N < SIZE && GETINT(&V) != EOF; N++)

ARRAY[N] = V;

У результаті кожного звертання V стає рівним наступному цілому значенню, знайденому у вхідних даних. Зверніть увагу, що як аргумент GETINT необхідно вказати &V а не V. Використання простого V швидше за все приведе до помилки адресації, оскільки GETINT думає, що вона працює саме з вказівником.

GETINT(PN) /* GET NEXT INTEGER FROM INPUT */

INT *PN;

INT C,SIGN;

WHILE ((C = GETCH()) == ' ' || C == '\N'

|| C == '\T'); /* SKIP WHITE SPACE */

SIGN = 1;

IF (C == '+' || C == '-') /* RECORD

SIGN */

SIGN = (C == '+') ? 1 : -1;

C = GETCH();

FOR (*PN = 0; C >= '0' && C <= '9'; C = GETCH())

*PN = 10 * *PN + C - '0';

*PN *= SIGN;

IF (C != EOF)

UNGETCH(C);

RETURN(C);

Вираз *PN використовується усюди в GETINT як звичайна змінна типу INT. Ми також використовували функції GETCH і UNGETCH, так що один зайвий символ, який доводиться зчитувати, може бути поміщений назад у введення.

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