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

ЛАБОРАТОРНА РОБОТА № 11

Тема: Вказівники, символьні рядки і функції.

Мета: отримання практичних навичок в роботі із вказівниками і отримання навичок роботи із функціями користувача.

Приклад рішення задачі

Функція виділяє із заданого рядка підрядок заданої довжини, починаючи із заданої позиції.

Розробка специфікацій функції

Дамо функції, яку ми створюємо, ім'я substr(). Склад і типи її параметрів досить просто можуть бути визначений із завдання: це мають бути рядок-джерело даних (src), рядок-результат (dest), початкова позиція (pos) і довжина результату (len).

Рядки мають бути визначені в тій функції, яка викликає нашу, отже, функції substr() передаються покажчики на ці рядки. Інші параметри мають бути цілими числами. Пам'ять для рядка-джерела має бути виділена в зовнішній функції. Якщо ми оголосимо рядок-результат як локальну у функції substr(), то пам'ять для неї буде виділена в нашій функції, і ця пам'ять буде звільнена, коли виконання функції закінчиться, отже, та функція, яка викликала нашу, скористатися результатом не зможе. Якщо ж ми виділимо пам'ять в нашій функції явним чином (за допомогою malloc()), то пам'ять збережеться, але тоді на ту функцію, яка викликала нашу, покладається обов'язок явним чином звільнити цю пам'ять, коли рядок-результат вже не буде потрібний. Проаналізуємо можливості некоректного завдання параметрів при виклику функції. По-перше, параметри pos і len не можуть мати негативного значення - воно просто не має сенсу. По-друге, можливе таке значення параметра pos, яке перевищуватиме довжину рядка-джерела. Вважатимемо усі ці випадки помилковим завданням параметрів. Що повинна робити функція при помилковому завданні параметрів? Можна запропонувати три варіанти:

1. Функція аварійно завершує роботу усієї програми.

2. Функція ніяк не реагує на помилку.

3. Функція повертає якусь ознаку помилки.

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

Встановимо, що це значення буде 0 при нормальній роботі функції, а при помилці в параметрах це значення буде - 1, а рядок-результат буде порожній.

У результаті розробки специфікації для функції ми формулюємо такий опис функції substr() :

int substr(

char *src

char *dest

int pos

int len);

Розробимо алгоритм для функції substr().

Функція починається з циклу (блоки 2 -5), мета якого - встановити покажчик на символ з номером pos. Як параметр циклу використовується параметр функції pos, який "працює на зменшення". У кожній ітерації циклу pos зменшується на 1 (блок 5), а покажчик src збільшується на 1 (блок 4). Коли pos зменшиться до 0 (блок 3) - покажчик має бути встановлений на потрібний символ. Але є можливість того, що ми досягнемо кінця рядка раніше, ніж символу з номером pos. Ця можливість перевіряється окремо (блок 2) - чи не показує src на символ з кодом 0 - ознака кінця рядка. Отже, вихід з циклу можливий або по досягненню потрібного символу (блок 3), або по досягненню кінця рядка, причому, останнє можливо тільки при некоректному завданні параметрів. Після виходу з циклу перевіряється коректність завдання параметрів : чи не задана негативна довжина підрядка (блок 6) і чи не виходить pos за межі рядка (блок 7). Відмітимо, що якщо значення pos помилково задане негативним, то цикл блоків 2 - 5 не виконається жодного разу і в блоці 7 значення pos буде ненульовим. Воно також буде ненульовим, якщо pos перевищує довжину рядка-джерела. У будь-якому випадку некоректного завдання повертане значення ret встановлюється в - 1 (блок 8), і управління переходжуватиме на завершення функції.

Якщо ж параметри задані коректно, виконується другий цикл (блоки 9 - 13). Цей цикл має параметром параметр функції len, який теж " працює на зменшення ". В кожній ітерації символ, на який показує покажчик src, пересилається туди, куди показує покажчик dest (блок 11) після чого обоє ці покажчика збільшуються на 1 (блок 12), а len зменшується на 1. Коли значення len досягне 0, це означає, що з джерела в результат переслано вже len символів і відбувається вихід з циклу (блок 10). Інша можливість виходу - якщо буде знайдений кінець рядка перш, ніж закінчиться пересилка (блок 9). Після виходу з циклу перевіряється залишок в змінній len (блок 14). Якщо він нульовий, значення для ret встановлюється в 0 (блок 16), якщо немає - це означає, що переслана менша кількість символів, і значення ret встановлюється в 1 (блок 17).

Перед завершенням у будь-якому випадку записується ознака кінця рядка туди, куди показує dest (блок 17). Якщо функція завершується із-за некоректного завдання параметрів, це забезпечить порожній рядок за адресою dest. Те значення ret, яке було встановлено при виконанні алгоритму, повертається функцією (блок 18).

Функція substr(). Текст програми.

Заголовок функції substr() повністю відповідає її опису, сформульованому при розробці специфікацій функції. У функції оголошується тільки одна локальна змінна - ret - в якій формується те значення, яке повертає функція.

Цикл, який на схемі алгоритму представлений блоками 2 - 5, реалізований одним оператором:

for(; pos&&*src; pos--, src++);

Початкові установки для циклу не потрібні. Обидві умови виходу з циклу перевіряються одним вираженням: pos&&*src, що еквівалентно:(*src=0), у кінці кожної ітерації зменшується pos і збільшується src. Тіло цього циклу порожнє.

Єдиний умовний оператор:

if (pos||(len<0)) ret=-1;

є програмною реалізацією перевірок некоректного завдання параметрів (блоки 6 - 8).

Якщо параметри коректні, наступний оператор циклу виконує пересилку символів з джерела в результат (блоки 9 - 13) :

for(;len&&*src; *dest++=*src++, len--);

Цей цикл теж має порожнє тіло, бо усі дії, які треба в нім виконувати задані в заголовку циклу.

Після циклу перевіряється залишок і встановлюється значення ret (блоки 14 - 16) :

ret = len ? 1 : 0;

Перед поверненням ще записується ознака кінця рядка (символ з кодом 0) в результат (блок 17) :

* *dest=0;

При завершенні функція повертає (блок 18) значення ret :

return ret;

Функція main()

Основним результатом нашого проекту має бути функція substr(). Але ця функція не може виконуватися самостійно, вона повинна викликатися з якої-небудь зовнішньої функції. Взагалі для виконання будь-якого програмного коду, написаного на мові C, в нім має бути функція main(). На цю функцію ми покладаємо завдання введення даних і виведення результатів. Отже, щоб змусити нашу функцію substr() виконуватися і перевірити її виконання, ми повинні створити також і функцію main().

Змінні функції main()

У функції main() мають бути оголошені змінні для:

  • рядка-джерела: char s1[80];

  • рядка-результату: char s2[80];

  • позиції першого символу в підрядку: int n;

  • довжини підрядка : int l;

  • того значення, яке поверне функція substr(), : int r;

Увага!!!

Звернення до символьних рядків в мові C відбувається через покажчик на початок рядка. Але таке оголошення, наприклад:

char *s1;

виділяє пам'ять тільки для розміщення покажчика, але не для розміщення самих символів рядка. Якщо далі введемо, наприклад, 80 символів функцією gets(s1), то символи розмістяться там, куди показує покажчик s1, але значення цього покажчика не визначене, отже, і символи розмістяться невідомо де. Отже, символьний рядок обов'язково має бути оголошений як масив символів - цим виділяється для неї пам'ять, а вже звертатися до неї можна через покажчик.

Не слід також забувати про необхідність резервувати в рядку додаткову позицію для ознаки кінця рядка.

Текст функції main()

Після оголошення змінних текст функції main() складається з єдиного нескінченного циклу. У кожній його ітерації передусім виводиться запрошення на введення рядка-джерела. Наступний оператор, можливо, вимагає детальнішого розгляду:

if (!strcmp(gets"(s1)***")) break;

При його виконанні передусім виконується функція gets(s1), яка вводить дані в рядок s1. Ця функція повертає покажчик на рядок s1. Рядок, на який показує цей покажчик, порівнюється за допомогою функції strcmp() із строковою константою "***". Якщо вони рівні, strcmp() повертає 0, тоді значення логічного виразу в умовному операторові істинне і виконується вихід з нескінченного циклу. Отже, цикл виконуватиметься, поки не буде введений рядок "***".

Потім вводяться значення змінних n і l і виконується виклик функції substr(), якій передаються параметри s1, s2, n, l. Значення, яке повертає substr(), привласнюється змінній r.

Рядок-джерело, рядок-результат і повернене значення виводяться на екран, і цикл повторюється. (Зверніть увагу на те, що при виведенні символьних рядків ми беремо їх в "дужки" : >> <<. Це зроблено для того, щоб на екрані можна було розгледіти символи-пропуски, які можуть бути на початку і у кінці рядків.)

Обращение к функции gets() в конце цикла - "технологическое". Дело в том, что функция scanf() оставляет в буфере ввода последний код клавиши Enter, которым был закончен ввод. Если не будет "технологического" gets(), то gets() в следующей итерации цикла прочитает этот символ, как пустую строку. Так что "технологическое" gets() удаляет из буфера код клавиши Enter. Чтобы убедиться, попробуйте его убрать и посмотрите, что получится.

5.4.3. Загальні оголошення

У функції main() застосовуються бібліотечні функції введення-виводу і функція порівняння рядків, так що включимо в програму файли stdio.h і string.h. Крім того, слід включити і опис функції substr(). Хоча усі ці описи потрібні тільки для функції main(), стиль програмування на мові C вимагає розміщення їх на початку програми, поза програмними блоками.

Повний текст програми приведений нижче.

##include <stdio.h>

##include <string.h>

int substr(char *, char *, int, int);

/*** /*** головна функція ***/

int main(void){

char s1[80],s2[80]; /* джерело і результат */

int n, l, r; /* позиція, довжина, результат */

for (;;){ /* нескінченний цикл */

printf("Введіть рядок >");

/* /* ввод строки-источника */

if (!strcmp(gets"(s1)***")) break;

/* /* ввод остальных параметров */

printf("Введиту pos len>");

scanf("%d %d",&n,&l);

/* /* обращение к функции substr() */

r=substr(s1, s2, n, l);

/* /* виведення результатів */

printf("pos =%d, len=%d\n", n, l);

printf("s 1=>>%s<<\n", s1);

printf("s 2=>>%s<<\n", s2);

printf("R =%d\n\n", r);

gets(s1);

}

}

/*** /*** функція виділення підрядка ***/

/* /* параметрів:

src - рядок-джерело

dest - рядок-результат

pos - позиція, з якою виділяється підрядок

len - довжина підрядка

функція повертає:

0 - 0 - нормальне виконання

1 - підрядок має меншу довжину, чим задано

- - 1 - помилка в параметрах, результат порожній */

int substr(char *src, char *dest, int pos, int len){

int ret; /* значення, яке повертається */

/* /* вихід на початкову позицію */

for(; pos&&*src; pos--, src++);

/* /* перевірка параметрів */

if (pos||(len<0)) ret=-1;

else { /* параметрів коректні */

/* /* пересилка символів */

for(;len&&*src; *dest++=*src++, len--);

/* /* перевірка довжини результату */

ret = len ? 1 : 0;

}

/* /* запис ознаки кінця в результат */

* *dest=0;

return ret;

}

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