
- •12. Класи пам'яті даних 100
- •13. Робота з файлами 106
- •14. Посилання 111
- •Склад мови
- •Алфавіт мови
- •Лексеми
- •Ключові слова
- •Ідентифікатори
- •Константи
- •Символьні рядки
- •Знаки операцій, роздільники, коментарі
- •Типи даних
- •Класифікація типів даних
- •Цілочислові типи
- •Дійсні типи
- •Оголошення змінних і констант
- •Переліки
- •Структура програми
- •Функція main
- •Область дії змінної
- •Введення і виведення даних
- •Функції стандартної математичної бібліотеки
- •Вирази і операції
- •Поняття виразу, операнда, операції
- •Порядок виконання операцій
- •Арифметичні операції
- •Порозрядні операції
- •Операції порівняння
- •Логічні операції
- •Операції присвоєння
- •Умовна операція
- •Операція визначення розміру sizeof
- •Узгодження типів у виразах
- •Умовні оператори
- •Оператори
- •Умовний оператор if
- •Оператор вибору switch
- •Оператори циклу
- •Цикл з параметром. Оператор for
- •Цикл з передумовою. Оператор while
- •Цикл з постумовою. Оператор do while
- •Оператори переходу
- •Використання псевдовипадкових чисел
- •Одновимірні масиви
- •Багатовимірні масиви
- •Символьні рядки
- •Вказівники
- •Оголошення вказівника, операції, пов’язані з вказівниками
- •Адресна арифметика
- •Void – вказівники, типізація вказівників
- •Звертання до елементів масивів через вказівники
- •Масиви символьних рядків і масиви вказівників
- •Динамічне виділення пам’яті
- •Структури і об’єднання
- •Оголошення і ініціалізація структур
- •Звертання до елементів структур
- •Перейменування типів
- •Об’єднання
- •Поля бітів
- •Директиви препроцесора
- •Призначення директив препроцесора
- •Директива включення #include
- •Директиви макропідстановок #define I #undef
- •Директиви умовної компіляції
- •Директиви #ifdef, #ifndef
- •Функції
- •Структура функції
- •Виклик функцій. Прототипи функцій
- •Взаємодія фактичних і формальних параметрів функцій
- •Inline – функції
- •Масиви і символьні рядки як параметри функцій
- •Використання кваліфікатора const в оголошеннях параметрів
- •Багатовимірні масиви як параметри функцій
- •Опрацювання структур у функціях
- •Вказівники на функції
- •Рекурсивні функції
- •Класи пам'яті даних
- •Клас пам'яті, час існування і видимість об’єкта
- •Область дії глобальних і локальних змінних
- •Специфікатори класів пам'яті
- •Специфікатори глобальних змінних
- •Багатофайлові програми
- •Робота з файлами
- •Звертання до файлів
- •Послідовний запис до файлу і послідовне читання з файлу
- •Файли з довільним доступом
- •Посилання
- •Призначення посилань
- •Передача аргументів функцій як посилань
Директиви #ifdef, #ifndef
Ще одним способом організації умовної компіляції є використання директив #ifdef (if defined – за умови визначеності) і #ifndef (if not defined – за умови невизначеності). Синтаксис їх однаковий:
#ifdef ім’я_макросу
фрагмент_програми
#endif
#ifndef ім’я_макросу
фрагмент_програми
#endif
Обидві директиви перевіряють, чи був макрос, ім’я якого задається в цій директиві, попередньо визначений директивою #define. При цьому не вимагається, щоб макрос мав повне визначення, достатньо оголошення
#define ім’я_макросу
Фрагмент програми, обмежений директивами #ifdef … #ifndef буде компілюватись за умови, що ім’я макросу попередньо визначене. Дія директиви #ifndef протилежна – фрагмент програми передається на компіляцію, якщо заданий макрос не визначений.
Умовну компіляцію можна використовувати для налагодження програм. Зокрема, в текст програм можна включити ряд контрольних перевірок і повідомлень, що допоможуть відстежити процес виконання програми. Наприклад:
#include <iostream>
using namespace std;
#define DEBUG
int main() {
int a = 123;
#ifdef DEBUG
cout << "a = " << a << endl;
#endif
}
Таких контрольних точок можна створити в програмі декілька. Коли від лагодження програми завершене, достатньо закоментувати директиву #define DEBUG або використати директиву #undef DEBUG.
Директиви #ifdef і #ifndef використовують в заголовних файлах для захисту програми від повторних включень заголовних файлів, які можуть виникнути через вкладення директив #include. Оголосимо структуру для збереження інформації про студентів в окремому файлі:
// student.h
#ifndef STUDENT_H
#define STUDENT_H
struct student {
int id;
int rating;
student * next;
};
#endif
#include <iostream>
#include "student.h"
using namespace std;
int main() {
struct student * p;
…
У разі повторного підключення файлу student.h макрос STUDENT_H вже буде визначеним і директива #ifndef пропустить препроцесування вмісту файлу.
Перевірити, чи ім’я заданого макросу визначене, можна також за допомогою операції defined, яка застосовується в виразах умови директив #if i #elif і має такий синтаксис:
defined ім’я_макросу
Якщо даний макрос був попередньо визначений, то результат операції буде істинний. До результатів цієї операції можна застосовувати логічні операції !, &&, ||. Попередній приклад можна записати так:
#if !defined STUDENT_H
#define STUDENT_H
struct student {
int id;
int rating;
student * next;
};
#endif
За допомогою цієї операції можна перевіряти визначеність декількох макросів:
#if defined A && defined B
…
#endif
Функції
Структура функції
При структурному підході до розробки програм їх основними складовими компонентами є функції, оператори яких реалізують алгоритм розв’язування задачі. Функціями називають самостійні, логічно завершені фрагменти програми, що мають власне ім’я і призначені для виконання певних заданих дій, останньою з яких може бути повернення деякого значення.
Використання функцій дає змогу структурувати програму – поділити складні обчислювальні і/або інформаційні процеси на окремі складові частини, виділити основні кроки в алгоритмі розв’язування задачі, а вже потім розкрити деталі їх програмної реалізації в окремних функціях. Завдяки функціям можна уникнути багаторазового запису одних і тих самих дій і скористатися тим кодом, який був створений раніше, в тому числі бібліотеками систем програмування.
Функції полегшують процес програмування, уможливлюють його розпаралелення, спрощують пошук помилок в програмі і внесення доповнень і розширень, роблять програму виразнішою та лаконічно.
Функція описується синтаксичною конструкцією такого виду:
тип_значення_функції ім’я_функції ( оголошення_параметрів ) {
тіло_функції
}
Перший рядок опису функції називається заголовком функції, частина в фігурних дужках формує тіло функції. У заголовку функції першим вказується тип значення, яке повертає функція в точку її виклику після завершення виконання. Тип значення функції може бути довільним допустимим для мови С++ типом, крім масиву та функції, але може бути вказівником на масив або функцію. Також функція може повертати значення користувацьких типів – структур та об’єднань. Особливим типом значення функції є тип void, який вказує, що функція не повертає значення. Такі функції є аналогами процедур в інших мовах програмування.
Ім’я функції призначене для звертання до неї, воно формується за правилами запису ідентифікаторів мови С++. Для наочності програми бажано імена програми обирати змістовними, такими, що відповідають призначенню функції.
Після імені функції вказують круглі дужки, в яких оголошується список параметрів функції – їх називають формальними параметрами або аргументами функції. У функції може бути порожній список параметрів, але круглі дужки, які є ознакою функції, вказувати обов’язково. Відсутність параметрів функції можна позначати не порожніми дужками, а ключовим словом void. В цьому випадку компілятор отримує інформацію про те, що виклик цієї функції повинен виконуватися без вказання фактичних параметрів, тобто тих параметрів, що задаються в звертанні до функції.
Якщо у функції є параметри, то кожен з них оголошується в списку параметрів з окремим зазначенням типу, навіть у випадках, коли типи послідовних параметрів збігаються. Оголошення відділяються одоне від одного комами. Типи параметрів можуть бути довільними типами мови С++.
Тіло функції, записане в фігурних дужках, реалізує дії, які повинна виконати ця функція. Для роботи функції можуть бути необхідні додаткові змінні, які можна оголошувати всередині функції, і які називають локальними змінними. Областю дії локальних змінних функції та її формальних параметрів є тіло даної функції. Ці змінні створюються на час виконання функції в стають невизначеними після її завершення.
Функція завершує свою роботу, коли виконані всі оператори її тіла та досягнуто кінця функції або коли зустрінеться оператор return. Якщо тип значення функції – це void, то вона не повертає значення і оператор return для неї не обов’язковий. Але при необхідності його можна використовувати для переривання роботи функції. Наприклад:
#include <iostream>
using namespace std;
void PrintSqrt( double x ) {
if ( x < 0 ) {
cout << "x < 0" << endl;
return;
}
cout << sqrt( x ) << endl;
}
int main() {
PrintSqrt( -1. );
PrintSqrt( 4 );
}
В цьому прикладі при виконання умови x < 0 на екран буде виведене відповідне повідомлення, після цього виконання функції буде завершене, управляння передане в точку її виклику.
Якщо функція повертає значення, тобто її тип відмінний від void, це значення має бути передане через вираз, записаний в операторі return. Значення виразу перетворюється до типу, заданого в заголовку функції, і передається в точку програми, з якої була викликана функція. Операторів return всередині функції може бути декілька, виконання функції завершується при виконання першого оператора return.
#include <iostream>
using namespace std;
int sum( int a, int b ) {
int t;
t = a + b;
return t;
}
int max( int a, int b ) {
if ( a > b )
return a;
return b;
}
int main() {
int x = 3, y = 4;
cout << sum( x, y ) << endl;
cout << max( 2, 3 ) << endl;
}