Файл: Основные правила работы с функциями: примеры и ограничения использования функций в различных языках программирования.pdf
Добавлен: 29.03.2023
Просмотров: 268
Скачиваний: 1
СОДЕРЖАНИЕ
1.1. Определение функции, основные понятия
1.3. Преимущества и недостатки использования функций
2. Анализ языков программирования для приведения примеров использования функций
2.1. Языки низкого и высокого уровня
2.2. Типизированные и не типизированные языки программирования
2.3. Выбор языков программирования для приведения примеров использования функций
3. Примеры и ограничения использования функций
3.1. Примеры и ограничения использования функций в C++
3.2. Примеры и ограничения использования функций в Assembler
3.3. Примеры и ограничения использования функций в JavaScript
Одним из языков с явной, статической и достаточно сильной типизацией является С++. Динамическая, слабая и неявная типизация характерна в том числе для языка программирования JavaScript.
2.3. Выбор языков программирования для приведения примеров использования функций
Из анализа типов языков программирования следует, что для того, чтобы наиболее полно рассмотреть примеры и ограничения функций в разных языках программирования, следует выбрать языки программирования Assembler (низкоуровневый язык программирования без типизации), С++ (высокоуровневый язык программирования со статической, сильной и явной типизацией) и JavaScript (высокоуровневый язык программирования с динамической, слабой и неявной типизацией).
3. Примеры и ограничения использования функций
3.1. Примеры и ограничения использования функций в C++
В языке программирования С++ функция – один из самых важных компонентов. Согласно особенностям языка, функция:
- имеет явно указанный тип, соответствующий значению, которая она возвращает. В случае, если функция ничего не возвращает, она имеет тип void;
- каждая функция имеет список параметров, которые ей передаются. Данный список может быть пустым. Он обязательно берётся в круглые скобки;
- начало функции обозначено открывающейся фигурной скобкой. Конец функции обозначен закрывающейся фигурной скобкой [3];
- в каждой программе существует функция main() – главная функция программы, а так же точка входа в программу и выхода из неё. Это значит, что вся работа программы начинается и заканчивается с фигурными скобками функции main() ;
- объявление функции делится на 2 части: прототип и непосредственно реализация. Сначала должен указываться прототип, а только потом писать реализацию. Прототип состоит из типа возвращаемого значения, имени функции и списка параметров в круглых скобках. В конце прототипа должна стоять точка с запятой. Обычно прототипы функций выносят в заголовочный .h файл, который подключают в .cpp файле, где указывается реализация функции;
- объявление функции должно происходить раньше ее использования [8].
Итак, рассмотрим основные примеры применения функций в С++.
В С++ можно задать функцию типа void []. На рисунке 3 изображен пример программы, которая состоит из двух функций – main() и _functionVoid(int). В функцию _functionVoid(int) передается один целочисленный параметр, который отображает номер вызова функции. Результат работы программы приведен на рисунке 4.
Рисунок 3. Пример функции на С++, которая ничего не возвращает
Рисунок 4. Результат работы программы
Рассмотрим работу функций, которые имеют тип и возвращают значение [3]. В примере на рисунке 5 есть функция _functionSumm(int, int). В нее передается два целочисленных параметра, сумму которых она возвращает. Для этого в начале функции в переменную целочисленного типа sum записывается сумма переданных чисел (строка 13). Затем эта переменная передается обратно в функцию main() при помощи оператора return (строка 16). Результат работы программы приведен на рисунке 6.
Рисунок 5. Пример функции на С++, которая возвращает значение
Рисунок 6. Результат работы программы
С++ допускает вычисление возвращаемого параметра прямо в операторе return [8]. В следующем примере (см. рисунок 7) функция _functionDiv(double, double) в строке 14 сначала приводит тип целочисленной переменной firstNumber к дробному числу, затем делит это дробное число на целочисленную secondNumber, после чего отправляет полученное значение в main(). Результат работы программы представлен на рисунке 8.
Рисунок 7. Пример функции на С++, возвращаемое значение которой формируется после ключевого слова return
Рисунок 8. Результат работы программы
Итак, базовые примеры функций с разными типами данных рассмотрены. Обратим внимание на тот факт, что С++ имеет такую особенность, как область видимости. Данные внутри программы на языке С++ могут быть локальными, то есть существующими и видимыми только в пределах функции, в которой они были объявлены, или глобальными, то есть видимыми всей программе [8]. В обычных условиях обращение внутри функции к глобальным и локальным переменным никак не отличается. Но может возникнуть ситуация, когда локальная и глобальная переменная имеют одно и то же имя, как в программе на рисунке 9. В таком случае для обращения к локальной переменной используется ее имя, а для обращения к глобальной задается явный указатель области видимости, в нашем случае «::» - это значит, что имя переменной, к которой мы обращаемся, видно во всем текущем файле [3]. В силу особенностей работы языка С++ при возникновении такой ситуации необходимо учитывать ограничения областей видимости в работе функции. Результат работы программы представлен на рисунке 10.
Рисунок 9. Пример локальной и глобальной переменных в функции C++
Рисунок 10. Результат работы программы
Кроме всего прочего, в С++ существует значение по умолчанию для параметра функции [3]. На рисунке 11 видно, что для параметра x функции localAndGlobalArgumentsCollision(int) задано значение по умолчанию 1 (строка 11). Далее в коде (строки 28-29) видно, что в цикле, в котором вызывается функция, значение параметра передается только при четных значениях переменной цикла i, во всех остальных случаях функция localAndGlobalArgumentsCollision(int) вызывается без аргументов. Тем не менее, по результату работы программы на рисунке 12 видно, что при незаданном значении параметра функции используется параметр по умолчанию, во всех остальных случаях он замещается переданным значением.
В С++ так же имеются лямбда-функции или лямбда-выражения. Лямбда-функции – это функции, имеющие особенное объявление, которые можно хранить в переменных, передавать как параметры других функций или использовать, как независимые функции внутри функций [8]. На рисунке 13 объявляется переменная lambdaFunction типа auto, в которую записывается лямбда-функция типа void без параметров (строка 15). Далее переменная с функцией вызывается как обычная функция (строка 17).
Рисунок 11. Пример параметров функции с начальным значением на С++
Рисунок 12. Результат работы программы
Код, приведенный на рисунке 13, эквивалентен коду на рисунке 14, с тем лишь отличием, что в данном случае лямбда-функция не присваивается переменной, она вызывается сразу после того, как объявлена. Результат работы обеих программ виден на рисунке 15.
Рисунок 13. Пример лямбда-функции на С++
Рисунок 14. Пример лямбда-функции на С++
Рисунок 15. Результат работы программы
Рассмотрим пример, в котором в лямбда-функцию передаются аргументы [8]. На рисунке 16 виден аналог рассмотренной ранее функции _functionSumm(int, int), которая вычисляет сумму двух целочисленных значений. В цикле данная лямбда-функция объявляется и сразу же вызывается пять раз (строка 17).
Рисунок 16. Пример передачи аргументов в лямбда-функцию на С++
При этом лямбда-функция способна также и возвращать значение [8]. Переделаем предыдущий пример так, чтобы лямбда-функция возвращала сумму чисел-параметров и напечатаем на экран то, что она вернет после вызова (рисунок 17). Фактически, текущий и предыдущий пример тоже эквивалентны. Результат работы обеих программ приведен на рисунке 18.
Рисунок 17. Пример возвращения значения лямбды-функции на С++
Рисунок 18. Результат работы программы
Теперь рассмотрим случай, при котором лямбда-функция сама выступает параметром для другой функции. На рисунке 19 видно, что объявленная прямо в вызове функции lambdaFunction(func, int) лямбда-функция передается туда, как параметр (строка 24). Следует отметить, что для этой передачи понадобилось объявить отдельный тип данных для функции с целочисленным параметром (строка 5). После получения лямбда-функции в качестве параметра lambdaFunction(func, int) вызывает ее затем как обычную функцию [3]. Результат работы программы представлен на рисунке 20.
Рисунок 19. Пример на С++, когда лямбда-функция выступает в качестве параметра функции.
Рисунок 20. Результат работы программы
С++ представляет для функций такую опцию, как перегрузка. Перегрузка функций – явление, при котором имеются несколько функций с разными типами данных, наборами параметров, а также, возможно, и алгоритмами [3].
На рисунках 22 и 23 представлены результаты работы программы с перегрузкой функции и без. Перейдем к коду, который привел к этим результатам.
Снова рассмотрим пример с _functionSumm на рисунке 21, однако на этот раз создадим два аналога этой функции – первый с типом double и такими же типами параметров (строка 5), а второй типа int и такими же типами параметров (строка 12). Внутри функций настроим сообщения так, чтобы они выводили на экран тип данных, с которым работает функция (строка 7, строка 14). Затем создадим два набора данных – две пары чисел, одну целочисленного типа (строка 27) и другую дробного (строка 28). Затем вызовем для обоих наборов данных функцию _functionSumm (строки 29-30). Как видно на рисунке 22, нужная функция для определенного типа данных подобралась автоматически.
Однако если убрать функцию int _functionSumm(int, int), результат будет иным (рисунок 23) – оба раза вызовется функция для расчёта суммы дробных чисел. Это произошло потому, что типы int и double могут конвертироваться автоматически один в другой, так как оба являются численными типами. Если попытаться вызвать ту же функцию для строк – произойдет ошибка [3].
Таким образом, С++ подбирает для набора данных наиболее подходящий вариант функции из существующих с учетом ограничений, свойственных для языка в силу строгой типизации.
Рисунок 22. Результат работы функции с перегрузкой
Рисунок 23. Результат работы функции без перегрузки
Рисунок 21. Пример перегрузки на С++
3.2. Примеры и ограничения использования функций в Assembler
В языке программирования Ассемблер имеются процедуры и функции. Здесь следует учитывать тот факт, что единственное отличие процедур от функций – это отсутствие в первых возвращаемого значения [1]. Таким образом, в данной работе следует рассматривать как процедуры, так и функции, поскольку, если учитывать формальное определение функции в целом, процедуры технически тоже являются функциями.
Важным понятием для работы с функциями в Ассемблере является соглашение о вызове. Соглашение о вызове – это свод правил, определенных заранее, согласно которым будет осуществляться вызов функции, передача аргументов, возвращение результата или другие действия. В случае, если соглашение о вызове с вызывающей стороны не совпадает с соглашением о вызове вызываемой стороны, ошибки неизбежны.
Для Ассемблера преимущественно используются два наиболее распространенных сообщения о вызове – stdcall и cdecl. По факту два этих соглашения не слишком различны – они отличаются в очистке стека от переданных функции параметров. Согласно сdecl, очистка стека осуществляется вызывающей стороной, согласно stdcall – очисткой стека должна заниматься сама функция. Первое характерно для С-подобных языков, второе более свойственно для Windows API [1].
Благодаря системе соглашений о вызове возможно встроить вызов функции на ассемблере в код другого языка, при условии соблюдения соглашения о вызове.
Код процедуры выделяется из общего потока инструкций тем, что в начале следует макрос proc, а завершает весь набор макрос endp – это макросы [11]. На рисунке 24 представлена процедура на ассемблере.
Рисунок 24. Процедура на ассемблере
Как видно на рисунке 24, в начале следует макрос proc, затем идет имя функции/процедуры, и после – перечисление всех параметров через запятую.