💀
Второй курс РПО
C++
C++
  • Свойства и типы
  • Блок-схемы
  • Visual Studio
  • Первый проект
  • Вывод данных
  • Типы данных
  • Переменные и Константы
  • Ввод данных
  • Литералы
  • Задание [ ! ]
  • Первая практическая
  • Операторы в языке программирования
  • Арифметические операции в C++
  • Применение арифметических операций
  • Практическая работа №2
  • Практическая работа №3
  • Логические операции
    • Справочник по командам
  • Практическая работа №4
  • Практическая работа №5
  • Массивы
    • Спец. задание
  • ⚙️Справочник по C++
  • Домашнее задание №1
  • Функции
    • Более краткая версия
  • Практическая работа №6
  • Указатели
  • Задание на экране 12.12
  • Введение в строки
  • Перегрузка функций в C++
  • Функции класса
  • Полезные штуки
  • Работа с классами в C++
  • Дружественные классы в C++
Powered by GitBook
On this page
  • 1. Что такое функция и зачем она нужна?
  • 2. Вызов функции
  • 3. Ключевое слово return
  • 4. Примеры создания функций
  • 5. Передача аргументов
  • 6. Прототипы функций
  • 7. Область видимости
  • 8. Аргументы по умолчанию
  • 9. Статические переменные
  • 10. Передача массивов
  • 11. Глобальные и локальные переменные
  • 12. Статические переменные
  • 13. Аргументы по умолчанию
  • 14. Советы по работе с функциями
  • Сортировка пузырьком (Bubble Sort)

Функции

06.12.2024

1. Что такое функция и зачем она нужна?

Функция в программировании — это независимый блок кода, который выполняет определенную задачу. Её цель:

  • Сократить повторение кода. Если в вашей программе повторяются одинаковые куски кода, их можно вынести в отдельную функцию.

  • Повысить читаемость и удобство. Программа, разбитая на функции, становится легче для понимания.

  • Упростить отладку. Исправить ошибку в функции проще, чем искать её по всему коду.

Синтаксис объявления функции

Существует два способа объявления функций:

1. Объявление до main

Функция полностью описывается перед функцией main.

Синтаксис

возвращаемый_тип имя_функции(параметры) {
    // тело функции
}

2. Объявление с помощью прототипа

Прототип функции размещается перед main, а её реализация — после.

Прототип:

возвращаемый_тип имя_функции(параметры);

Пример прототипа и реализации:

#include <iostream>
using namespace std;

// Прототип
int Add(int a, int b);

int main() {
    cout << "Сумма: " << Add(5, 3) << endl;
    return 0;
}

// Реализация
int Add(int a, int b) {
    return a + b;
}

2. Вызов функции

Функция вызывается по имени с указанием аргументов (если они есть). Синтаксис вызова:

тип_данных имя_функции(аргументы);

void Функция ничего не возвращает. Используется для выполнения действий, например, вывода информации. int Функция возвращает целое число. Применяется для целочисленных вычислений. float Возвращает число с плавающей точкой (с десятичной частью). Подходит для работы с дробными числами. double Тоже для чисел с плавающей точкой, но с большей точностью, чем float. char Возвращает один символ. Используется для работы с текстовыми данными. bool Возвращает логическое значение true или false. Часто применяется в условиях.

Если функция возвращает значение, его можно сохранить в переменную:

int результат = имя_функции(аргументы);

Пример:

#include <iostream>
using namespace std;

int Square(int number) {
    return number * number;
}

int main() {
    int result = Square(4);
    cout << "Квадрат числа: " << result << endl;
    return 0;
}

3. Ключевое слово return

Ключевое слово return используется для завершения работы функции и возвращения результата.

Особенности:

  1. Если функция возвращает значение Тип возвращаемого значения должен совпадать с указанным в объявлении функции.

    return значение;
  2. Если функция ничего не возвращает Используется тип void. Можно использовать return; для выхода.

    void PrintHello() {
        cout << "Hello, World!" << endl;
        return;
    }

Пример:

int Max(int a, int b) {
    return (a > b) ? a : b;
}

4. Примеры создания функций

Функция без параметров и без возвращаемого значения

void PrintMessage() {
    cout << "Привет!" << endl;
}

Функция с параметрами и без возвращаемого значения

void PrintStars(int count) {
    for (int i = 0; i < count; ++i) {
        cout << '*';
    }
    cout << endl;
}

Функция с параметрами и с возвращаемым значением

int Multiply(int a, int b) {
    return a * b;
}

Функция, принимающая два параметра

#include <iostream>
using namespace std;

void DrawLine(char symbol, int length) {
    for (int i = 0; i < length; ++i) {
        cout << symbol;
    }
    cout << endl;
}

5. Передача аргументов

Передача аргументов по значению

При передаче данных по значению создается их копия. Все изменения в функции не влияют на оригинальные переменные.

Пример:

void ChangeValues(int a, int b) {
    int temp = a;
    a = b;
    b = temp;
}

Передача массивов

Массивы передаются по ссылке, поэтому изменения внутри функции сохраняются.

Пример работы с массивом:

void FillArray(int arr[], int size) {
    for (int i = 0; i < size; ++i) {
        arr[i] = rand() % 100;
    }
}

6. Прототипы функций

Прототип позволяет объявить функцию до её реализации. Это особенно удобно при длинных программах.

Пример:

#include <iostream>
using namespace std;

// Прототип
void Greet();

int main() {
    Greet();
    return 0;
}

// Реализация
void Greet() {
    cout << "Hello!" << endl;
}

7. Область видимости

Локальные переменные

Переменные, объявленные внутри функции, видны только в ней.

Глобальные переменные

Переменные, объявленные вне функций, доступны везде.


8. Аргументы по умолчанию

Если аргументы не указаны, используются значения по умолчанию.

Пример:

void PrintLine(char symbol = '*', int length = 10) {
    for (int i = 0; i < length; ++i) {
        cout << symbol;
    }
    cout << endl;
}

9. Статические переменные

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

Пример:

void Counter() {
    static int count = 0;
    count++;
    cout << "Count: " << count << endl;
}

10. Передача массивов

Одномерные массивы

Массив передается в функцию по ссылке. Это означает, что изменения внутри функции будут сохраняться после её завершения.

Общий синтаксис:

тип_данных имя_функции(тип_массив[], int размер) {
    // тело функции
}

Пример: Заполнение массива случайными числами и их отображение:

#include <iostream>
#include <cstdlib>
#include <ctime>
using namespace std;

// Заполнение массива случайными числами
void InitArray(int arr[], int size) {
    for (int i = 0; i < size; ++i) {
        arr[i] = rand() % 100; // случайные числа от 0 до 99
    }
}

// Вывод массива
void ShowArray(const int arr[], int size) {
    for (int i = 0; i < size; ++i) {
        cout << arr[i] << " ";
    }
    cout << endl;
}

int main() {
    srand(time(0)); // инициализация генератора случайных чисел
    const int size = 10;
    int numbers[size];

    InitArray(numbers, size);
    ShowArray(numbers, size);

    return 0;
}

Двумерные массивы

При передаче двумерных массивов в функцию обязательно указывается количество столбцов.

Общий синтаксис:

тип_данных имя_функции(тип_массив[][количество_столбцов], int количество_строк) {
    // тело функции
}

Пример: Инициализация и вывод двумерного массива:

#include <iostream>
#include <cstdlib>
#include <ctime>
using namespace std;

void InitMatrix(int matrix[][3], int rows, int cols) {
    for (int i = 0; i < rows; ++i) {
        for (int j = 0; j < cols; ++j) {
            matrix[i][j] = rand() % 10; // случайные числа от 0 до 9
        }
    }
}

void ShowMatrix(const int matrix[][3], int rows, int cols) {
    for (int i = 0; i < rows; ++i) {
        for (int j = 0; j < cols; ++j) {
            cout << matrix[i][j] << " ";
        }
        cout << endl;
    }
}

int main() {
    srand(time(0));
    const int rows = 3, cols = 3;
    int matrix[rows][cols];

    InitMatrix(matrix, rows, cols);
    ShowMatrix(matrix, rows, cols);

    return 0;
}

11. Глобальные и локальные переменные

Локальные переменные

Переменные, объявленные внутри функции, существуют только в её области видимости. После завершения работы функции эти переменные уничтожаются.

Пример:

#include <iostream>
using namespace std;

void Test() {
    int x = 10; // локальная переменная
    cout << "x внутри функции: " << x << endl;
}

int main() {
    Test();
    // cout << x; // Ошибка: x недоступна в этой области
    return 0;
}

Глобальные переменные

Переменные, объявленные вне всех функций, доступны в любой части программы. Они хранятся в памяти на протяжении всего времени работы программы.

Пример:

#include <iostream>
using namespace std;

int globalVar = 5; // глобальная переменная

void PrintGlobal() {
    cout << "Глобальная переменная: " << globalVar << endl;
}

int main() {
    PrintGlobal();
    globalVar = 10;
    PrintGlobal();
    return 0;
}

Конфликт локальной и глобальной переменных

Если внутри функции создается локальная переменная с тем же именем, что и у глобальной, то она скрывает глобальную.

Пример:

#include <iostream>
using namespace std;

int a = 10; // глобальная переменная

void Test() {
    int a = 20; // локальная переменная
    cout << "Локальная переменная: " << a << endl;
    cout << "Глобальная переменная через :: " << ::a << endl;
}

int main() {
    Test();
    return 0;
}

12. Статические переменные

Статические переменные в функции создаются только один раз при первом вызове и сохраняют своё значение между вызовами.

Пример:

#include <iostream>
using namespace std;

void StaticExample() {
    static int count = 0; // статическая переменная
    count++;
    cout << "Вызов функции #" << count << endl;
}

int main() {
    StaticExample();
    StaticExample();
    StaticExample();
    return 0;
}

Вывод:

Вывод 1  
Вывод 1  
Вывод 1  

13. Аргументы по умолчанию

Если при вызове функции аргумент не указан, используется значение по умолчанию, заданное в объявлении.

Пример:

#include <iostream>
using namespace std;

void PrintLine(char symbol = '-', int length = 10) {
    for (int i = 0; i < length; ++i) {
        cout << symbol;
    }
    cout << endl;
}

int main() {
    PrintLine();          // линия из 10 дефисов
    PrintLine('*');       // линия из 10 звездочек
    PrintLine('#', 5);    // линия из 5 решеток
    return 0;
}

14. Советы по работе с функциями

  1. Декомпозиция. Разбивайте код на небольшие функции, каждая из которых решает одну задачу.

  2. Имена функций. Используйте осмысленные названия, чтобы код был читаемым. Например, вместо func1 пишите CalculateSum.

  3. Минимизируйте использование глобальных переменных. Они усложняют отладку.

  4. Повторное использование. Функции можно вызывать из других функций, что упрощает структуру программы.

Да, метод сортировки "пузырьком" действительно один из классических алгоритмов, который часто рассматривается в курсах программирования. Вот подробное объяснение с реализацией и разбором.


Сортировка пузырьком (Bubble Sort)

Сортировка пузырьком — это простой алгоритм, который многократно проходит по массиву, сравнивая соседние элементы и меняя их местами, если они расположены в неправильном порядке. Этот процесс продолжается до тех пор, пока массив не будет отсортирован.


Принцип работы

  1. Проходим по массиву от начала до конца.

  2. Сравниваем каждый элемент с его соседом:

    • Если текущий элемент больше следующего, меняем их местами.

  3. Повторяем процесс для всех элементов, кроме последнего (так как он уже на своём месте после первой итерации).

  4. С каждым проходом диапазон проверяемых элементов уменьшается на 1, так как последние элементы уже отсортированы.


Эффективность

  • Сложность в худшем случае: O(n2)O(n^2)O(n2) — требуется nn проходов по массиву, каждый из которых включает n−1n-1 сравнений.

  • Сложность в лучшем случае: O(n)O(n)O(n), если массив изначально отсортирован (при использовании оптимизации с флагом).


Реализация

Без оптимизации

#include <iostream>
using namespace std;

void BubbleSort(int arr[], int size) {
    for (int i = 0; i < size - 1; ++i) {
        for (int j = 0; j < size - i - 1; ++j) {
            if (arr[j] > arr[j + 1]) {
                // Меняем элементы местами
                int temp = arr[j];
                arr[j] = arr[j + 1];
                arr[j + 1] = temp;
            }
        }
    }
}

void PrintArray(const int arr[], int size) {
    for (int i = 0; i < size; ++i) {
        cout << arr[i] << " ";
    }
    cout << endl;
}

int main() {
    int numbers[] = {64, 34, 25, 12, 22, 11, 90};
    int size = sizeof(numbers) / sizeof(numbers[0]);

    cout << "Исходный массив: ";
    PrintArray(numbers, size);

    BubbleSort(numbers, size);

    cout << "Отсортированный массив: ";
    PrintArray(numbers, size);

    return 0;
}

Вывод:

Исходный массив: 64 34 25 12 22 11 90  
Отсортированный массив: 11 12 22 25 34 64 90  

С оптимизацией (с флагом)

Если массив уже отсортирован, дополнительное прохождение бессмысленно. Для этого вводится булевый флаг, который отслеживает, были ли произведены обмены на текущей итерации. Если обменов не было, алгоритм завершает работу.

#include <iostream>
using namespace std;

void OptimizedBubbleSort(int arr[], int size) {
    for (int i = 0; i < size - 1; ++i) {
        bool swapped = false; // Флаг, показывающий наличие обменов
        for (int j = 0; j < size - i - 1; ++j) {
            if (arr[j] > arr[j + 1]) {
                int temp = arr[j];
                arr[j] = arr[j + 1];
                arr[j + 1] = temp;
                swapped = true; // Были произведены обмены
            }
        }
        // Если на данной итерации не было обменов, массив уже отсортирован
        if (!swapped) {
            break;
        }
    }
}

int main() {
    int numbers[] = {5, 1, 4, 2, 8};
    int size = sizeof(numbers) / sizeof(numbers[0]);

    cout << "Исходный массив: ";
    for (int i = 0; i < size; i++) {
        cout << numbers[i] << " ";
    }
    cout << endl;

    OptimizedBubbleSort(numbers, size);

    cout << "Отсортированный массив: ";
    for (int i = 0; i < size; i++) {
        cout << numbers[i] << " ";
    }
    cout << endl;

    return 0;
}

Преимущество оптимизации: Сокращает количество ненужных проходов по массиву, если он уже отсортирован, что улучшает производительность для частично отсортированных массивов.


Визуализация работы алгоритма

Итерации сортировки пузырьком можно представить следующим образом: Допустим, массив: [5, 3, 8, 6, 2].

  1. Первый проход:

    • Сравниваем 5 и 3, меняем местами: [3, 5, 8, 6, 2].

    • Сравниваем 5 и 8, оставляем на месте.

    • Сравниваем 8 и 6, меняем местами: [3, 5, 6, 8, 2].

    • Сравниваем 8 и 2, меняем местами: [3, 5, 6, 2, 8].

  2. Второй проход:

    • Сравниваем 3 и 5, оставляем на месте.

    • Сравниваем 5 и 6, оставляем на месте.

    • Сравниваем 6 и 2, меняем местами: [3, 5, 2, 6, 8].

  3. Третий проход:

    • Сравниваем 3 и 5, оставляем на месте.

    • Сравниваем 5 и 2, меняем местами: [3, 2, 5, 6, 8].

  4. Четвёртый проход:

    • Сравниваем 3 и 2, меняем местами: [2, 3, 5, 6, 8].

Массив отсортирован.


Когда использовать сортировку пузырьком?

  1. Учебные задачи: Отличный алгоритм для обучения основам сортировки.

  2. Маленькие массивы: Подходит для массивов, где n≤10n \leq 10n≤10.

  3. Редко изменяемые массивы: Если массив почти отсортирован, оптимизированный алгоритм будет эффективен.

PreviousДомашнее задание №1NextБолее краткая версия

Last updated 5 months ago