Подпрограма е явление от програмирането, чрез което се описват поредица от програмни инструкции, които решават даден проблем – може да приема параметри и да връща стойност. Подпрограмата може да се приеме като строителен блок на програмата, т.к. в нея се извършва цялостната обработка на данни, която трябва да се извърши, за да се реши поставената задача. Подпрограмите могат да се дефинират в самата програма или в библиотеки, които могат да бъдат използвани от множество програми.
В различните програмни езици, за подпрограма се използват различни термини, като: процедура – Pascal, функция – Pascal, C, C++, Java, JavaScript, Python, метод – C#, или подпрограма – Visual Basic, Perl. Функцията връща резултат, а процедурата – не.
Подпрограмата функционира като част от по-голяма програма или друга подпрограма. Една от причините за използването на подпрограми е избягването на повторението на кода, т.к. повтарящият се код затруднява поддръжката на програмата и води до грешки. Затова когато се използва даден фрагмент от програмния код повече от един път, е добра практика да бъде дефиниран като отделна подпрограма (метод), за да може да се изпълнява многократно. По този начин при изпълнението на дадена програма, подпрограмата може да бъде извикана няколко пъти от различни места в кода, включително и от други подпрограми.
Морис Уилкс, Дейвид Уилър и Стенли Джил измислят концепцията за подпрограмата, като първоначално я наричат „затворена подпрограма“, което контрастира с т.нар. отворена подпрограма или макро.
Подпрограмите са важна част от програмиране и затова синтаксиса на много програмни езици включва поддръжка за писане и използване на подпрограми. Разумният подход за използване на подпрограми, напр. чрез структурно програмиране, намалява значително разходите за разработка и поддръжка на голяма програма като междувременно се повишава качеството и надеждността ѝ. Подпрограмите, често записвани в библиотеки, са важен механизъм за споделяне и търгуване със софтуер. Дисциплината на обектно-ориентираното програмиране е базирана на обекти и методи, които са подпрограми свързани с тези класове или обекти.
Основни понятия Съдържанието на подпрограмата е нейното „тяло“ – тази част от програмата, която се изпълнява когато подпрограмата е „извикана“.
Подпрограмата може да бъде написана така че да получи една или повече стойности от програмата, която я извиква параметри или условни параметри). Извикващата програма предоставя стойностите на тези параметри, наречени аргументи. Различните програмни езици могат да използват различни конвенции за предаването на аргументи: Конвенция Описание Използва се в: Извикване по стойност Аргументите се оценяват/изчисляват и „копие“ от изчислената стойност се предава на C, C++, Java Извикване по референция Референция към аргумент C, Fortran, PL/I Извикване по резултат Стойността на параметъра се копира обратно в аргумента след изпълнение на подпрограмата Ada OUT parameters Извикване чрез стойност-резултат Стойността на параметъра се копира обратно в началото при влизане в подпрограмата и отново след изпълнението и Algol Извикване по име Като макро – заменя параметрите с неизчислени аргументи Algol извикване по константна величина Като извикване по стойност с изключение на това, че тук параметърът се третира като константа PL/I NONASSIGNABLE parameters, Ada IN parameters
Подпрограмата може да върне изчислена стойност на програмата или подпрограмата, която я е извикала или да предостави различни стойности или изходящи параметри. Всъщност, най-честата употреба на подпрограмата е имплементиране на математически функции, в които целта на подпрограмата е само да се изчисли една или повече стойности на резултатите, които са изцяло определени от параметрите, подадени на подпрограмата. (Примерите могат да включват изчисляване на логаритъм на число или детерминанта на матрица.)
Подпрограмата може да има и т.нар. странични ефекти, като модифициране структурата на данните в паметта, четене или писане върху периферно устройство, създаване на , спиране на програмата или машината, дори забавяне изпълнението на програмата за даден час. Подпрограма със странични ефекти (допълнителни функционалности) може да връща различни резултати след всеки път, когато е извикана, дори и да е извикана с едни и същи аргументи. Пример за това е генераторът на случайни числа, който връща различна стойност всеки път, когато е повикан. Широко разпространената употреба на подпрограми с подобни ефекти е характерна за езиците с императивно програмиране imperative programming.
Една подпрограма може да бъде програмирана така, че да бъде извикана от едно или повече места. Този метод дава възможност за директно изпълнение на функции, дефинирани чрез математическа индукция и рекурсия „разделяй и владей“ алгоритъм.
Подпрограма, чиято цел е да изчисли булев израз (това означава да отговори с да/не на въпрос) предикат.
Поддръжка на езика Езиците за програмиране от високо ниво обикновено включват специфични конструкции, за да: разграничат тази част от тялото на програмата, което представлява подпрограмата зададе идентификатор (име) на подпрограмата специфицират имената и/или типа на данните на параметрите и/или върнатите стойности осигури отделен обхват за локалните променливи идентифицират променливи извън подпрограмата, които са достъпни в нея извикат подпрограма присвоят стойности на параметрите уточни връщаните стойности от тялото на подпрограмата връщане към извикващата програма разпореждане с върнатите от подпрограмата стойности обработват всякакви изключения, които могат да бъдат срещнати докато се извика и изпълни дадена подпрограма обединяват подпрограми в модул, библиотека, обект, клас и прочее.
В функционалните програмни езици като Haskell, подпрограмите могат да нямат (допълнителни функционалности) странични ефекти и винаги да връщат един и същ резултат ако са извикани многократно с едни и същи аргументи. Такива езици обикновено поддържат само функции, т.к. подпрограмите не връщат стойност и няма смисъл да бъдат използвани освен ако не се използват за страничен ефект (допълнителна функционалност).
В програмни езицикато C, C++, и C#, подпрограмите могат да бъдат наречени функции, но да не бъдат бъркани с математически функции.
Предимства Предимствата на разделяне на една програма на подпрограми включва: Разделяне на сложна задача на няколко по-прости стъпки: това е един от основните инструменти на структурното програмиране, заедно със структурите от данни намаляване на дублиращия се код в програмата позволява преизползване на кода в различни програми разделяне на голяма задача на няколко програмиста на различен етап от разработката на софтуера скриване на детайли относно имплементацията от потребителите на подпрограмата подобряване на възможностите за търсене traceability, например в повечето езици има начини за получаване на информация за поредица от извиквания, която включва имената на включените в пътеката подпрограми и може би дори и допълнителни данни, като имена на файлове и номера на редове; ако кодът не се разделя на подпрограми, дебъгването би било сериозно затруднено
Недостатъци Извикването на подпрограма (сравнено с използването на обикновен код) налага computational overhead в механизма на извикванията.
Подпрограмата обикновено изисква стандартен housekeeping код – и при входа и изхода
История Поддръжка на езика В ранните асемблери поддържането на подпрограми е било доста ограничено. Подпрограмите не са били отделени една от друга или от главната програма и кодът на подпрограмата може да се премеси с този на другите подпрограми. Някои асемблери могат да предложат предефинирани макроси за генериране на извикването на подпрограмата и връщането на резултат. По-късните асемблери (1960) имат много по-усъвършенствана поддръжка на подпрограми.
Самомодифициращ се код Първата употреба на подпрограми е била на ранните компютри, програмирани с машинен код или асемблерни езици и са нямали точно определени инструкции за извикване. На тези компютри, всяко извикване на подпрограма е трябвало да бъде имплементирано като поредица от машинни инструкции на ниско ниво базирани на self-modifying code. Чрез замяната на операнд на branch instruction в края на тялото на процедурата, изпълнението на подпрограмата може да се върне на правилната локация (посочено от върнатия адрес) е извикващата програма (обикновено точно след инструкцията, която се прехвърля в подпрограмата.
Подпрограмни библиотеки Дори и с този тромав подход, подпрограмите са много полезни. Те позволяват един и същи код да бъде използван в много и различни програми. Освен това, паметта е била много ограничен ресурс при първите компютри и чрез използването на подпрограми се позволяват значителни икономии в това отношение.
В много от първите компютри, програмните инструкции са били въведени в паметта от перфолента. В този случай всяка подпрограма може да бъде предоставена от отделна лента, заредена и обединена преди или след главната програма; същата подпрограмата може да бъде използвана от много различни програми. Подобен подход е бил използван и от компютри, при които се е работело с перфокарти перфокарта. Името подпрограмни библиотеки първоначално е означавало библиотека, в директен смисъл, която е съдържала индексирани групи от подобни ленти или карти за употреба от повече от един човек.
Връщане чрез индиректно прехвърляне За да се премахне нуждата от самомодифициращия се код, разработчиците са включили indirect jump инструкция, чиито операнд, вместо самия той да бъде адреса за връщане, е съхранявал местоположението на променлива или регистър съдържащ в себе си адресът за връщане.
На тези компютри вместо да се модифицира връщането на контрола от подпрограмата, извикващата програма съхранява адресът за връщане в променлива, така че когато завърши изпълнението на подпрограмата, тя ще изпълни индиректно прехвърляне, което ще насочи изпълнението към позицията посочена от дефинираната вече променлива.
Преминаване към изпълнение на подпрограма Друго подобрение е инструкцията за преминаване към изпълнение на подпрограмата, което комбинира записването на първоначалния адрес с calling jump, като по този начин минимизира overhead значително.
Например при IBM System/360, инструкции BAL или BALR създадени за извикване на подпрограма, биха запазили адреса за връщане в регистър на процесора упоменат в конкретната инструкция. За да се върне, подпрограмата трябва само да изпълни индиректна инструкция (BR) чрез този регистър. Ако този регистър е необходим на подпрограмата за друга цел (като например извикването на друга подпрограма), тя би записала съдържанието на регистъра в отделена част от паметта или стек регистър.
При HP 2100, инструкцията JSB би извършила подобни действия, само че адресът за връщане е бил съхраняван в чатта от паметта, която е била целта на инструкцията. Изпълнението на подпрограмата всъщност би започнала от следващата клетка на паметта. При HP 2100 assembly language, би трябвало да се запише например: ... JSB MYSUB (Calls subroutine MYSUB.) Извиква подпрограмата MYSUB BB ... (Ще се върне тук след като MYSUB приключи.)
за да извика подпрограма именована MYSUB от главната програма. Подпрограмата би била написана така: MYSUB NOP (Склад за адреса за връщане на MYSUB.) AA ... (Началото на тялото на MYSUB.) ... JMP MYSUB,I (Връща се към извикващата програма.) Инструкцията JSB поставя адреса на инструкцията NEXT (namely, BB) на позицията посочена като неин операнд (namely, MYSUB), и след това свързана с NEXT позицията (namely, AA = MYSUB + 1). След това подпрограмата би могла да върне контрола на главната програма чрез изпълнението на индиректното прехвърляне JMP MYSUB,I which branched to the location stored at location MYSUB.