Студопедия

КАТЕГОРИИ:


Архитектура-(3434)Астрономия-(809)Биология-(7483)Биотехнологии-(1457)Военное дело-(14632)Высокие технологии-(1363)География-(913)Геология-(1438)Государство-(451)Демография-(1065)Дом-(47672)Журналистика и СМИ-(912)Изобретательство-(14524)Иностранные языки-(4268)Информатика-(17799)Искусство-(1338)История-(13644)Компьютеры-(11121)Косметика-(55)Кулинария-(373)Культура-(8427)Лингвистика-(374)Литература-(1642)Маркетинг-(23702)Математика-(16968)Машиностроение-(1700)Медицина-(12668)Менеджмент-(24684)Механика-(15423)Науковедение-(506)Образование-(11852)Охрана труда-(3308)Педагогика-(5571)Полиграфия-(1312)Политика-(7869)Право-(5454)Приборостроение-(1369)Программирование-(2801)Производство-(97182)Промышленность-(8706)Психология-(18388)Религия-(3217)Связь-(10668)Сельское хозяйство-(299)Социология-(6455)Спорт-(42831)Строительство-(4793)Торговля-(5050)Транспорт-(2929)Туризм-(1568)Физика-(3942)Философия-(17015)Финансы-(26596)Химия-(22929)Экология-(12095)Экономика-(9961)Электроника-(8441)Электротехника-(4623)Энергетика-(12629)Юриспруденция-(1492)Ядерная техника-(1748)

Auto, extern, register, static, mutable 7 страница




7.1.3. Формальні параметри

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

Необхідно пам'ятати! Незважаючи на те, що формальні параметри виконують спеціальне завдання отримання значень аргументів, їх можна використовувати подібно до будь-яких інших локальних змінних. Наприклад, параметру усередині функції можна присвоїти яке-небудь нове значення.

7.1.4. Глобальні змінні

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

Якщо глобальна і локальна змінні мають однакові імена, то перевага знаходиться на стороні локальної змінної. Іншими словами, локальна змінна приховає глобальну з таким самим іменем. Таким чином, незважаючи на те, що до глобальної змінної теоретично можна отримати доступ з будь-якого коду програми, практично це можливо тільки у випадку, якщо однойменна локальна змінна не перевизначить глобальну.

Використання глобальних змінних продемонстровано у наведеному нижче коді програми. Як бачимо, змінні count і num_right оголошені поза всіма функціями, отже, вони глобальні. Із звичайних практичних міркувань краще оголошувати глобальні змінні ближче до початку програми. Але формально вони просто мають бути оголошені до їх першого використання. Пропонована для перегляду програма – тільки простий тренажер з виконання арифметичного додавання. Спочатку користувачу запропоновано вказати кількість вправ. Для виконання кожної вправи викликається функція drill(), яка генерує два випадкові числа в діапазоні від 0 до 99. Користувачу пропонується додати ці числа, а потім перевіряється відповідь. На кожну вправу дається три спроби. В кінці програма відображає кількість правильних відповідей. Зверніть особливу увагу на глобальні змінні, що використовуються у цій програмі.

Код програми 7.5. Демонстрація програми-тренажера з виконання операції додавання

#include <iostream> // Для потокового введення-виведення

#include <cstdlib> // Для використання бібліотечних функцій

using namespace std; // Використання стандартного простору імен

 

void drill();

 

int count; // Змінні count і num_right – глобальні.

int num_right;

 

int main ()

{

cout << "Скільки практичних вправ: ";

cin >> count;

 

num_right = 0;

do {

drill();

count--;

} while (count);

cout << "Ви дали " << num_right << " правильних відповідей.\n";

 

getch (); return 0;

}

 

void drill()

{

int count; /* Ця змінна count -- локальна і ніяк

не пов'язана з однойменною глобальною. */

int a, b, ans;

// Генеруємо два числа між 0 і 99.

a = rand () % 100;

b = rand () % 100;

// Користувач отримує три спроби дати правильну відповідь.

for (count = 0; count<3; count++) {

cout << "Скільки буде " << а << " + " << b << "? ";

cin >> ans;

if (ans == a + b) {

cout << "Правильно.\n";

num_right++;

return;

}

}

cout << "Ви використовували всі свої спроби.\n";

cout << "Відповідь дорівнює " << a + b << "\n";

}

При уважному вивченні цієї програми Вам стане зрозуміло, що як функція main (), так і функція drill() отримують доступ до глобальної змінної num_right. Але із змінною count справа йде дещо складніше. У функції main () використовується глобальна змінна count. Проте у функції drill() оголошується локальна змінна count. Тому тут під час використання імені count маємо на увазі саме локальну, а не глобальну змінну count. Необхідно пам'ятати, що, коли у функції глобальна і локальна змінні мають однакові імена, то під час звернення до цього імені маємо на увазі локальну, а не глобальну змінну.

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

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

● використання глобальної змінної в "ролі", з якою легко б "справилася" локальна змінна, робить таку функцію менш універсальною, оскільки вона покладається на необхідність визначення даних поза цією функцією;

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

Основна проблема, характерна для розроблення великих С++-програм, випадкове модифікування значення змінної у якомусь іншому місці програми. Чим більше глобальних змінних у програмі, тим більшою є ймовірність помилки.

7.2. Передача покажчиків і масивів як аргументів функціям

Дотепер у прикладах, наведених тут, функціям передавалися значення простих змінних. Але можливі ситуації, коли як аргументи необхідно використовувати покажчики і масиви. Вивченню особливостей передачі таких аргументів функцій і присвячено наступні підрозділи.

7.2.1. Виклик функцій з покажчиками

У мові програмування C++ дозволено передавати функції покажчики як аргументи. Для цього достатньо оголосити параметр типу покажчика. Розглянемо такий приклад.

Код програми 7.6. Демонстрація механізму передачі функції покажчика: початкова версія

#include <iostream> // Для потокового введення-виведення

using namespace std; // Використання стандартного простору імен

 

void fun(int *jzm);

 

int main ()

{

int izm;

int *p;

 

p = &izm; // Покажчик p тепер містить адресу змінної izm.

fun(p);

 

cout << izm; // Змінна izm тепер містить число 100.

 

getch (); return 0;

}

 

void fun(int *jzm)

{

*jzm = 100; // Змінній, яка адресується покажчиком jzm,

// присвоюється число 100.

}

Як бачите, у цій програмі функція fun() приймає один параметр: покажчик на цілочисельне значення. У функції main () покажчику р присвоюється адреса змінної izm. Потім з функції main () викликається функція fun(), а покажчик р передається їй як аргумент. Після того, як параметр-покажчик jzm набуде значення аргументу р, він (так само, як і р) указуватиме на змінну izm, що визначається у функції main (). Таким чином, у процесі виконання операції присвоєння *jzm = 100; змінна izm набуває значення 100. Тому програма відобразить на екрані число 100. У загальному випадку наведена тут функція fun() присвоює число 100 змінній, адреса якої була передана цій функції як аргумент.

У попередньому прикладі необов'язково було використовувати змінному р. Замість неї під час виклику функції fun() достатньо використовувати змінну izm, якій передував оператором "&" (при цьому, як уже зазначалося вище, генерується адреса змінної izm). Після внесення зумовленої зміни попередня програма набуває такого вигляду:

Код програми 7.7. Демонстрація механізму передачі покажчика функції: виправлена версія

#include <iostream> // Для потокового введення-виведення

using namespace std; // Використання стандартного простору імен

 

void fun(int *jzm);

 

int main ()

{

int izm;

fun(&izm);

cout << izm;

 

getch (); return 0;

}

 

void fun(int *jzm)

{

*jzm = 100; // Змінній, яка адресується покажчиком jzm,

// присвоюється число 100.

}

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

7.2.2. Виклик функцій з масивами

Якщо масив є аргументом функції, то необхідно розуміти, що під час виклику такої функції їй передається тільки адреса першого елемента масиву, а не повна його копія[28]. Це означає, що оголошення параметра повинно мати тип, сумісний з типом аргумента. Взагалі існує три способи оголосити параметр, який приймає покажчик на масив. По-перше, параметр можна оголосити як масив, тип і розмір якого збігається з типом і розміром масиву, використовуваного під час виклику функції. Цей варіант оголошення параметра-масиву продемонстровано в наведеному нижче прикладі.

Код програми 7.8. Демонстрація способу оголошення параметра-масиву

#include <iostream> // Для потокового введення-виведення

using namespace std; // Використання стандартного простору імен

 

void display(int num[10]);

 

int main ()

{

int t[10], i;

 

for (i=0; i<10; ++i) t[i] =i;

 

display(t); // Передаємо функції масив t.

 

getch (); return 0;

}

 

// Функція виводить усі елементи масиву.

void display(int num[10])

{

int i;

 

for (i=0; i<10; i++) cout << num[i]<< "\n";

}

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

Другий спосіб оголошення параметра-масиву полягає в його представленні у вигляді безрозмірного масиву, як це показано нижче:

void display(int num[])

{

int i;

 

for (i=0; i<10; i++) cout << num[i] << "\n";

}

У цьому записі параметр num оголошується як цілочисельний масив невідомого розміру. Оскільки мова C++ не забезпечує перевірки порушення меж масиву, то реальний розмір масиву не релевантний чинник для подібного параметра (але, безумовно, не для програми в цілому). Цілочисельний масив при такому способі оголошення також автоматично перетвориться С++-компілятором у покажчик на цілочисельне значення.

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

void display(int *num)

{

int i;

 

for (i=0; i<10; i++) cout << num[i]<< "\n";

}

Можливість такого оголошення параметра (у цьому випадку num) пояснюється тим, що будь-який покажчик (подібно до масиву) можна індексувати за допомогою символів квадратних дужок ([]). Таким чином, всі три способи оголошення параметра-масиву приводяться до однакового результату, який можна виразити одним словом: покажчик.

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

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

#include <iostream> // Для потокового введення-виведення

using namespace std; // Використання стандартного простору імен

 

void display(int num);

 

int main ()

{

int tMas[10], i;

 

for (i=0; i<10; ++i) tMas[i] =i;

for (i=0; i<10; i++) display(tMas[i]);

 

getch (); return 0;

}

 

// Функція виводить одне число.

void display(int num)

{

cout << num << "\n";

}

Як бачите, параметр, який використовується функцією display(), має тип int. Тут не важливо, що ця функція викликається з використанням елемента масиву, оскільки їй передається тільки один його елемент.

Необхідно пам'ятати! Коли масив використовується як аргумент функції, то функції передається адреса цього масиву. Це означає, що код функції може потенційно з|мінити| реальний вміст масиву, використовуваного під час виклику функції.

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

Код програми 7.10. Демонстрація перетворення значень елементів масиву

#include <iostream> // Для потокового введення-виведення

using namespace std; // Використання стандартного простору імен

void Cube(int *n, int num);

 

int main ()

{

int i, tMas[10];

 

for (i=0; i<10; i++) tMas[i] = i+1;

cout << "Початковий вміст масиву: ";

for (i=0; i<10; i++) cout << tMas[i] << "\n";

cout << "\n";

 

Cube(tMas, 10); // Обчислюємо куби значень.

 

cout << "Змінений вміст: ";

for (i=0; i<10; i++) cout << tMas[i] << "\n";

 

getch (); return 0;

}

 

void Cube(int *n, int num)

{

while (num) {

*n = *n * *n * *n;

num--;

n++;

}

}

Результати виконання цієї програми є такими:

Початковий вміст масиву: 1 2 3 4 5 6 7 8 9 10

Змінений вміст: 1 8 27 64 125 216 343 512 729 1000

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

7.2.3. Передача функціям рядків

Як зазначалося вище, рядки у мові програмування C++ – це звичайні символьні масиви, які завершуються нульовим символом. Таким чином, під час передачі функції рядка реально передається тільки покажчик (типу char *) на початок цього рядка. Розглянемо, наприклад, наведену нижче програму. У ній визначається функція StrUpper(), яка перетворить рядок символів у її прописний еквівалент.

Код програми 7.11. Демонстрація механізму передачі функції рядка

#include <iostream> // Для потокового введення-виведення

#include <cstring> // Для роботи з рядковими типами даних

#include <cctype> // Для роботи з символьними аргументами

using namespace std; // Використання стандартного простору імен

void StrUpper(char *str);

 

int main ()

{

char strMas[80];

 

strcpy (strMas, "Мені подобається мова програмування C++");

 

StrUpper(strMas);

cout << strMas; // Відображаємо рядок з використанням

// прописного написання символів.

getch (); return 0;

}

 

void StrUpper(char *str)

{

while (*str) {

*str = toupper (*str); // Отримуємо прописний

// еквівалент одного символу.

str++; // Переходимо до наступного символу.

}

}

Результати виконання цієї програми є такими:

Мені подобається мова програмування C++

Звернемо Вашу увагу на те, що параметр str функції StrUpper() оголошується з використанням типу char *. Це дає змогу отримати покажчик на символьний масив, який містить рядок.

Розглянемо ще один приклад передачі рядка функції. Як було зазначено в розд. 5, стандартна бібліотечна функція strlen () повертає довжину рядка. У наведеному нижче коді програми показано один з можливих варіантів реалізації цієї функції.

Код програми 7.12. Демонстрація механізму використання функції strlen()

#include <iostream> // Для потокового введення-виведення

using namespace std; // Використання стандартного простору імен

int myStrlen(char *str);

 

int main ()

{

cout << "Довжина рядка ПРИВІТ УСІМ дорівнює: ";

cout << myStrlen("ПРИВІТ УСІМ");

 

getch (); return 0;

}

 

// Нестандартна реалізація функції strlen ().

int myStrlen(char *str)

{

int i;

 

for (i=0; str[i]; i++); // Знаходимо кінець рядка.

 

return i;

}

Ось як виглядають результати виконання цієї програми.

Довжина рядка ПРИВІТ УСІМ дорівнює: 11

Як вправу Вам варто було б спробувати самостійно реалізувати інші рядкові функції, наприклад strcpy () або strcpy (). Цей тест дасть змогу дізнатися, наскільки добре Ви засвоїли такі елементи мови C++, як масиви, рядки і покажчики.

7.3. Аргументи функції main(): argc і argv

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

Іноді виникає потреба передати інформацію програмі при її запуску. Як правило, це реалізується шляхом передачі аргументів командного рядка функції main (). Аргумент командного рядка є інформацією, що вказується в команді (командному рядку), призначеною для виконання операційною системою, після імені програми[29]. Наприклад, С++-програми можна компілювати шляхом виконання наступної команди:

cl prog_name

У цьому записі елемент prog_name – ім'я програми, яку ми хочемо скомпілювати. Ім'я програми передається С++-компіляторові як аргумент командного рядка.

У мові програмування C++ для функції main () визначено два вбудованих, але необов'язкових параметри, argc і argv, які набувають свої значення від аргументів командного рядка. У конкретному операційному середовищі можуть підтримуватися і інші аргументи (таку інформацію необхідно уточнити по документації, що додається до Вашого компілятора). Розглянемо параметри argc і argv детальніше.

Варто знати! Формально для імен параметрів командного рядка можна вибирати будь-які ідентифікатори, проте імена argc і argv використовуються за домовленістю вже протягом декількох років. Тому є сенс не вдаватися до інших імен ідентифікаторів, щоб будь-який програміст, якому доведеться розбиратися у Вашій програмі, зміг швидко ідентифікувати їх як параметри командного рядка.

Параметр argc має цілочисельний тип і призначений для зберігання кількості аргументів командного рядка. Його значення завжди не менше від одиниці, оскільки ім'я програми також є одним з аргументів, що враховуються. Параметр argv є покажчик на масив символьних покажчиків. Кожен покажчик в масиві argv посилається на рядок, що містить аргумент командного рядка. Елемент argv [0] вказує на ім'я програми; елемент argv [1] – на перший аргумент, елемент argv [2] – на другий і т.д. Всі аргументи командного рядка передаються програмі як рядки, тому числові аргументи необхідно перетворити у програмі у відповідний внутрішній формат.

Важливо правильно оголосити параметр argv. Переважно це робиться так:

char * argv [];

Доступ до окремих аргументів командного рядка можна отримати шляхом індексації масиву argv. Як це зробити, показано у наведеному нижче коді програми. Під час її виконання на екран виводиться вітання ("Привіт"), а за ним Ваше ім'я, яке повинно бути першим аргументом командного рядка.

Код програми 7.12. Демонстрація механізму доступу до окремих аргументів командного рядка

#include <iostream> // Для потокового введення-виведення

using namespace std; // Використання стандартного простору імен

 

int main (int argc, char * argv [])

{

if (argc!=2) {

cout << "Ви забули ввести своє ім'я.\n";

return 1;

}

cout << "Привіт, " << argv [1] << "\n";

 

getch (); return 0;

}

Припустимо, що Ви називаєтеся Тимур і що Ви назвали цю програму іменем name. Тоді, якщо запустити цю програму, ввівши команду name Тимур, результат її роботи повинен виглядати так: Привіт, Тимур. Наприклад, Ви працюєте з диском А, і у відповідь на пропозицію введення команди Ви повинні ввести згадану вище команду і отримати такий результат:

A>name Тимур

Привіт, Тимур

А>

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

один, два і три

складається з чотирьох рядкових аргументів, тоді як рядок

один,два, три

містить тільки два, оскільки кома не є допустимим роздільником.

Якщо необхідно передати як один аргумент командного рядка набір символів, який містить пропуски, то його потрібно помістити в лапки. Наприклад, цей набір символів буде сприйнятий як один аргумент командного рядка:

"це тільки один аргумент"

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

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

Код програми 7.13. Демонстрація посимвольного виведення всіх аргументів командного рядка, з яких вона була викликана

#include <iostream> // Для потокового введення-виведення

using namespace std; // Використання стандартного простору імен

 

int main (int argc, char * argv [])

{

int t, i;

for (t=0; t< argc; ++t) {

i=0;

while (argv [t][i]) {

cout << argv [t][i];

++i;

}

cout << " ";

}

 

getch (); return 0;

}

Неважко здогадатися, що перший індекс масиву argv дає змогу отримати доступ до відповідного аргументу командного рядка, а другий – до конкретного символу цього рядкового аргументу.

Звичайно аргументи argc і argv використовуються для введення у програму початкових параметрів, початкових значень, імен файлів або варіантів (режимів) роботи програми. У мові програмування C++ можна ввести стільки аргументів командного рядка, скільки допускає операційна система. Використання аргументів командного рядка додає програмі професійний вигляд і дає змогу використовувати її в командному файлі (виконуваному текстовому файлі, що містить одну або декілька команд).

7.3.1. Передача програмі числових аргументів командного рядка

Як ми вже зазначали вище, під час передачі програмі числових даних як аргументів командного рядка, ці дані приймаються в рядковій формі. У програмі повинно бути передбачено їх перетворення у відповідний внутрішній формат за допомогою однієї із стандартних бібліотечних функцій, що підтримуються мовою C++. Наприклад, у процесі виконання наведеної нижче програми виводиться сума двох чисел, які вказуються в командному рядку після імені програми. Для перетворення аргументів командного рядка у внутрішнє представлення тут використовується стандартна бібліотечна функція atof (). Вона перетворить число з рядкового формату в значення типу double.

Код програми 7.14. Демонстрація знаходження суми двох числових аргументів командного рядка

#include <iostream> // Для потокового введення-виведення

#include <cstdlib> // Для використання бібліотечних функцій

using namespace std; // Використання стандартного простору імен

 

int main (int argc, char * argv [])

{

double а, b;

if (argc!=3) {

cout << "Використання: add число число \n|";

return 1;

}

a = atof (argv [1]);

b = atof (argv [2]);




Поделиться с друзьями:


Дата добавления: 2014-11-29; Просмотров: 426; Нарушение авторских прав?; Мы поможем в написании вашей работы!


Нам важно ваше мнение! Был ли полезен опубликованный материал? Да | Нет



studopedia.su - Студопедия (2013 - 2024) год. Все материалы представленные на сайте исключительно с целью ознакомления читателями и не преследуют коммерческих целей или нарушение авторских прав! Последнее добавление




Генерация страницы за: 0.168 сек.