Типы и функции / Хабр
Это третья статья в цикле «Теория категорий для программистов».
Категория типов и функций играет важную роль в программировании, так что давайте поговорим о том, что такое типы, и зачем они нам нужны.
Кому нужны типы?
В сообществе есть некоторое несогласие о преимуществах статической типизации против динамической и сильной типизации против слабой. Позвольте мне проиллюстрировать выбор типизации с помощью мысленного эксперимента. Представьте себе миллионы обезьян с клавиатурами, радостно жмущих случайные клавиши, которые пишут, компилируют и запускают программы.
С машинным языком, любая комбинация байтов производимая обезьянами будет принята и запущена. Но в высокоуровневых языках, высоко ценится то, что компилятор способен обнаружить лексические и грамматические ошибки. Многие программы будут просто отвергнуты, а обезьяны останутся без бананов, зато остальные будут иметь больше шансов быть осмысленными. Проверка типов обеспечивает еще один барьер против бессмысленных программ. Кроме того, в то время как в динамически типизированных языках несоответствия типов будут обнаружены только во время выполнения, в строго типизированных статически проверяемых языках несоответствия типов обнаруживаются во время компиляции, что отсеивает множество некорректных программ, прежде чем у них есть шанс быть запущенными.
Итак, вопрос в том, хотим ли мы, чтобы обезьяны были счастливы, или создавать корректные программы?
(прим. переводчика: не стоит оскорбляться, автор просто любит менее скучные метафоры, чем ГСЧ и «случайные последовательности байт», а не называет программистов обезьянами).
Обычно цель мысленного эксперимента с печатающими обезьянами — создание полного собрания сочинений Шекспира (прим. переводчика: или Война и Мир Толстого). Проверка орфографии и грамматики в цикле резко увеличит шансы на успех. Аналог проверки типов пойдет еще дальше: после того, как Ромео объявлен человеком, проверка типов убедится, что на нем не растут листья и что он не ловит фотоны своим мощным гравитационным полем.
Типы нужны для компонуемости
Теория категорий изучает композиции стрелок. Не любые две стрелки могут быть скомпонованы: целевой объект одной стрелки должен совпадать с исходным обьектом следующей. В программировании мы передаем результаты из одной функции в другую. Программа не будет работать, если вторая функция не может правильно интерпретировать данные, полученные с помощью первой. Обе функции должны подходить друг к другу, чтобы их композиция заработала. Чем сильнее система типов языка, тем лучше это подхождение можно описать и автоматически проверить.
Единственный серьезный аргумент, который я слышу против строгой статической типизации: она может отвергнуть некоторые программы, которые семантически верны. На практике это случается крайне редко (прим. переводчика: во избежания срача замечу, что тут автор не учел, или несогласен, что есть много стилей, и привычный программсистом на скриптовых языках duck-typing тоже имеет право на жизнь.
Другой аргумент, который я часто слышу, в том, что строгая типизация накладывает слишком много нагрузки на программиста. Я могу сочувствовать этой проблеме, так как сам написал несколько обьявлений итераторов в С++, только вот есть технология, вывод типов, которая позволяет компилятору вывести большинство типов из контекста, в котором они используются. В С++, вы можете объявить переменную auto, и компилятор выведет тип за вас.
В Haskell, за исключением редких случаев, аннотации типа являются опциональными. Программисты, как правило, все равно их используют, потому что типы могут многое рассказать о семантике кода, и обьявления типов помогают понимать ошибки компиляции. Обычная практика в Haskell — начинать проект с разработки типов. Позже, аннотации типов являются основой для реализации и становятся гарантированными компилятором комментариями.
Строгая статическая типизация часто используется в качестве предлога для нетестирования кода. Иногда вы можете услышать, как Haskell-программисты говорят: «Если код собирается, он правильный.» Конечно, нет никакой гарантии, что программа, корректная с точки зрения типов, коректна в смысле правильного результата. В результате такого отношения в ряде исследований Haskell не стал сильно опережать остальные языки по качеству кода, как можно было бы ожидать. Кажется, что в коммерческих условиях необходимость чинить баги существует только до определенного уровня качества, что в основном связано с экономикой разработки программного обеспечения и толерантности конечного пользователя, и очень слабо связано с языком программирования или методологией разработки.
Теперь, что касается утверждения, что модульное тестирование может заменить строгую типизацию. Рассмотрим общую практику рефакторинга в строго типизированных языках: изменение типа аргумента какой-либо функции. В сильно типизированных языках достаточно изменить декларацию этой функции, а затем исправить все ошибки сборки. В слабо типизированных языках, тот факт, что функция в теперь ожидает другие данные не может быть связан с вызывающей стороной.
Модульное тестирование может поймать некоторые из несоответствий, но тестирование практически всегда вероятностный, а не детерминированный процесс (прим. переводчика: возможно, имелся ввиду набор тестов: вы покрываете не все возможные входы, а некую репрезентативную выборку.) Тестирование — плохая замена доказательству корректности.
Что такое типы?
Простейшее описание типов: они представляют собой множества значений. Типу Bool (помните, конкретные типы начинаются с заглавной буквы в Haskell) соответствует множество из двух элементов: True и False. Тип Char — множество всех символов Unicode, например ‘a’ или ‘ą’.
Множества могут быть конечными или бесконечными. Тип String, который, по сути, синонимом списка Char, — пример бесконечного множества.
Когда мы обьявляем x, как Integer:
x :: Integer
мы говорим, что это элемент множества целых чисел. Integer в Haskell — бесконечное множество, и может быть использовано для арифметики любой точности. Есть и конечное множество Int, которое соответствует машинному типу, как int в C++.
Есть некоторые тонкости, которые делают приравнивание типов к множествам сложным. Есть проблемы с полиморфными функциями, которые имеют цикличные определения, а также с тем, что вы не можете иметь множество всех множеств; но, как я и обещал, я не буду строгим математиком. Важно то, что есть категория множеств, которая называется Set, и мы с ней будем работать.
В Set, объекты — это множества, а морфизмы (стрелки) — функции.
Set — особая категория, потому что мы можем заглянуть внутрь ее объектов и это поможет многое интуитивно понять. Например, мы знаем, что пустое множество не имеет элементов. Мы знаем, что существуют специальные множества из одного элемента. Мы знаем, что функции отображают элементы одного множества в элементы другого. Они могут отображать два элемента в один, но не один элемент в два. Мы знаем, что тождественная функция отображает каждый элемент множества в себя, и так далее. Я планирую постепенно забывать всю эту информацию и вместо этого выразить все эти понятия в чисто категорийной форме, то есть в терминах объектов и стрелок.
В идеальном мире мы могли бы просто сказать, что типы в Haskell — множества, а функции в Haskell — математические функции между ними. Существует только одна маленькая проблема: математическая функция не выполняет какой-либо код — она знает только ответ. Функция в Haskell должна ответ вычислять.
f :: Bool -> Bool
может вернуть True, False, или _|_; последнее значит, что функция никогда не завершается.
Интересно, что, как только вы принимаете bottom в систему типов, удобно рассматривать каждую ошибку времени исполнения за bottom, и даже позволить функции возвращать bottom явно.
Последнее, как правило, осуществляется с помощью выражения undefined:f :: Bool -> Bool f x = undefined
Это определение проходит проверку типов потому, что undefined вычисляется в bottom, которое включено во все типы, в том числе и Bool. Можно даже написать:
f :: Bool -> Bool f = undefined
(без x) потому, что bottom еще и член типа Bool -> Bool.
Функции, которые могут возвращать bottom, называются частичными, в отличие от обычных функций, которые возвращают правильные результаты для всех возможных аргументов.
Из-за bottom, категория типов Haskell и функций, называется Hask, а не Set. С теоретической точки зрения, это источник нескончаемых осложнений, поэтому на данном этапе я использую мой нож мясника и завершу эти рассуждения. С прагматической точки зрения, можно игнорировать незавершающиеся функции и bottom и работать с Hask как с полноценным Set.
Зачем нам математическая модель?
Как программист, вы хорошо знакомы с синтаксисом и грамматикой языка программирования. Эти аспекты языка, как правило, формально описываются в самом начале спецификации языка. Но смысл и семантику языка гораздо труднее описать; это описание занимает намного больше страниц, редко достаточно формально, и почти никогда не полно. Отсюда никогда не заканчивающиеся дискуссии среди языковых юристов, и вся кустарная промышленность книг, посвященных толкованию тонкостей языковых стандартов.
Есть формальные средства для описания семантики языка, но из-за их сложности они в основном используются для упрощенных, академических языков, а не реальных гигантов промышленного программирования. Один из таких инструментов называется операционная семантика и описывает механику исполнения программы. Он определяет формализованный, идеализированный интерпретатор. Семантика промышленных языков, таких как C++, как правило, описывается с помощью неформального рассуждения, часто в терминах «абстрактной машины».
Проблема в том, что что о программах, использующих операционную семантику, очень трудно что-то доказать. Чтобы показать некое свойство программы вы, по сути, должны «запустить ее» через идеализированный интерпретатор.
Не важно, что программисты никогда формально не доказывают корректность. Мы всегда «думаем», что мы пишем правильные программы. Никто не сидит за клавиатурой, говоря: «О, я просто напишу несколько строк кода и посмотрю, что происходит.» (прим. переводчика: ах, если бы…) Мы считаем, что код, который мы пишем, будет выполнять определенные действия, которые произведут желаемые результаты. Мы, как правило, очень удивлены, если это не так. Это означает, что мы действительно думаем о программах, которые мы пишем, и мы, как правило, делаем это, запуская интерпретатор в наших головах. Просто, очень трудно уследить за всеми переменными. Компьютеры хороши для исполнения программ, люди — нет! Если бы мы были, нам бы не понадобились компьютеры.
Но есть и альтернатива. Она называется денотационной семантикой и основана на математике. В денотационной семантике для каждой языковой конструкции описана математичесая интерпретация. Таким образом, если вы хотите доказать свойство программы, вы просто доказываете математическую теорему. Вы думаете, что доказывание теорем трудно, но на самом деле мы, люди, строили математические методы тысячи лет, так что есть множество накопленных знаний, которые можно использовать. Кроме того, по сравнению с теоремами, которые доказывают профессиональные математики, задачи, с которыми мы сталкиваемся в программировании, как правило, довольно просты, если не тривиальны. (прим. переводчика: для доказательства, автор не пытается обидеть программистов.)
Рассмотрим определение функции факториала в Haskell, языке, легко поддающемуся денотационной семантике:
fact n = product [1..n]
Выражение [1..n] — это список целых чисел от 1 до n. Функция product умножает все элементы списка. Точно так, как определение факториала, взятое из учебника. Сравните это с C:
int fact(int n) { int i; int result = 1; for (i = 2; i <= n; ++i) result *= i; return result; }
Нужно ли продолжать? (прим. переводчика: автор слегка схитрил, взяв библиотечную функцию в Haskell. На самом деле, хитрить было не нужно, честное описание по определению не сложнее):
fact 0 = 1 fact n = n * fact (n - 1)
Хорошо, я сразу признаю, что это был дешевый прием! Факториал имеет очевидное математическое определение. Проницательный читатель может спросить: Какова математическая модель для чтения символа с клавиатуры, или отправки пакета по сети? Долгое время это был бы неловкий вопрос, ведущий к довольно запутанным объяснениям. Казалось, денотационная семантика не подходит для значительного числа важных задач, которые необходимы для написания полезных программ, и которые могут быть легко решаемы операционной семантикой. Прорыв произошел из теории категорий. Еугенио Моджи обнаружил, что вычислительные эффекты могут быть преобразованы в монады. Это оказалось важным наблюдением, которое не только дало денотационной семантике новую жизнь и сделало чисто функциональные программы более удобными, но и дало новую информацию о традиционном программировании. Я буду говорить о монадах позже, когда мы разработаем больше категорийных инструментов.
Одним из важных преимуществ наличия математической модели для программирования является возможность выполнить формальное доказательство корректности программного обеспечения. Это может показаться не столь важным, когда вы пишете потребительский софт, но есть области программирования, где цена сбоя может быть огромной, или там, где человеческая жизнь находится под угрозой. Но даже при написании веб-приложений для системы здравоохранения, вы можете оценить ту мысль, что функции и алгоритмы из стандартной библиотеки языка Haskell идут в комплекте с доказательствами корректности.
Чистые и Грязные функции
То, что мы называем функциями в C++ или любом другом императивном языке, не то же самое, что математики называют функциями. Математическая функция — просто отображение значений в значения.
Мы можем реализовать математическую функцию на языке программирования: такая функция, имея входное значение будет рассчитать выходное значение. Функция для получения квадрата числа, вероятно, умножит входное значение само на себя. Она будет делать это при каждом вызове, и гарантированно произведет одинаковый результат каждый раз, когда она вызывается с одним и тем же аргументом. Квадрат числа не меняется с фазами Луны.
Кроме того, вычисление квадрата числа не должно иметь побочного эффекта, вроде выдачи вкусного ништячка вашей собаке. «Функция», которая это делает, не может быть легко смоделирована математической функцей.
В языках программирования функции, которые всегда дают одинаковый результат на одинаковых аргументах и не имеют побочных эффектов, называются чистыми. В чистом функциональном языке, наподобие Haskell, все функции чисты. Благодаря этому проще определить денотационную семантику этих языков и моделировать их с помощью теории категорий. Что касается других языков, то всегда можно ограничить себя чистым подмножеством, или размышлять о побочных эффектах отдельно. Позже мы увидим, как монады позволяют моделировать все виды эффектов, используя только чистые функции. В итоге мы ничего не теряем, ограничиваясь математическими функциями.
Примеры типов
Как только вы решите, что типы — это множества, вы можете придумать некоторые весьма экзотические примеры. Например, какой тип соответствует пустому множеству? Нет, это не void в C++, хотя этот тип называется Void в Haskell. Это тип, который не наполнен ни одним значением. Вы можете определить функцию, которая принимает Void, но вы никогда не сможете ее вызвать. Чтобы ее вызвать, вам придется обеспечить значение типа Void, а его там просто нет. Что касается того, что эта функция может вернуть — не существует никаких ограничений. Она может возвращать любой тип (хотя этого никогда не случится, потому что она не может быть вызвана). Другими словами, это функция, которая полиморфна по возвращаемому типу. Хаскеллеры назвали ее:
absurd :: Void -> a
(прим. переводчика: на С++ такую функцию определить невозможно: в С++ у каждого типа есть хотя бы одно значение. )
(Помните, что a — это переменная типа, которая может быть любым типом.) Это имя не случайно. Существует более глубокая интерпретация типов и функций с точки зрения логики под названием изоморфизм Карри-Говарда. Тип Void представляет неправдивость, а функция absurd — утверждение, что из ложности следует что-нибудь, как в латинской фразе «ex falso sequitur quodlibet.» (прим. переводчика: из ложности следует что угодно.)
Далее идет тип, соответствующий одноэлементному множеству. Это тип, который имеет только одно возможное значение. Это значение просто «есть». Вы могли сразу его не признать, но это void в C++. Подумайте о функциях от и в этот тип. Функция из void всегда может быть вызвана. Если это чистая функция, она всегда будет возвращать один и тот же результат. Вот пример такой функции:
int f44() { return 44; }
Вы можете подумать что эта функция принимает «ничего», но, как мы только что видели, функция, которая принимает «ничего» не может быть вызвана, потому что нет никакого значения, представляющего тип «ничего». Итак, что же эта функция принимает? Концептуально, она принимает фиктивное значение, у которого есть только единственный экземпляр, так что мы можем явно его не указывать в коде. В Haskell, однако, есть символ этого значения: пустая пара скобок (). Таким образом, из за забавного совпадения (или не совпадения?), вызов функции от void выглядит одинаково и в C++ и в Haskell. Кроме того, из-за любви Хаскеля к лаконичности, тот же символ () используется и для типа, конструктора и единственного значения, соответствующего одноэлементному множеству. Вот эта функция в Haskell:
f44 :: () -> Integer f44 () = 44
Первая строка обьявляет, что f44 преобразует тип (), названный «единица», в тип Integer. Вторая строка определяет, что f44 с помощью паттерн-матчинга преобразует единственный конструктор для единицы, а именно () в число 44. Вы вызываете эту функцию, предоставляя значение ():
f44 ()
Обратите внимание, что каждая функция от единицы эквивалентна выбору одного элемента из целевого типа (здесь, выбирается Integer 44). На самом деле, вы можете думать о f44, как ином представлении числа 44. Это пример того, как мы можем заменить прямое упоминание элементов множества на функцию (стрелку). Функции из единицы в некий тип А находятся во взаимно-однозначном соответствии с элементами множества A.
А как насчет функций, возвращающих void, или, в Haskell, возвращающих единицу? В C++ такие функции используются для побочных эффектов, но мы знаем, что такие функции — не настоящие, в математическом смысле этого слова. Чистая функция, которая возвращает единицу, ничего не делает: она отбрасывает свой аргумент.
Математически, функция из множества А в одноэлементное множество отображает каждый элемент в единственный элемент этого множества. Для каждого А есть ровно одна такая функция. Вот она для Integer:
fInt :: Integer -> () fInt x = ()
Вы даете ей любое целое число, и она возвращает единицу. Следуя духу лаконичности, Haskell позволяет использовать символ подчеркивания в качестве аргумента, который отбрасывается. Таким образом, не нужно придумывать для него название. Код выше можно переписать в виде:
fInt :: Integer -> () fInt _ = ()
Обратите внимание, что выполнение этой функции не только не зависит от значения, ей переданного, но и от типа аргумента.
Функции, которые могут быть определены одной и той же формулой для любого типа называются параметрически полиморфными. Вы можете реализовать целое семейство таких функций одним уравнением, используя параметр вместо конкретного типа. Как назвать полиморфную функцию из любого типа в единицу? Конечно, мы назовем ее unit:
unit :: a -> () unit _ = ()
В C++ вы бы реализовали ее так:
template<class T> void unit(T) {}
(прим. переводчика: дабы помочь компилятору оптимизировать ее в noop, лучше так):
template<class T> void unit(T&&) {}
Далее в «типологии типов» набор из двух элементов. В C++ он называется bool, а в Haskell, что не удивительно, Bool. Разница в том, что в C++ bool является встроенным типом, в то время как в Haskell он может быть определен следующим образом:
data Bool = True | False
(Читать это определение стоит так: Bool может быть или True или False.) В принципе, можно было бы описать этот тип и в C++:
enum bool { true, false };
Но C++ перечисление на самом деле целое число. Можно было бы использовать C++11 «class enum», но тогда пришлось бы уточнять значение именем класса: bool::true или bool::false, не говоря уже о необходимости включать соответствующий заголовок в каждом файле, который его использует.
Чистые функции из Bool просто выбирают два значения из целевого типа, одно, соответствующее True и другое — False.
Функции в Bool называются предикатами. Например, библиотека Data.Char в Haskell содержит много предикатов, например IsAlpha или isDigit. В C++ есть похожая библиотека <cctype>, которая обьявляет, помимо прочего, функции isalpha и isdigit, но они возвращают int, а не булевое значение. Настоящие предикаты определены в <locale> и называются ctype::is(alpha, c) и ctype::is(digit, c).
Теория категорий для программистов: предисловие
Категория: суть композиции
Типы и функции
Категории, большие и малые
Категории Клейсли
c++ — Выполнение функции в условии if
Вопрос задан
Изменён 2 года 1 месяц назад
Просмотрен 427 раз
Если я выполняю функцию в условии
if (function() == 0) {...}
она выполнится абсолютно также как и вне условия, это не влечет никаких побочных эффектов?
Чем такой вызов отличается от вызова вне условия?
1
Нет, это ни на что не влияет. Никакой разницы нет.
В соседнем ответе вспомнили про short-circuiting — свойство операторов &&
и ||
не вычислять второй операнд (и не вызывать в нем функции), и первого операнда достаточно для определения результата.
Но это происходит везде — и внутри условий if
-ов, и снаружи.
13
Если функция выполняется, то она выполняется точно так же, как и вне условия.
Вся тонкость в «если выполняется» — в сложном выражении с операторами &&
и ||
, для которых справедливо сокращенное вычислоение, она может не выполниться. Сокращенное вычисление — это если вычисляя условия, в некоторый момент становится очевидно значение полного условия, то остальные части не вычисляются. Например, в A && B
, если вычисление A
дает false
, каким бы ни было B
— результат будет false
. В таком случае B
не вычисляется.
Так что в
if (i == 0 && function() == 0)
при i
, не равном 0, function()
вычисляться просто не будет.
2
Также скажу, что в С++ может быть код, в котором вызов функции не приводит к её выполнению. Например, если вызов функции — это аргумент decltype
:
#include <iostream> int func() { std::cout << "Hello from func!" << std::endl; return 123; } int main() { decltype(func()) var; return 0; }
Этот код ничего не отобразит на экране, потому что чтобы определить тип возвращаемого значения, можно просто посмотреть его не вызывая функцию.
Зарегистрируйтесь или войдите
Регистрация через Google
Регистрация через Facebook
Регистрация через почту
Отправить без регистрации
Почта
Необходима, но никому не показывается
Отправить без регистрации
Почта
Необходима, но никому не показывается
Нажимая на кнопку «Отправить ответ», вы соглашаетесь с нашими пользовательским соглашением, политикой конфиденциальности и политикой о куки
Что такое функции и их преимущества в языке C?
Ниширика | 29 января 2017 г. | c программирование | 1 Комментарий
Функция — это группа операторов, которые выполняются всякий раз, когда функция вызывается для выполнения определенной назначенной задачи.
Программирование на языке C использует модульность для устранения сложности программы. Программист делит программу на разные модули или функции и при необходимости обращается к определенным функциям. Каждая программа на C имеет по крайней мере одну функцию. Наиболее распространенная функция, которую мы используем в нашем повседневном программировании, — это 9.0009 функция main() . Компилятор всегда сначала выполняет функцию main() , а затем любую другую функцию (если она вызывается из основного метода).
Функция в основном представляет собой блок операторов, который выполняет определенную задачу. Предположим, что задача должна выполняться непрерывно со многими данными в разные моменты времени, например, одна в начале программы и одна в конце программы, поэтому вместо того, чтобы писать один и тот же фрагмент кода дважды, человек может просто написать его в функцию и вызовите его дважды. И после выполнения любого функционального блока управление всегда возвращается к функция main() .
Содержание
1. Преимущества использования функций
Вот несколько преимуществ использования функций в вашем коде:
- Использование функций улучшает читаемость программы. Большой код всегда трудно читать. Разбивая код на более мелкие функции, программа остается организованной, простой для понимания и делает ее пригодной для повторного использования.
- Компилятор C выполняет сверху вниз , поэтому поток управления может быть легко управляется в случае функций. Управление всегда будет возвращаться к функции main() .
- Снижает сложность программы и придает модульную структуру .
- В случае, если нам нужно протестировать только определенную часть программы, нам придется запустить всю программу и выяснить ошибки, что может быть довольно сложным процессом. Другим преимуществом здесь является то, что функции могут тестироваться по отдельности , что более удобно, чем описанный выше процесс.
- Функцию можно использовать для создания собственного файла заголовка , который можно использовать в любом количестве программ, т. е. возможность повторного использования.
С таким количеством преимуществ функции являются благом для любого программиста. Давайте узнаем больше об этих функциях:
2. Типы функций
Помимо функций, которые программисты создают в соответствии со своими требованиями, компиляторы C имеют некоторые встроенные функции, которые программист может использовать в любое время. Язык программирования C имеет два типа функций:
- Встроенные функции/библиотека функций
- Пользовательские функции
2.1. Библиотечные функции
Эти функции уже определены в компиляторах C. Они используются для обработки строк, операций ввода-вывода и т. д. Эти функции определены в заголовочном файле . Чтобы использовать эти функции, нам нужно импортировать определенные заголовочные файлы.
Например:
Библиотечная функция
- printf() показывает вывод в пользовательском формате.
- scanf() используется для ввода данных пользователем, который может быть символом, числовым значением, строкой и т.д.
- getchar() принимает ввод символов от пользователя.
- gets() читает строку.
Библиотечная функция
- pow() находит степень заданного числа.
- sin() находит синус заданного числа.
- exp() находит показатель степени заданного числа.
- cos() находит косинус заданного числа.
- sqrt() находит квадратный корень из числа.
- log() находит логарифмическое значение числа.
Кроме
2.2. Пользовательские функции
Пользовательские функции создаются пользователем. Пользователь может запрограммировать его на выполнение любой желаемой функции. Это похоже на настройку функций, которые нам нужны в программе. Программа может иметь более одной пользовательской функции. Все пользовательские функции должны вызываться (прямо или косвенно) внутри функция main() для выполнения. Пользовательские функции могут быть добавлены в программу двумя способами. Либо с помощью определяемых пользователем заголовочных файлов , либо путем добавления функционального блока непосредственно в программу. Но самое главное — иметь функцию main() . Это упрощает кодирование и вызов других функций в его теле.
Любая функция имеет 4 строительных блока, которые необходимо объявить –
- Имя функции
- Параметры функции
- Тип возврата
- Выполняемые операторы
Для получения более подробной информации обо всех строительных блоках и полной информации о функциях, пожалуйста, прочитайте следующий пост User Defined Functions .
Подробнее :- Препроцессоры на языке программирования C
Полезные ссылки
Пожалуйста, следуйте Учебникам по программированию на C или Учебникам по программированию на C или серии для полного меню 9061 на боковой панели1.
Также примеры программ на C см. в Примеры программирования на C .
Все примеры размещены на Github .
Рекомендуемые книги
Инвестиции в знания всегда окупаются. Надеюсь, вам понравился урок. Приходите еще, потому что обучение прокладывает путь к лучшему пониманию
Не забудьте поделиться и подписаться.
Удачного кодирования!! 😊
Рекомендовано —
Теги:Язык программирования C, Учебники C
Встроенная функция в C | Как реализовать встроенную функцию в C с примерами
Встроенные функции в программировании на C играют важную роль как в небольшом, так и в сложном коде. Это экономит огромное количество времени выполнения, а также место в памяти. Для определения встроенной функции нам нужно использовать ключевое слово «inline». Каждый раз, когда вам нужна функция в коде, все, что нам нужно сделать, это логически определить встроенную функцию, а затем использовать ее столько раз, сколько вы хотите в коде, поскольку это поможет повысить скорость выполнения кода. Он мал для определения и может использоваться снова и снова после определения.
Синтаксис для определения встроенной функции в программировании на C:
встроенный тип_данных имя_функции (тип_данных аргумент1 , тип_данных аргумент2 ) { возвращаться () ; // вернуть, как определено пользователем. }
В приведенном выше синтаксисе ключевое слово inline является обязательным, чтобы компилятор знал во время выполнения кода, что после этой функции нам нужно с типом данных и ее именем. Под именем функции мы можем передать столько аргументов, сколько захотим, в зависимости от требований кода. В конце концов, оператор return также является обязательным в зависимости от того, какой результат мы хотим получить в соответствии с определением функции.
Примеры встроенной функции в C
Ниже приведены упомянутые примеры:
Пример #1
Для демонстрации работы встроенной функции в программировании.
Код:
#includeh> // определение встроенной функции с ключевым словом inline статический встроенный intfoo() { вернуть 10 ; // возвращаем выходное значение согласно требованию } // Основной код пользователя внутренний() { инткс ; // Вызов встроенной функции х = фу(); // делаем ссылку на встроенную функцию. printf("Результат встроенной функции: %d\n", x); вернуть 0 ; }
Вывод:
Объяснение:
- Как вы можете видеть в приведенном выше коде, мы определили встроенную функцию с именем «foo()» целочисленного типа данных, и она вернет значение 10 как объявляется всякий раз, когда мы делаем вызов данной встроенной функции.
- Следовательно, в основном коде целочисленная переменная с именем «x», чтобы мы могли сделать вызов встроенной функции через ссылку. Хотя мы также можем вызывать встроенную функцию foo() без какой-либо ссылки, хорошо сделать ссылку, чтобы избежать конфликтов.
Пример #2
Для демонстрации встроенной функции для сложения целых чисел.
Код:
#include// определение встроенной функции с ключевым словом inline для добавления статическое встроенное добавление (int a, int b) { вернуть а + б ; // возвращаем выходное значение согласно требованию } // Основной код пользователя внутренний() { инта, б, х; printf("Пожалуйста, введите значение целого числа a:"); scanf("%d", &a) ; printf("Пожалуйста, введите значение целого числа b:"); scanf("%d", &b); // Вызов встроенной функции по ссылке х = сложение ( а , б ) ; printf("Результат сложения заданных целых чисел: %d\n", x) ; вернуть 0 ; }
Вывод:
Объяснение:
- Как вы можете видеть в приведенном выше коде, мы определили встроенную функцию с именем «дополнение (int a, int b)» целочисленного типа данных, и она будет вернуть значение целых чисел a + b, так как мы передаем в функцию два целочисленных аргумента. Следовательно, в основном коде целочисленная переменная с именем «x», чтобы мы могли сделать вызов встроенной функции через ссылку.
- Хотя мы также можем вызывать встроенную функцию add( int a, int b ) без ссылки, лучше сделать ссылку, чтобы избежать конфликтов. После этого мы берем входные значения от пользователя для добавления, а затем вызываем встроенную функцию в конце.
Пример #3
Демонстрация встроенной функции для вычитания целых чисел.
Код:
#include// определение встроенной функции с ключевым словом inline статическое встроенное вычитание (int a, int b) { вернуть а-б; // возвращаем выходное значение согласно требованию } // Основной код пользователя внутренний() { инта, б, х; printf("Пожалуйста, введите значение целого числа a:"); scanf("%d", &a) ; printf("Пожалуйста, введите значение целого числа b:"); scanf("%d", &b); // Вызов встроенной функции х = вычитание ( а , б ) ; // делаем ссылку на встроенную функцию. printf("Результат вычитания заданных целых чисел: %d\n", x) ; вернуть 0 ; }
Вывод:
Объяснение:
- Как вы можете видеть в приведенном выше коде, мы определили встроенную функцию с именем «вычитание (int a, int b)» целочисленного типа данных, и она будет вернуть значение целых чисел a – b, так как мы передаем в функцию два целочисленных аргумента.