- •12. Класи пам'яті даних 100
- •13. Робота з файлами 106
- •14. Посилання 111
- •Склад мови
- •Алфавіт мови
- •Лексеми
- •Ключові слова
- •Ідентифікатори
- •Константи
- •Символьні рядки
- •Знаки операцій, роздільники, коментарі
- •Типи даних
- •Класифікація типів даних
- •Цілочислові типи
- •Дійсні типи
- •Оголошення змінних і констант
- •Переліки
- •Структура програми
- •Функція main
- •Область дії змінної
- •Введення і виведення даних
- •Функції стандартної математичної бібліотеки
- •Вирази і операції
- •Поняття виразу, операнда, операції
- •Порядок виконання операцій
- •Арифметичні операції
- •Порозрядні операції
- •Операції порівняння
- •Логічні операції
- •Операції присвоєння
- •Умовна операція
- •Операція визначення розміру sizeof
- •Узгодження типів у виразах
- •Умовні оператори
- •Оператори
- •Умовний оператор if
- •Оператор вибору switch
- •Оператори циклу
- •Цикл з параметром. Оператор for
- •Цикл з передумовою. Оператор while
- •Цикл з постумовою. Оператор do while
- •Оператори переходу
- •Використання псевдовипадкових чисел
- •Одновимірні масиви
- •Багатовимірні масиви
- •Символьні рядки
- •Вказівники
- •Оголошення вказівника, операції, пов’язані з вказівниками
- •Адресна арифметика
- •Void – вказівники, типізація вказівників
- •Звертання до елементів масивів через вказівники
- •Масиви символьних рядків і масиви вказівників
- •Динамічне виділення пам’яті
- •Структури і об’єднання
- •Оголошення і ініціалізація структур
- •Звертання до елементів структур
- •Перейменування типів
- •Об’єднання
- •Поля бітів
- •Директиви препроцесора
- •Призначення директив препроцесора
- •Директива включення #include
- •Директиви макропідстановок #define I #undef
- •Директиви умовної компіляції
- •Директиви #ifdef, #ifndef
- •Функції
- •Структура функції
- •Виклик функцій. Прототипи функцій
- •Взаємодія фактичних і формальних параметрів функцій
- •Inline – функції
- •Масиви і символьні рядки як параметри функцій
- •Використання кваліфікатора const в оголошеннях параметрів
- •Багатовимірні масиви як параметри функцій
- •Опрацювання структур у функціях
- •Вказівники на функції
- •Рекурсивні функції
- •Класи пам'яті даних
- •Клас пам'яті, час існування і видимість об’єкта
- •Область дії глобальних і локальних змінних
- •Специфікатори класів пам'яті
- •Специфікатори глобальних змінних
- •Багатофайлові програми
- •Робота з файлами
- •Звертання до файлів
- •Послідовний запис до файлу і послідовне читання з файлу
- •Файли з довільним доступом
- •Посилання
- •Призначення посилань
- •Передача аргументів функцій як посилань
Inline – функції
Функції, оголошення яких починається ключовим словом inline, називають вбудованими функціями. Виконання таких функцій має бути максимально швидким, тому компілятор «вбудує» код такої функції у місці її виклику. Це може збільшити обсяг коду програми, оскільки кожен виклик функції буде замінений її тілом. Тому вбудованими доцільно оголошувати невеликі функції, які викликаються багатократно і мають критичний вплив на виконання програми.
В останньому прикладі inline – функцією можна оголосити функцію array_swap, яка виконує просту операцію і викликається багато разів. Оголошення функції буде виглядати таким чином:
inline void array_swap( int * a, int * b ) {
int t = *a;
*a = *b;
*b = t;
}
Використання вбудованих функції дає змогу дотримуватись принципів функціональної структуризації програми і одночасно забезпечити максимальну швидкодію реалізації таких функцій. Виграш у часі виконання inline – функцій порівняно з звичайними функціями досягається завдяки тому, що вбудований код не вимагає передавання параметрів і значення функції, а також збереження в стеку поточних станів системних регістрів. Але inline не є обов’язковою директивою, тобто компілятор може не вбудовувати дану функцію, а використати інші засоби оптимізації швидкодії або взагалі проігнорувати вказівку inline.
Для прикладу наведемо inline – функцію, що обчислює значення квадрату заданого аргументу дійсного типу:
inline int sqr( int x ) {
return x * x;
}
Якщо звернутися до цієї функції для обчислення виразу
sqr( a + b ) / ( sqr( a ) + sqr( b ) )
то компілятор може поміняти виклики функцій на вбудування її тіла, тобто вираз набуде вигляду
( a + b ) * ( a + b ) / ( a * a + b * b )
Масиви і символьні рядки як параметри функцій
Якщо у функцію для опрацювання передається ім’я масиву, то на відміну від змінних інших типів не створюється копія масиву, а передається адреса його першого елемента. Тому в процесі виконання функції дії виконуються не над копією, а над оригіналом масиву, який заданий через відповідний фактичний параметр.
Використовують дві форми оголошення формального параметра функції, який повинен відповідати масиву:
тип_елементів ім’я_масиву[] int vect[]
тип_елементів * ім’я_вказівника int * vect
Обидві форми рівнозначні і оголошують формальний параметр, що є вказівником на перший елемент масиву. Перша форма візуально підкреслює, що функція опрацьовує масив, друга зазначає, що для роботи з елементами масиву використовується вказівник.
В якості приклада створимо дві функції, які знаходять середнє значення елементів масиву цілих чисел, використаємо обидві форми оголошення параметрів:
double avg1( int a[ ], int n ) {
double t = 0;
for ( int i = 0; i < n; i++ )
t += a[ i ];
return t / n;
}
double avg2( int * p, int n ) {
int * last;
double t;
for ( last = p + n, t = 0; p < last; p++ )
t += * p;
return t / n;
}
…
const int NUM = 5;
int a[ NUM ];
…
cout << avg1( a, NUM ) << endl;
cout << avg2( a, NUM ) << endl;
Формальний параметр функції, навіть оголошений як масив, є звичайним, а не константним вказівником на початок масиву, тому його значення можна змінювати в тілі функції:
double avg2( int p[ ], int n ) {
int * last;
double t;
for ( last = p + n, t = 0; p < last; p++ )
t += * p;
return t / n;
}
Все сказане про параметри – масиви відноситься і параметрів функцій, які є символьними рядками. Їх можна оголошувати як масиви або як вказівники, остання форма на практиці застосовується частіше. Розглянемо функцію, яка замінює малі літери рядка на великі:
char * to_upper( char * p ) {
char d = 'A' - 'a';
char * t;
for ( t = p; *t; t++ )
if ( 'a' < *t && *t < 'z' )
*t += d;
return p;
}
…
char s[ ] = "hello world!";
cout << to_upper( s ) << endl;
cout << s << endl;
Функція повертає вказівник на початок перетвореного рядка. Такий тип поверненого значення здебільшого використовують у функціях, змінюють вміст символьного рядка. Хоча адреса, яку повертає функція, відома (це адреса першого символу рядка, що опрацьовувався), наявність поверненого значення дає змогу використовувати цю функцію як параметр інших функцій, які працюватимуть з перетвореним символьним рядком.
Значенням, яке повертає функція, не може бути масив або символьний рядок. Проте в багатьох задачах необхідно створювати нові масиви або рядки символів саме всередині функцій. Розглянемо приклад функції, яка повинна виділяти з заданого речення перше слово.
char * WrongFirstWord( char * s ) {
char word[ 20 ], * w = word;
while ( *s != ' ' && *s != '.' && *s != ',' && s != '\0' )
*w++ = *s++;
*w = '\0';
return word;
}
…
char * str = "There is no place like 127.0.0.1";
cout << WrongFirstWord( str ) << endl;
При спробі компіляції такої функції буде видано попередження «Функція повертає значення локальної змінної», результат виконання такої функції буде непередбачуваний. Вирішити цю проблему можна таким чином: 1) всередині функції виділяти пам’ять динамічно, повертати вказівник на виділену ділянку пам'яті; 2) передавати адресу пам'яті, в яку необхідно записувати результат виконання функції, в якості параметра функції; 3) використовувати глобальні змінні.
В наступному прикладі функція повертає вказівник на ділянку пам'яті, яка виділяється під час роботи програми, оператор new знаходиться всередині функції. Така ділянка пам'яті не звільняється після закінчення роботи функції, необхідно лише коректно передати з функції вказівник на неї.
char * FirstWord( char * s ) {
char * pc = s;
while ( *pc != ' ' && *pc != '.' && *pc != ',' && pc != '\0' )
pc++;
int n = pc - s; // Кількість символів в першому слові
char * word = new char [ n + 1 ];
pc = word;
while ( *s != ' ' && *s != '.' && *s != ',' && s != '\0' )
*pc++ = *s++;
*pc = '\0';
return word;
}
…
char * str = "There is no place like 127.0.0.1";
char * res = NULL;
res = FirstWord( str );
cout << res << endl;
delete[] res;
