Въведение във функциите

Главната задача на програмиста е да пише код. Негова втора задача е да не повтаря вече написан код. За да не повтаряме кода си ние пишем функции. Пишем ги не само, за да не повтаряме кода. А и да разделим голяма задача на под задачи и така да я решим. Както са казали римските императори „разделяй и владей“.

Какво е функция?

Това е под програма. Тя се изпълнява един или повече пъти. Самата функция е част от цялото. В зависимост от предназначението си тя може да връща стойност или да не връща нищо. Понякога функция която не връща нищо се нарича процедура. Тя извършва някакво действие, но не връща резултат. Такава функция в C++ връща void (нищо).

Синтаксис

void Func();
void Func1(int x, int y);
long long PowerOf2(long long int q);

В този пример функциите от ред 1 и 2 правят нещо, не не връщат резултат. Докато в ред 3 всичко е ясно. Функцията връща q *q. Името и е достатъчно описателно.

За името на функция

Името трябва да е достатъчно описателно. За да, не се чудим за какво се ползва тази функция. Името на функцията отговаря на изискването за имената на променливите. Може да съдържа главни и малки английски букви, цифри и знака за подчертаване. Но не може да започва с цифра.

Аргументи на функция и връщана стойност

Нека имаме следната функция:

int getDayOfWeek(int day, int month, int year) {
    ...
}

Тази функция връща int. Ако не връщаше нищо щяхме да напишем пред името и void.

Можем да извикаме функцията така:

int day = 1;
int month = 4;
int year = 2026;
int dw = getDayOfWeek(1, 4, 2026);

Тук параметрите с които извикваме функцията (1, 4 и 2026) се наричат аргументи.

Аргументите се копират в локалните променливи вътре в тялото на функцията. А оригиналните променливи от редове 1..3 не се променят. Върнатия резултат също се предава на променливата dw чрез копиране. Това е така, защото след излизане от функцията нейните локални променливи се унищожават.

Разлика между локални и глобални променливи

  • Глобални променливи

Всяка програма има глобални и локални променливи. Глобалните променливи се дефинират преди функциите. Рядко между тях, избягва се за по добре четим код. Това значи, че глобалните променливи никога не са ограничени от блока { … } . Глобалните променливи „са живи“ докато програмата се изпълнява. Те са достъпни от всяка функция в нашия код. Но най – добрата практика е когато функция използва някакви глобални променливи те да се предават като аргументи на функцията. И ако те са само за четене можем да ги предадем с const. Така за читателя на вашия код веднага става ясно какви данни обработва вашата функция.

Може би ще възразите за масивите. Те са голям блок от памет. Но се предават специфично. Когато предаваме масив като аргумент на функция компилатора предава указател към първия елемент от масива (това става под капака). И понеже масива е структура от данни от един и същи тип данни компилатор може да изчисли адреса на всеки елемент от масива. Когато предавате char[] индикатор за края на текста е стойността ‘\0’ записана в последната клетка на масива. Когато предавате друг вид масиви трябва да укажете дължината на масива чрез друга променлива. Например:

int StrLen(char* x); // string
bool StrCat(char[] x, char[] y, char[] dest, int dest_size); // string
int ArraySum(int[] arr, int len); // задаваме големината на arr
  • Локални променливи

Локалните променливи живеят в специална област от паметта на програмата наречена „програмен стек“. Стека е разделен на области и нараства от долу нагоре. Всяка функция в която сте влезли но не е завършила заема място в стека. В нейната област от стека живеят локалните променливи на съответната функция. Когато излезете от функция заетата памет от локалните и променливи се освобождава. И се заемат от променливите на следващата извикана функция. Именно за това резултата от функцията се копира. За да се запази. Поведението на стека е: последния заел памет -> първи я освобождава. Тази схема е много гъвкава. И ни позволява да разделяме кода си на логически парчета – функциите.

Дефиниране на функция

Между къдравите скоби описваме как да се изпълни функцията. Представям ви практически пример, малко променен от стандартната библиотека на езика.

/*
    Връща:
    
    0 - "Събота", 
    1 - "Неделя", 
    2 - "Понеделник",
    3 - "Вторник", 
    4 - "Сряда", 
    5 - "Четвъртък", 
    6 - "Петък"

Алгоритъм на Zeller за ден от седмицата
*/
int getDayOfWeek(int d, int m, int y) {
    // Ако месецът е януари или февруари
    if (m < 3) {
        m += 12;
        y -= 1;
    }

    int K = y % 100;   // година от века
    int J = y / 100;   // век

    int h = (d + (13 * (m + 1)) / 5 + K + K / 4 + J / 4 + 5 * J) % 7;

    return h;
}

Разделяй и владей

Старайте се вашите функции да правят само едно логическо нещо от цялото. Ето как би трябвало да решавате задачите от тук нататък.

#include <iostream>
using namespace std;

// функция за събиране
int sum(int a, int b) {
    return a + b;
}

// намиране на по - голямото число
int maxNumber(int a, int b) {
    if (a > b)
        return a;
    return b;
}

// проверка дали едно число е четно
bool isEven(int n) {
    return n % 2 == 0;
}

// чете int въведен от потребителя
int readInt(char* prompt) {
    int t = 0;
    cout << prompt <<  " ";
    cin >> t;
    return t;
}

int main() {
    int x, y;
    
    cout << "Въведи две числа: \n";
    
    x = readInt("X =");
    y = readInt("Y =");

    cout << "Сума: " << sum(x, y) << endl;
    cout << "По-голямо число: " << maxNumber(x, y) << endl;

    if (isEven(x))
        cout << x << " е четно число" << endl;
    else
        cout << x << " е нечетно число" << endl;

    if (isEven(y))
        cout << y << " е четно число" << endl;
    else
        cout << y << " е нечетно число" << endl;
}

Задачи

1. Напишете програма, която чете 10 int числа въведени от потребителя. Отпечатайте масива на един ред в терминала. Съберете числата и изведете сбора и квадратен корен от сбора.

Решение
#include <iostream>
#include <cmath>
using namespace std;

const int SIZE = 10;
int arr[SIZE];

// въвеждане
void inputArray(int arr[], int size) {
    cout << "Въведете " << size << " цели числа:\n";
    for (int i = 0; i < size; i++) {
        cout << i << ": ";
        cin >> arr[i];
    }
}

// печат
void printArray(const int arr[], int size) {
    cout << "Масив: ";
    for (int i = 0; i < size; i++) {
        cout << arr[i] << " ";
    }
    cout << endl;
}

// сума на масива
int calculateSum(const int arr[], int size) {
    int sum = 0;
    for (int i = 0; i < size; i++) {
        sum += arr[i];
    }
    return sum;
}

// печат на резултатите
void printResults(const int sum) {
    cout << "Сбор: " << sum << endl;

    if (sum >= 0) {
        cout << "Квадратен корен: " << sqrt(sum) << endl;
    } else {
        cout << "Квадратен корен: няма (сборът е отрицателен)" << endl;
    }
}

int main() {
    inputArray(arr, SIZE);
    printArray(arr, SIZE);

    int sum = calculateSum(arr, SIZE);
    printResults(sum);
}

Забелязвате ли поведението на inputArray? Пишем директно в масива. Това работи защото масива е предаден с указател. Това още не сме го учили. Но скоро.

2. Отпечатайте преобразуването: Градуси по Целзий в градуси във Фаренхайт. От 0 до 300. През 20 градуса. Не използвайте масиви.

Fahrenheit = Celsius * 9.0 / 5.0 + 32;
Решение
#include <iostream>
using namespace std;

void PrintFahrenheit(int celsius) {
    int c = celsius;
    double f = c * 9.0 / 5.0 + 32;
    cout << c << " C = " << f << " F" << endl;

}

int main() {
    for (int c = 0; c <= 300; c += 20)
        PrintFahrenheit(c);
}

3. Напишете обратната на задача 2. Принтирайте от 0 до 300 градуса по Фаренхайт през 20 градуса и след това техните съответстващи градуси по Целзий. Използвайте trunc от cmath, за да премахнете дробната част от резултата.

Celsius = (Farenheit - 32) * 5.0 / 9.0;
Решение
#include <iostream>
#include <cmath>
using namespace std;

void PrintCelsius(int farenheit) {
    double celsius = (farenheit - 32) * 5.0 / 9.0;
    cout << farenheit << " F = " << trunc(celsius) << " C" << endl;
}

int main() {
    for (int f = 0; f <= 300; f += 20)
        PrintCelsius(f);
}

4. Потребителя въвежда 20 float числа. Сортирайте ги с функцията sort от <algorithms>. И изведете на терминала несортирания и после сортирания масив. Всеки на един ред.

Решение
#include <iostream>
#include <algorithm>
using namespace std;

const int SIZE = 20;
float arr[SIZE];

// въвеждане
void input(float arr[], int size) {
    cout << "Въведете " << size << " числа:\n";
    for (int i = 0; i < size; i++) {
        cout << i << ": ";
        cin >> arr[i];
    }
}

// принтиране
void print(float arr[], int size) {
    for (int i = 0; i < size; i++) {
        cout << arr[i] << " ";
    }
    cout << endl;
}

// cортиране
void sortArray(float arr[], int size) {
    sort(arr, arr + size);
}

int main() {
    input(arr, SIZE);

    cout << "Несортиран масив:\n";
    print(arr, SIZE);

    sortArray(arr, SIZE);

    cout << "Сортиран масив:\n";
    print(arr, SIZE);
}

5*. Напишете програма която показва календарния месец в терминала във вид подобен на този:

     Април 2026       
 П  В  С  Ч  П  С  Н  
       1  2  3  4  5
 6  7  8  9 10 11  12
13 14 15 16 17 18  19 
20 21 22 23 24 25  26
27 28 29 30  

Използвайте функцията getDayOfWeek. Която вече описахме в този урок.

Решение
#include <iostream>
#include <iomanip>
using namespace std;

// Проверка за високосна година
bool isLeap(int year) {
    return (year % 4 == 0 && year % 100 != 0) || (year % 400 == 0);
}

// Брой дни в месец
int daysInMonth(int month, int year) {
    switch(month) {
        case 1: return 31;
        case 2: return isLeap(year) ? 29 : 28;
        case 3: return 31;
        case 4: return 30;
        case 5: return 31;
        case 6: return 30;
        case 7: return 31;
        case 8: return 31;
        case 9: return 30;
        case 10: return 31;
        case 11: return 30;
        case 12: return 31;
        default: return 0;
    }
}

// Алгоритъм на Zeller за ден от седмицата
int dayOfWeek(int d, int m, int y) {
    if (m < 3) {
        m += 12;
        y--;
    }
    int K = y % 100;
    int J = y / 100;

    int f = d + (13*(m+1))/5 + K + K/4 + J/4 + 5*J;
    return f % 7; // 0 = събота
}

void printCalendar(int month, int year) {
    const string months[] = {
        "", "Януари", "Февруари", "Март", "Април", "Май", "Юни",
        "Юли", "Август", "Септември", "Октомври", "Ноември", "Декември"
    };

    cout << "\n   " << months[month] << " " << year << "\n";
    cout << " Пн Вт Ср Чт Пт Сб Нд\n";

    int firstDay = dayOfWeek(1, month, year);
    // Преобразуване: Zeller дава 0=събота → ние искаме Пн=0
    int start = (firstDay + 5) % 7;

    int days = daysInMonth(month, year);

    // Празни места
    for(int i = 0; i < start; i++)
        cout << "   ";

    // Дни
    for(int day = 1; day <= days; day++) {
        cout << setw(3) << day;

        if ((start + day) % 7 == 0)
            cout << "\n";
    }
    cout << "\n";
}

int main() {
    int month, year;

    cout << "Въведи месец (1-12): ";
    cin >> month;
    cout << "Въведи година: ";
    cin >> year;

    printCalendar(month, year);
}