서브루틴

Subroutine

컴퓨터 프로그래밍에서 서브루틴은 하나의 단위로 포장된 특정 작업을 수행하는 프로그램 명령의 순서다.그런 다음 이 유닛은 특정 작업을 수행해야 하는 모든 프로그램에서 사용할 수 있다.

서브루틴은 프로그램 내에서 또는 많은 프로그램에서 사용할 수 있는 라이브러리에서 별도로 정의될 수 있다.다른 프로그래밍 언어에서 서브루틴은 루틴, 서브프로그램, 함수, 방법 또는 절차라고 불릴 수 있다.기술적으로 이 용어들은 모두 다른 정의를 가지고 있으며, 명칭은 언어마다 다르다.일반적이고 우산 용어콜러블 단위를 사용하기도 한다.[1]

서브프로그램이라는 명칭은 서브루틴이 더 큰 프로그램이나 다른 서브프로그램에서 한 단계로 사용되는 컴퓨터 프로그램과 거의 같은 방식으로 동작한다는 것을 암시한다.서브루틴은 다른 서브루틴을 포함하여 프로그램을 한 번 실행하는 동안 여러 장소에서 여러 번 시작할 수 있도록 코딩된 후 서브루틴의 작업이 완료되면 호출 후 다음 지침으로 분기(복귀)하는 경우가 많다.서브루틴의 아이디어는 처음에 존 모클리ENIAC에 관한 연구 중에 구상한 것으로,[2] 1947년 1월 하버드 심포지엄에서 "EDVAC형 기계의 문제 준비"에 관한 기록을 남겼다.[3]Maurice Wilkes, David Wheeler, Stanley Gill은 일반적으로 이 개념의 공식적인 발명을 인정받는데, 그들은 이것을 폐쇄 서브루틴이라고 불렀고,[4][5] 오픈 서브루틴이나 매크로와 대조된다.[6]그러나 Turing은 1945년 논문에서 NPL ACE에 대한 설계 제안에 대해 서브루틴을 논의하여 반환 주소 스택의 개념을 고안해냈다.[7]

서브루틴은 강력한 프로그래밍 도구로,[8] 많은 프로그래밍 언어구문에는 쓰기 및 사용에 대한 지원이 포함되어 있다.서브루틴을 현명하게 사용하면(예를 들어, 구조화된 프로그래밍 접근법을 통해) 대형 프로그램을 개발하고 유지하는 데 드는 비용을 상당히 줄이는 동시에 그것의 품질과 신뢰성을 높일 수 있다.[9]종종 도서관에 수집되는 서브루틴은 소프트웨어를 공유하고 거래하는 중요한 메커니즘이다.객체 지향 프로그래밍의 규율은 객체방법(이러한 객체 또는 객체 클래스에 부착된 서브루틴)에 기초한다.

스레드 코드라고 불리는 컴파일 방식에서 실행 가능한 프로그램은 기본적으로 서브루틴 호출의 순서다.

주요개념

서브루틴의 내용은 그 본체로서 서브루틴을 호출하거나 호출할 때 실행되는 프로그램 코드의 일부분이다.

서브루틴은 호출 프로그램으로부터 하나 이상의 데이터 값을 얻을 것으로 예상하도록 작성될 수 있다(파라미터 또는 형식 매개변수를 대체한다).호출 프로그램은 이러한 매개 변수에 대한 실제 값을 인수로 제공한다.다른 프로그래밍 언어는 인수 전달에 서로 다른 규칙을 사용할 수 있다.

컨벤션 설명 공용
가치별 호 인수가 평가되고 값의 복사본이 서브루틴으로 전달됨 파스칼, 델파이, 시물라, CPL, PL/M, 모둘라, 오버론, 에이다 등 알골 60 이후의 대부분의 알골 유사 언어에서 디폴트된다.C, C++, Java(물체 및 어레이에 대한 참조도 값으로 전달됨)
참조로 호출 인수에 대한 참조, 일반적으로 인수의 주소가 전달됨 알골 68, 파스칼, 델파이, 시물라, CPL, PL/M, 모둘라, 오베론, 에이다 등 대부분의 알골 60 이후의 언어에서 선택 가능하다.C++, 포트란, PL/I
결과별 호출 하위 루틴에서 반환할 때 매개 변수 값이 인수에 다시 복사됨 Ada OUT 매개변수
가치-결과별 호 하위 루틴에 대한 입력 시 파라미터 값이 다시 복사되고 반환 시 다시 복사됨 Algol, Swift in-out 매개변수
이름별 호칭 매크로와 마찬가지로 매개 변수를 평가되지 않은 인수 표현식으로 바꾼 다음 호출된 루틴이 매개 변수를 사용할 때마다 호출자의 컨텍스트에서 인수를 평가하십시오. 알골, 스칼라
상수 값에 의한 호출 매개 변수가 상수로 처리된다는 점을 제외하고 호출 기준 값과 유사 PL/I NOAssignable 매개 변수, Ada IN 매개 변수

서브루틴 호출은 또한 컴퓨터 메모리의 데이터 구조를 수정하거나, 주변 장치에서 읽거나, 쓰거나, 파일을 만들거나, 프로그램이나 기계를 정지시키거나, 심지어 프로그램 실행을 지정된 시간 동안 지연시키는 등의 부작용을 일으킬 수 있다.부작용이 있는 하위 프로그램은 같은 주장으로 호출되더라도 호출될 때마다 다른 결과를 반환할 수 있다.예를 들어, 여러 언어로 사용할 수 있는 임의의 숫자 서브루틴이 있으며, 호출될 때마다 다른 의사 무작위 번호를 반환한다.부작용과 함께 서브루틴을 광범위하게 사용하는 것은 명령적 프로그래밍 언어의 특징이다.

서브루틴은 그 임무를 수행하기 위해 하나 이상의 장소에서 반복적으로 자신을 호출할 수 있도록 코딩될 수 있다.이 방법은 수학적 유도재귀분할정복 알고리즘에 의해 정의된 기능의 직접 구현을 가능하게 한다.

하나의 부울함수(예/아니오 질문에 대답하는 것)를 계산하는 것이 목적인 서브루틴을 술어라고 부르기도 한다.논리 프로그래밍 언어에서, 주로 성공[vague] 또는 실패를 결정하기 때문에 종종[vague] 모든 서브루틴을 술어라고 부른다.[citation needed]

값을 반환하지 않거나 null 값을 반환하는 서브루틴을 프로시저라고 부르기도 한다.절차는 대개 그들의 주장을 수정하며 절차 프로그래밍의 핵심 부분이다.


기능들

함수는 값을 반환하는 서브루틴이다.[10]기능의 주된 목적은 복잡한 계산을 의미 있는 덩어리로 분해하고 이름을 붙이는 것이다.[11] 서브루틴은 계산된 값을 호출자에게 반환하거나(반환 값), 다양한 결과 값 또는 출력 파라미터를 제공할 수 있다.실제로 서브루틴의 일반적인 용도는 수학적 함수를 구현하는 것인데, 서브루틴의 목적은 서브루틴에 전달된 인수에 의해 값이 전적으로 결정되는 하나 이상의 결과를 계산하는 것이다. (예는 숫자의 로그행렬결정인자를 계산하는 것을 포함할 수 있다.)일부 언어에서 값을 반환하는 절차의 구문은 예를 들어 RETURINGS 절이 없는 경우를 제외하고 값을 반환하지 않는 절차의 구문과 본질적으로 동일하다.일부 언어에서 절차는 그 인수에 따라 값을 포함하거나 포함하지 않고 동적으로 반환하는 것을 선택할 수 있다.

언어 지원

고급 프로그래밍 언어는 일반적으로 다음을 위한 특정 구조를 포함한다.

  • 서브루틴을 구성하는 프로그램(본문) 부분 구분
  • 서브루틴에 식별자(이름) 할당
  • 매개 변수의 이름 및 데이터 유형 지정 및 반환 값
  • 임시 변수에 대한 개인 이름 지정 범위 제공
  • 서브루틴에서 액세스할 수 있는 서브루틴
  • 서브루틴 호출
  • 매개 변수에 값 제공
  • 주 프로그램은 하위 프로그램의 주소를 포함한다.
  • 서브프로그램은 메인프로그램에서 기능호출의 다음지령 주소를 포함한다.
  • 본문 내에서 반환 값 지정
  • 호출 프로그램으로 돌아가기
  • 호출에 의해 반환된 값 삭제
  • 통화 중에 발생하는 예외적인 조건 처리
  • 서브루틴을 모듈, 라이브러리, 객체 또는 클래스로 패키지화

Pascal, Fortran, Ada와 같은 프로그래밍 언어와 BASIC많은 방언들은 호출 프로그램에 명시적인 반환 값을 제공하는 함수나 함수 하위 프로그램과 그렇지 않은 서브루틴이나 절차를 구별한다.이러한 언어에서 함수 호출은 일반적으로 표현식에 포함되어 있다(예: a).sqrt함수라고 할 수 있다.y = z + sqrt(x)) 절차 호출은 문장으로서 구문적으로 행동한다(예: a).print절차라고 할 수 있다.if x > 0 then print(x)또는 다음과 같은 진술에 의해 명시적으로 호출된다.CALL또는GOSUB(예:call print(x)). CLisp와 같은 다른 언어는 기능과 서브루틴을 구분하지 않는다.

하스켈과 같은 엄격히 기능하는 프로그래밍 언어에서 하위 프로그램은 부작용이 없을 수 있으며, 이는 프로그램의 다양한 내부 상태가 변하지 않는다는 것을 의미한다.함수는 동일한 인수로 반복해서 호출할 경우 항상 동일한 결과를 반환한다.이러한 언어는 일반적으로 값을 반환하지 않는 서브루틴은 부작용을 일으킬 수 없는 한 소용이 없기 때문에 기능만 지원한다.

C, C++, C#같은 프로그래밍 언어에서 서브루틴은 단순히 함수(다른 개념인 수학 함수기능 프로그래밍과 혼동되지 않도록 함)라고도 할 수 있다.

언어의 컴파일러는 일반적으로 절차 호출과 복귀를 잘 정의된 호출 규칙에 따라 기계 명령으로 변환하여 서브루틴을 호출하는 프로그램과 별도로 컴파일할 수 있게 한다.호출과 회신문에 해당하는 명령 순서를 절차의 프롤로그와 에필로그라고 한다.

이점

프로그램을 서브루틴으로 분할할 때의 이점은 다음과 같다.

  • 복잡한 프로그래밍 작업을 더 간단한 단계로 분해: 이것은 데이터 구조와 함께 구조화된 프로그래밍의 두 가지 주요 도구 중 하나이다.
  • 프로그램 내 중복 코드 감소
  • 여러 프로그램에서 코드 재사용 가능
  • 대규모 프로그래밍 작업을 다양한 프로그래머 또는 프로젝트의 다양한 단계로 구분
  • 서브루틴 사용자로부터 구현 세부 정보 숨기기
  • 코드 블록을 코드 블록을 기능 호출로 대체하여 코드 블록을 설명하는 기능 호출로 코드 가독성 향상.이렇게 하면 기능을 재사용하지 않아도 호출 코드를 간결하고 읽을 수 있게 된다.
  • 추적성 향상(즉, 대부분의 언어는 관련 서브루틴의 이름과 파일 이름 및 라인 번호와 같은 더 많은 정보를 포함하는 통화 추적을 얻을 수 있는 방법을 제공한다); 코드를 서브루틴으로 분해하지 않음으로써 디버깅이 심각하게 손상될 수 있다.

단점들

인라인 코드를 사용하는 것과 비교하여 서브루틴을 호출하면 통화 메커니즘에 약간의 계산 오버헤드가 부과된다.[citation needed]

서브루틴은 일반적으로 기능(프로로그와 에필로그 기능 - 일반적으로 범용 레지스터반송 주소를 최소로 저장)으로 진입 및 퇴장 시 표준 하우스키핑 코드를 요구한다.

역사

서브루틴의 아이디어는 컴퓨터 기계가 이미 얼마간 존재한 후에 해결되었다.산술과 조건부 점프 지시는 사전에 계획되어 비교적 거의 바뀌지 않았지만, 절차 호출에 사용되는 특별 지시는 해를 거듭하면서 크게 달라졌다.맨체스터 베이비, RCA 1802와 같은 초기의 컴퓨터와 마이크로프로세서는 서브루틴 호출 명령이 단 한 건도 없었다.서브루틴은 구현될 수 있지만, 프로그래머가 각 통화 사이트에서 일련의 지시사항인 통화 시퀀스를 사용하도록 요구하였다.

서브루틴은 1945년 콘라드 주세Z4에서 구현되었다.

1945년에 앨런 M. 튜링은 서브루틴에서 전화를 걸고 돌아오는 수단으로 "bury"와 "unbury"라는 용어를 사용했다.[12][13]

1947년 1월 존 모클리는 하버드 대학교와 미국 해군 오드넌스국의 공동 후원 하에 '대규모 디지털 계산기 심포지엄'에서 일반 노트를 발표했다.여기서 그는 직렬 및 병렬 운영 제안사항에 대해 논의한다.

...기계의 구조는 조금도 복잡할 필요가 없다.이 절차에 필수적인 모든 논리적 특성을 이용할 수 있기 때문에 기계에 알려진 장소에 서브루틴을 메모리에 배치하기 위한 코딩 지침을 쉽게 사용할 수 있도록 진화시키는 것이 가능하다.

즉, 서브루틴 A를 분할로, 서브루틴 B를 복잡한 곱으로, 서브루틴 C를 숫자 시퀀스의 표준 오차에 대한 평가로 지정할 수 있으며, 특정 문제에 필요한 서브루틴 리스트를 통해서도 그렇게 할 수 있다.이 모든 서브루틴은 기계에 저장될 것이며, 코딩에 표시된 대로 번호별로 간단히 참조하기만 하면 된다.[3]

Kay McNultyENIAC 팀에서 John Mauchly와 긴밀하게 협력했고, 2차 세계대전 동안 그녀가 프로그래밍하던 ENIAC 컴퓨터의 서브루틴에 대한 아이디어를 개발했다.[14]그녀와 다른 ENIAC 프로그래머들은 미사일 궤적을 계산하는 것을 돕기 위해 서브루틴을 사용했다.[14]

골드스틴과 폰 노이만은 1948년 8월 16일 서브루틴의 사용을 논의한 논문을 썼다.[15]

IBM 1620, Intel 4004Intel 8008PIC 마이크로컨트롤러와 같은 일부 초기의 컴퓨터와 마이크로프로세서는 전용 하드웨어 스택을 사용하여 반환 주소를 저장하는 단일 계측 서브루틴 호출을 가지고 있다. 이러한 하드웨어는 서브루틴 네스팅의 일부 수준만 지원하지만 재귀 서브루틴은 지원할 수 있다.UNIVAC I, PDP-1IBM 1130과 같은 1960년대 중반 이전의 기계들은 일반적으로 호출 규칙을 사용하여 호출된 서브루틴의 첫 번째 메모리 위치에 명령 카운터를 저장한다.이것은 임의로 깊은 수준의 서브루틴 중첩을 허용하지만 재귀 서브루틴은 지원하지 않는다.PDP-11(1970)은 스택 푸싱 서브루틴 호출 명령이 있는 최초의 컴퓨터 중 하나이다. 이 기능은 임의의 딥 서브루틴 내포와 재귀 서브루틴을 모두 지원한다.[16]

언어 지원

초기 조립자들에서는 서브루틴 지원이 제한적이었다.서브루틴은 서로 명시적으로 분리되거나 메인 프로그램에서 분리되지 않았으며, 실제로 서브루틴의 소스 코드는 다른 서브프로그램의 소스 코드와 상호 교환될 수 있었다.일부 조립자는 통화 및 반환 시퀀스를 생성하기 위해 미리 정의된 매크로를 제공할 수 있다.1960년대까지 조립자들은 대개 인라인과 별도로 조립된 서브루틴을 함께 연결할 수 있는 훨씬 더 정교한 지원을 받았다.

사용자가 작성한 서브루틴과 기능을 지원하는 최초의 프로그래밍 언어 중 하나는 FORTRAN II였다.IBM FORTRAN II 컴파일러는 1958년에 출시되었다.ALGOL 58과 다른 초기 프로그래밍 언어들도 절차적 프로그래밍을 지원했다.

서브루틴 라이브러리

이런 번거로운 접근에도 불구하고 서브루틴은 매우 유용하다는 것이 입증되었다.우선, 그들은 많은 다른 프로그램에서 동일한 코드를 사용하도록 허용했다.게다가, 초기 컴퓨터에서는 메모리가 매우 부족한 자원이었고, 서브루틴은 프로그램 크기에서 상당한 절약을 가능하게 했다.

많은 초기 컴퓨터들은 주먹으로 친 종이 테이프에서 프로그램 지시를 메모리에 저장했다.각 서브루틴은 메인 프로그램(또는 "메인라인")[17] 전후에 로드되거나 분할된 별도의 테이프 조각에 의해 제공될 수 있으며, 동일한 서브루틴 테이프가 많은 다른 프로그램에서 사용될 수 있다.펀치 카드를 주 입력에 사용하는 컴퓨터에서도 이와 유사한 접근법이 적용되었다.서브루틴 라이브러리라는 이름은 원래 문자 그대로의 의미로, 집합적인 사용을 위해 테이프나 카드데크의 색인화된 컬렉션을 보관하는 라이브러리를 의미했다.

간접 점프에 의한 리턴

자체 수정 코드의 필요성을 없애기 위해, 컴퓨터 설계자들은 결국 간접 점프 명령을 제공했는데, 피연산자는 반환 주소 자체가 아니라 반환 주소를 포함하는 변수나 프로세서 레지스터의 위치였다.

그 컴퓨터들에서, 서브루틴의 리턴 점프를 수정하는 대신에, 호출 프로그램은 리턴 주소를 변수에 저장하여 서브루틴이 완료되면, 미리 정의된 변수가 제공하는 위치로 직접 실행하는 간접 점프를 실행한다.

서브루틴으로 이동

또 다른 진보는 서브루틴 명령으로의 점프가 있었는데, 이는 반환 주소의 저장과 호출 점프를 결합하여 오버헤드를 상당히 최소화하였다.

예를 들어 IBM System/360에서, 절차 호출용으로 설계된 지점 명령 BAL 또는 BALR은 컨벤션 레지스터 14에 의해 지침에 지정된 프로세서 레지스터에 반환 주소를 저장한다.반환하기 위해 서브루틴은 그 레지스터를 통해 간접 분기 명령(BR)만 실행하면 되었다.서브루틴이 다른 목적으로 등록해야 하는 경우(다른 서브루틴을 호출하는 등), 레지스터의 내용을 개인 메모리 위치나 레지스터 스택에 저장한다.

HP 2100과 같은 시스템에서, JSB 명령어는 반환 주소가 지점의 대상이었던 메모리 위치에 저장되었다는 것을 제외하고 유사한 작업을 수행할 것이다.절차의 실행은 실제로 다음 메모리 위치에서 시작될 것이다.예를 들어 HP 2100 어셈블리 언어에서는 한 사람이 쓰곤 한다.

...JSB MYSUB (Calls subroutine MYSUB.) BB ... (MYSUB가 끝나면 여기로 돌아올 것이다.) 

메인 프로그램에서 MYSUB라는 서브루틴을 호출한다.서브루틴은 다음과 같이 코딩될 것이다.

MYSUB NOP(MYSUB 반송 주소 저장)AA … (MYSUB의 본체 시작.) …JMP MYSUB,I (통화 프로그램으로 복귀) 

JSB 지침은 NEXT 명령(이름, BB)의 주소를 피연산자로 지정된 위치(이름, MYSUB)에 배치한 후 그 다음 위치(이름, AA = MYSUB + 1)에 연결했다.그런 다음 서브루틴은 간접 점프 JMP MYSUB를 실행함으로써 메인 프로그램으로 돌아갈 수 있으며, 나는 위치 MYSUB에 저장된 위치로 연결되었다.

Fortran 및 다른 언어의 컴파일러는 사용 가능할 때 이러한 지침을 쉽게 사용할 수 있었다.이 접근방식은 여러 단계의 통화를 지원했지만 서브루틴의 반환 주소, 매개변수 및 반환 값이 고정 메모리 위치에 할당되었기 때문에 재귀 호출은 허용하지 않았다.

우연히도, 1980년대 초 Lotus 1-2-3에서 스프레드시트에서 재계산 종속성을 검색하기 위해 유사한 방법을 사용하였다.즉, 반환 주소를 저장하기 위해 각 셀에 위치를 예약했다.자연 재계산 순서에서는 순환 참조가 허용되지 않기 때문에 IBM PC와 같은 소형 컴퓨터에서는 매우 제한적인 메모리 스택 공간을 예약하지 않고 트리 보행이 가능하다.

콜 스택

서브루틴 호출의 가장 현대적인 구현은 서브루틴 호출과 리턴을 구현하기 위해 스택 데이터 구조의 특별한 사례인 콜 스택을 사용한다.각 절차 호출은 스택의 맨 위에 스택 프레임이라고 불리는 새로운 항목을 생성하며, 절차가 반환되면 스택 프레임은 스택에서 삭제되며, 그 공간은 다른 절차 호출에 사용될 수 있다.각 스택 프레임에는 해당 호출의 개인 데이터가 포함되며, 여기에는 일반적으로 절차의 매개 변수 및 내부 변수와 반환 주소가 포함된다.

통화 순서는 일련의 통상적인 명령어(RISC(Reduced Instruction Set Computing)와 매우명령어(VLIW) 아키텍처에 여전히 사용되는 접근법)에 의해 구현될 수 있지만, 1960년대 후반부터 설계한 많은 전통적인 기계들은 그러한 목적을 위한 특별 지시를 포함하고 있다.

콜 스택은 보통 메모리의 연속적인 영역으로 구현된다.스택의 맨 아래가 이 영역 내에서 가장 낮은 주소인지 아니면 가장 높은 주소인지에 따라 스택이 메모리에서 앞뒤로 성장할 수 있는 것은 임의의 설계 선택이지만, 많은 아키텍처가 후자를 선택했다.[citation needed]

일부 설계, 특히 일부 Forth 구현에서는 두 개의 별도 스택을 사용했는데, 하나는 주로 제어 정보(반환 주소 및 루프 카운터 등)를 위한 것이고 다른 하나는 데이터에 대한 것이다.전자는 통화 스택이었고, 다른 언어 구조를 통해서만 프로그래머가 간접적으로 접근할 수 있는 반면 후자는 더 직접적으로 접근할 수 있었다.

스택 기반 프로시저 호출이 처음 도입되었을 때 중요한 동기 부여는 소중한 기억을 살리는 것이었다.[citation needed]이 방법을 사용하면 컴파일러는 각 절차의 개인 데이터(파라미터, 반환 주소 및 로컬 변수)를 위해 메모리에 별도의 공간을 예약할 필요가 없다.언제라도 스택에는 현재 활성 상태인 통화의 개인 데이터(이름, 호출되었지만 아직 반환되지 않은 통화)만 들어 있다.보통 프로그램이 도서관에서 조립되는 방식 때문에, 수천 개의 서브루틴이 포함된 프로그램을 찾는 것이 드물지 않았고, 그 중 특정 순간에 소수의 프로그램만 활동한다.[citation needed]그러한 프로그램의 경우, 콜 스택 메커니즘은 상당한 양의 메모리를 절약할 수 있다.실제로 콜 스택 메커니즘은 자동 메모리 관리를 위한 가장 빠르고 간단한 방법으로 볼 수 있다.

그러나 콜 스택 방식의 또 다른 장점은 동일한 절차에 대한 중첩된 각 호출은 개인 데이터의 개별 인스턴스를 얻기 때문에 반복적인 서브루틴 호출을 허용한다는 것이다.

지연 쌓기

콜 스택 메커니즘의 한 가지 단점은 프로시저 호출의 증가된 비용과 그에 상응하는 리턴이다.[clarification needed]추가 비용에는 스택 포인터의 증분 및 감소(그리고 일부 아키텍처에서는 스택 오버플로 검사), 절대 주소 대신 프레임 상대 주소별로 로컬 변수와 매개변수에 액세스하는 것이 포함된다.비용은 실행 시간 증가, 프로세서 복잡성 증가, 또는 둘 다에서 실현될 수 있다.

이 오버헤드는 어떤 절차도 스스로 호출하지 않고 돌아오는 잎 절차나 잎 기능에서 가장 명백하고 불쾌하다.[18][19][20]이러한 오버헤드를 줄이기 위해, 많은 현대 컴파일러들은 통화 스택이 정말로 필요할 때까지 사용을 연기하려고 한다.[citation needed]예를 들어, 절차 P의 호출은 호출된 절차의 반환 주소와 매개변수를 특정 프로세서 레지스터에 저장하고, 간단한 점프로 절차 본체에 제어를 전송할 수 있다.절차 P가 다른 통화를 하지 않고 되돌아오면 호출 스택은 전혀 사용되지 않는다.P가 다른 절차 Q를 호출해야 할 경우, Q가 반환된 후 필요한 레지스터(반환 주소 등)의 내용을 저장하기 위해 호출 스택을 사용할 것이다.

C 및 C++의 예

CC++ 프로그래밍 언어에서 하위 프로그램은 함수(클래스와 관련될 경우 멤버 함수로, 그렇지 않을 경우 자유 함수[21] 분류됨)로 불린다.이 언어들은 특별한 키워드를 사용한다.void함수가 값을 반환하지 않음을 나타냄C/C++ 함수는 주소가 매개 변수로 전달되는 변수를 수정하는 등 부작용이 있을 수 있다는 점에 유의하십시오.예:

공허하게 하다 함수1() { /* 일부 코드 */ } 

함수는 값을 반환하지 않으며, 예를 들어 독립형 함수로 불려야 한다.Function1();

인트로 함수2() {   돌아오다 5; } 

이 함수는 결과(숫자 5)를 반환하며, 통화는 표현식의 일부가 될 수 있다(예:x + Function2()

마를 뜨다 함수3(인트로 번호를 붙이다) {   마를 뜨다 선발[] = {'S', 'M', 'T', 'W', 'T', 'F', 'S'};   돌아오다 선발[번호를 붙이다]; } 

이 함수는 0과 6 사이의 숫자를 해당 요일의 초기 문자, 즉 0에서 'S', 1에서 'M', ..., 6에서 'S'로 변환한다.호출한 결과는 변수에 할당될 수 있다(예:num_day = Function3(number);.

공허하게 하다 함수4(인트로* 포인터_to_var) {   (*포인터_to_var)++; } 

이 함수는 값을 반환하지 않지만 주소가 매개 변수로 전달된 변수를 수정한다. 이 함수는 다음과 같이 호출된다.Function4(&variable_to_increment);.

작은 기본 예제

()                               ' 서브루틴을 호출한다.  후보선수                              ' 서브루틴 시작     텍스트창.WriteLine("마이크로소프트 스몰 베이직의 서브루틴의 예다.")  ' 서브루틴이 하는 일 EndSub                                  ' 서브루틴을 종료한다. 

위의 예에서,Example()서브루틴을 호출한다.[22]실제 서브루틴을 정의하려면Sub키워드를 사용해야 하며 서브루틴 이름은 다음과 같다.Sub컨텐츠가 따라온 후,EndSub타이핑을 해야 한다.

Visual Basic 6 예제

Visual Basic 6 언어에서 하위 프로그램은 함수 또는 하위 프로그램(또는 클래스와 관련된 경우 방법)으로 불린다.Visual Basic 6는 어떤 것이 매개 변수로 전달되고 있는지를 정의하기 위해 type이라고 불리는 다양한 용어를 사용한다.기본적으로 지정되지 않은 변수는 변종 유형으로 등록되며 ByRef(기본값) 또는 ByVal로 전달될 수 있다.또한 기능이나 서브가 선언되면 공개, 비공개 또는 친구 지정이 주어지는데, 이 지정은 선언된 모듈이나 프로젝트 밖에서 접속할 수 있는지 여부를 결정한다.

  • 값별 [ByVal] – 주소 대신 값의 복사본을 전달하여 인수의 값을 프로시저에 전달하는 방법.결과적으로, 변수의 실제 값은 변수의 전달 절차에 의해 변경될 수 없다.
  • 참조에 의한 [ByRef] – 변수의 주소를 전달하여 변수의 값의 복사본을 전달하지 않고 절차로 인수 값을 전달하는 방법.이를 통해 절차가 실제 변수에 접근할 수 있다.따라서 변수의 실제 값은 변수의 전달 절차에 의해 변경될 수 있다.달리 명시되지 않는 한, 인수는 참조에 의해 전달된다.
  • 공개(옵션) – 모든 모듈의 다른 모든 절차에서 기능 절차에 액세스할 수 있음을 표시한다.옵션 Private가 포함된 모듈에서 사용할 경우 이 절차는 프로젝트 외부에서 사용할 수 없다.
  • 비공개(선택사항) – 기능 절차가 선언된 모듈의 다른 절차에만 접근할 수 있음을 표시한다.
  • 친구(선택사항) – 클래스 모듈에서만 사용.기능 절차는 프로젝트 전체에서 볼 수 있지만 개체 인스턴스(instance)의 컨트롤러에는 볼 수 없음을 나타낸다.
사설 함수 함수1()     '여기서 어떤 코드  함수 

함수는 값을 반환하지 않으며, 예를 들어 독립형 함수로 불려야 한다.Function1

사설 함수 함수2() 로서 정수     함수2 = 5  함수 

이 함수는 결과(숫자 5)를 반환하며, 통화는 표현식의 일부가 될 수 있다(예:x + Function2()

사설 함수 함수3(바이발 intvalue 로서 정수) 로서      어둡다 스트레이어(6) 로서      스트레이어 = 배열("M",, "T", "W", "T", "F", "S",, "S",)     함수3 = 스트레이어(intvalue)  함수 

이 함수는 0과 6 사이의 숫자를 해당 요일의 초기 문자, 즉 0에서 'M', 1에서 'T', ..., 6에서 'S'로 변환한다.호출한 결과는 변수에 할당될 수 있다(예:num_day = Function3(number).

사설 함수 함수4(ByRef intvalue 로서 정수)     intvalue = intvalue + 1  함수 

이 함수는 값을 반환하지 않지만 주소가 매개 변수로 전달된 변수를 수정한다. 이 함수는 ""로 호출된다.Function4(variable_to_increment)".

PL/I 예제

PL/I에서 호출된 절차는 문자열 길이 및 배열 한계와 같은 인수에 대한 정보를 제공하는 설명자를 통과할 수 있다.이렇게 하면 절차가 보다 일반적일 수 있고 프로그래머가 그러한 정보를 전달할 필요가 없어진다.기본적으로 PL/I는 참조로 인수를 전달한다.2차원 배열의 각 요소의 기호를 변경하는 서브루틴은 다음과 같이 보일 수 있다.

change_sign: procedure(array); arrayercript,*) float; array = -array; end change_sign;

이는 다음과 같이 다양한 배열로 호출할 수 있다.

/* 첫 번째 배열 범위는 -5 ~ +10, 3 ~ 9 */ 어레이1 선언(-5:10, 3:9)16, /* 두 번째 배열 범위는 1 ~ 16 및 1 ~ 16 */ 어레이2 플로트 선언, 호출 변경_사인(array1); 호출 변경_사인(array2);

파이톤 예

Python에서 키워드def함수를 정의하는 데 사용된다.함수의 본문을 구성하는 문장은 같은 선에서 계속하거나 다음 선에서 시작하여 들여써야 한다.[23]다음 예시 프로그램은 다음 줄에 "Hello world!"를 인쇄한 후 "Wikipedia"를 인쇄한다.

반항하다 단순_기능():     인쇄하다('헬로 월드!')     인쇄하다('위키피디아') 단순_기능() 

로컬 변수, 재귀 및 재귀성

하위 프로그램은 특정 양의 스크래치 공간, 즉 중간 결과를 보유하기 위해 하위 프로그램을 실행하는 동안 사용된 메모리를 사용하는 것이 유용할 수 있다.이 스크래치 공간에 저장된 변수를 국부 변수라고 하고, 스크래치 공간을 활성화 기록이라고 한다.활성화 레코드는 일반적으로 하위 프로그램이 종료될 때 제어 권한을 다시 전달해야 하는 위치를 알려주는 반환 주소를 가지고 있다.

하위 프로그램은 통화 사이트의 수와 특성을 가질 수 있다.재귀가 지원되는 경우, 하위 프로그램은 자체적으로 호출하여 동일한 하위 프로그램의 또 다른 중첩된 실행이 발생하는 동안 실행이 중단될 수 있다.재귀는 일부 복잡한 알고리즘을 단순화하고 복잡한 문제를 타파할 수 있는 유용한 수단이다.재귀 언어는 일반적으로 각 통화에 대한 지역 변수의 새로운 사본을 제공한다.프로그래머가 지역변수의 가치가 통화 사이에 동일하게 유지되기를 원하는 경우, 일부 언어로 정적인 것으로 선언하거나, 글로벌 가치나 공통 영역을 사용할 수 있다.다음은 피보나치 숫자를 찾기 위한 C/C++의 재귀 서브루틴의 예다.

인트로 섬유(인트로 n) {   만일 (n <= 1) {     돌아오다 n;   }   돌아오다 섬유(n - 1) + 섬유(n - 2); } 

포트란과 같은 초기 언어는 처음에 변수가 정적으로 할당되었고 반환 주소의 위치도 할당되었기 때문에 재귀성을 지원하지 않았다.PDP-8과 같은 1960년대 후반 이전의 대부분의 컴퓨터는 하드웨어 스택 레지스터를 지원하지 않았다.[citation needed]

PL/IC와 같은 ALGOL 이후의 현대 언어는 거의 변함없이 스택을 사용하며, 대개 대부분의 현대 컴퓨터 명령 집합에 의해 지원되어 하위 프로그램의 모든 실행에 대한 새로운 활성화 기록을 제공한다.이렇게 하면 중첩된 실행은 진행 중인 다른 일시 중단된 실행에도 영향을 주지 않고 지역 변수를 자유롭게 수정할 수 있다.중첩된 통화가 축적되면, 보류된 각 서브프로그램에 대해 하나의 활성화 레코드로 구성된 콜 스택 구조가 형성된다.실제로 이 스택 구조는 사실상 어디에나 있어 활성화 기록은 흔히 스택 프레임이라고 불린다.

Pascal, PL/I, Ada와 같은 일부 언어들은 또한 외측 (상위) 서브루틴의 범위 내에서만 호출 가능한 서브루틴을 지원하는 중첩된 서브루틴은 외부 (상위) 서브루틴의 범위 내에서만 호출 가능하다.내부 서브루틴은 이들을 호출한 외부 서브루틴의 로컬 변수에 접근할 수 있다.이는 디스플레이라고도 하는 활성화 기록 내에 추가 컨텍스트 정보를 저장함으로써 달성된다.

동일한 하위 프로그램의 다른 실행이 이미 진행 중인 경우에도 하위 프로그램이 제대로 실행될 수 있는 경우, 해당 하위 프로그램은 재입력되었다고 한다.재귀적 하위 프로그램은 재입력되어야 한다.리엔트런트 하위 프로그램도 여러 개의 스레드가 서로 간섭할 염려 없이 동일한 하위 프로그램을 호출할 수 있기 때문에 다중 스레드 상황에서 유용하다.IBM CICS 거래 처리 시스템에서 준재발행은 많은 스레드에 의해 공유되는 응용 프로그램에 대해 약간 덜 제한적이지만 유사한 요구사항이었다.

다중 스레드 환경에서는 일반적으로 한 개 이상의 스택이 있다.코루틴이나 게으른 평가를 완전히 지원하는 환경에서는 활성화 기록을 저장하기 위해 스택 이외의 데이터 구조를 사용할 수 있다.

과부하

강하게 타이핑된 언어의 경우, 동일한 이름을 사용하지만 다른 유형의 데이터에서 작동하거나 매개 변수 프로파일을 가진 여러 함수를 갖는 것이 바람직할 때도 있다.예를 들어 제곱근 함수는 실수, 복잡한 값 또는 행렬에서 작동하도록 정의될 수 있다.각각의 경우에 사용되는 알고리즘은 다르며, 반환 결과는 다를 수 있다.프로그래머는 동일한 이름으로 3개의 별도 함수를 작성함으로써 데이터 유형별로 다른 이름을 기억하지 않아도 되는 편리성을 갖게 된다.또한, 리얼에 대해 서브타입이 정의될 수 있는 경우, 플러스 리얼과 마이너스 리얼을 분리할 수 있는 경우, 리얼에 대해 두 가지 함수, 파라미터가 플러스일 때 리얼을 반환하는 함수, 파라미터가 마이너스일 때 복합값을 반환하는 함수 등이 작성될 수 있다.

객체 지향 프로그래밍에서, 동일한 이름의 일련의 기능이 다른 파라미터 프로파일이나 다른 유형의 파라미터를 수용할 수 있을 때, 각각의 기능은 과부하된다고 한다.

다음은 C++에서 서브루틴 과부하 예:

#include <아이오스트림>  곱절로 하다 면적(곱절로 하다 h, 곱절로 하다 w) { 돌아오다 h * w; }  곱절로 하다 면적(곱절로 하다 r) { 돌아오다 r * r * 3.14; }  인트로 본래의() {   곱절로 하다 직사각형_면적 = 면적(3, 4);   곱절로 하다 원_면적 = 면적(5);    찌꺼기::뻐드렁니가 나다 << "직사각형의 면적은 "이다. << 직사각형_면적 << 찌꺼기::끝을 맺다;   찌꺼기::뻐드렁니가 나다 << "원형의 면적은 " << 원_면적 << 찌꺼기::끝을 맺다; } 

이 코드에는 이름이 같은 두 가지 기능이 있지만 파라미터가 다르다.

또 다른 예로서 서브루틴은 방향을 수용하는 객체를 구성하여 스크린에서 이러한 점들에 대한 경로를 추적할 수 있다.생성자에게 전달될 수 있는 파라미터가 풍부하다(추적 색, 시작 x 및 y 좌표, 추적 속도).만약 프로그래머가 색 매개변수만 받아들이기를 원한다면, 그는 색상만 받아들이는 다른 생성자를 호출할 수 있고, 이것은 다른 모든 매개변수에 대해 일련의 기본값으로 전달되는 모든 매개변수로 생성자를 호출할 수 있다(X와 Y는 일반적으로 화면 중앙에 있거나 원점에 배치된다).속도가 코더가 선택한 다른 값으로 설정될 수 있다.

PL/I는 다음과 같은 기능을 가지고 있다.GENERIC속성: 다른 유형의 인수로 호출된 항목 참조 집합의 일반 이름을 정의한다.예:

Gen_name GEN_name GENERIC(이름 WIFT(고정 이진), 불꽃 WIFLAT, 경로 이름 그렇지 않으면 );

각 항목에 대해 다중 인수 정의를 지정할 수 있다.gen_name에 대한 호출은 인수가 고정 BINION일 때 "name"으로, FLOT일 때 "flame" 등으로 호출된다.인수가 일치하는 경우 "pathname" 선택사항은 호출되지 않는다.

폐쇄

폐쇄(closure)는 폐쇄가 생성된 환경에서 캡처된 일부 변수의 값과 함께 하위 프로그램이다.폐쇄성은 존 매카시가 소개한 리스프 프로그래밍 언어의 주목할 만한 특징이었다.시행에 따라 폐쇄는 부작용의 메카니즘으로 작용할 수 있다.

관습

서브루틴 코딩에 대한 광범위한 규약이 개발되었다.그들의 이름과 관련하여, 많은 개발자들은 서브루틴의 이름이 그것이 특정한 일을 할 때 동사가 되어야 하고, 그것이 약간의 조사를 할 때 형용사가 되어야 하며, 변수를 대체할 때 명사가 되어야 한다는 접근법을 채택했다.

일부 프로그래머들은 서브루틴은 한 가지 작업만 수행해야 하며 서브루틴이 한 가지 이상의 작업을 수행하는 경우에는 더 많은 서브루틴으로 분할해야 한다고 제안한다.그들은 서브루틴이 코드 유지보수의 핵심 구성요소이며, 프로그램에서의 그들의 역할은 구별되어야 한다고 주장한다.

모듈형 프로그래밍(변조 코드)의 지지자들은 각 서브루틴이 다른 코드 조각에 대한 최소한의 의존성을 가져야 한다고 주장한다.예를 들어, 서브루틴과 이러한 글로벌 변수 사이에 긴밀한 결합을 추가하기 때문에 일반적으로 글로벌 변수를 사용하는 것은 이 관점에 대한 옹호자들에 의해 현명하지 못한 것으로 간주된다.그러한 결합이 필요하지 않은 경우, 그들의 조언은 대신 통과된 매개변수를 받아들이도록 서브루틴을 리팩터링하는 것이다.그러나 서브루틴에 전달되는 파라미터의 수를 증가시키면 코드 가독성에 영향을 미칠 수 있다.

반송 코드

서브루틴은 주 또는 정상 효과 에도 실행 중에 발생했을 수 있는 예외적인 조건에 대해 호출 프로그램에 알릴 필요가 있을 수 있다.일부 언어와 프로그래밍 표준에서 이것은 종종 반환 코드, 즉 서브루틴이 어떤 표준 위치에서 배치한 정수 값을 통해 이루어지며, 이것은 정상적이고 예외적인 조건을 인코딩한다.

서브루틴에서 리턴 코드가 예상되었던 IBM System/360에서 리턴 값은 4의 배수로 설계되는 경우가 많았는데, 이는 추가 조건부 테스트를 피하기 위해 호출 명령 직후에 종종 위치한 분기 테이블에 직접 분기 테이블 인덱스로 사용할 수 있도록 하여 효율성을 더욱 향상시켰다.시스템/360 어셈블리 언어로 다음과 같은 내용을 쓰기도 한다.

BAL 14, SUBRTN01은 서브루틴으로 이동하여 R14 B TABLE(15)에 반환 주소를 저장하며 레지스트리 15의 반환 값을 사용하여 분기 테이블을 인덱싱하고 * 적절한 분기 인스터로 분기한다.표 B OK 반환 코드 =00 GOOD } B B 불량 반환 코드 =04 잘못된 입력 } 분기 표 B 오류 반환 코드 =08 예기치 않은 조건 }

서브루틴 호출 최적화

인수 전달, 하위 프로그램 분기 및 호출자에게 다시 분기하는 것을 포함하여 서브루틴을 호출할 때 상당한 런타임 오버헤드가 있다.오버헤드에는 종종 특정 프로세서 레지스터의 저장 및 복원, 콜 프레임 스토리지 할당 및 회수 등이 포함된다.[example needed]일부 언어에서, 각 서브루틴 호출은 서브루틴의 반환 코드의 자동 시험이나 서브루틴이 제기할 수 있는 예외의 처리를 의미하기도 한다.객체 지향 언어에서 중요한 오버헤드의 원천은 메서드 호출에 대해 집중적으로 사용되는 동적 전송이다.

절차의 부작용이 있을 경우 적용할 수 없는 절차 호출의 일부 분명한 최적화가 있다.예를 들어, 표현에서(f(x)-1)/(f(x)+1), 함수f두 통화는 서로 다른 결과를 반환할 수 있기 때문에 두 번 호출해야 한다.더구나 의 가치.x두 번째 통화 전에 다시 가져와야 한다. 첫 번째 통화로 인해 변경되었을 수 있기 때문이다.서브프로그램이 부작용을 일으킬 수 있는지 여부를 결정하는 것은 매우 어렵다(사실, 라이스의 정리에 의해 결정되지 않았다).따라서 이러한 최적화는 순전히 기능적인 프로그래밍 언어에서는 안전하지만, 일반적인 명령적 프로그래밍의 컴파일러들은 대개 최악의 상황을 가정해야 한다.

인라이닝

이러한 오버헤드를 제거하기 위해 사용되는 방법은 각 통화 사이트에서 서브프로그램 본체의 인라인 확장 또는 인라이닝이다(서브루틴과 후면에 분기하는 방법).이것은 통화 오버헤드를 피할 뿐만 아니라, 컴파일러가 해당 통화의 맥락과 주장을 고려함으로써 절차의 본체를 보다 효과적으로 최적화할 수 있게 한다.삽입된 본체는 컴파일러에 의해 최적화될 수 있다.그러나 프로그램에 서브루틴에 대한 호출이 하나만 포함되지 않는 한, 인라이닝은 일반적으로 코드 크기를 증가시킨다.

참고 항목

참조

  1. ^ U.S. Election Assistance Commission (2007). "Definitions of Words with Special Meanings". Voluntary Voting System Guidelines. Archived from the original on 2012-12-08. Retrieved 2013-01-14.
  2. ^ Subrata Dasgupta (7 January 2014). It Began with Babbage: The Genesis of Computer Science. Oxford University Press. pp. 155–. ISBN 978-0-19-930943-6.
  3. ^ a b J.W. Mauchley, 1982년 스프링거, Brian Randell (Ed.)의 "EDVAC형 기계의 문제 준비" (1947)
  4. ^ Wheeler, D. J. (1952). "The use of sub-routines in programmes" (PDF). Proceedings of the 1952 ACM national meeting (Pittsburgh) on - ACM '52. p. 235. doi:10.1145/609784.609816.
  5. ^ Wilkes, M. V.; Wheeler, D. J.; Gill, S. (1951). Preparation of Programs for an Electronic Digital Computer. Addison-Wesley.
  6. ^ Dainith, John (2004). ""open subroutine." A Dictionary of Computing". Encyclopedia.com. Retrieved January 14, 2013.
  7. ^ Turing, Alan M. (1945), Report by Dr. A.M. Turing on proposals for the development of an Automatic Computing Engine (ACE): Submitted to the Executive Committee of the NPL in February 1946 로 다시 인쇄된.
  8. ^ Donald E. Knuth (1997). The Art of Computer Programming, Volume I: Fundamental Algorithms. Addison-Wesley. ISBN 0-201-89683-4.
  9. ^ O.-J. Dahl; E. W. Dijkstra; C. A. R. Hoare (1972). Structured Programming. Academic Press. ISBN 0-12-200550-3.
  10. ^ Wilson, Leslie B. (2001). Comparative Programming Languages, Third Edition. Addison-Wesley. p. 140. ISBN 0-201-71012-9.
  11. ^ Stroustrup, Bjarne (2013). The C++ Programming Language, Fourth Edition. Addison-Wesley. p. 307. ISBN 978-0-321-56384-2.
  12. ^ Turing, Alan Mathison (1946-03-19) [1945], Proposals for Development in the Mathematics Division of an Automatic Computing Engine (ACE) (NB. 1946-03-19일 국립물리실험실 집행위원회(영국)에 상정)
  13. ^ Carpenter, Brian Edward; Doran, Robert William (1977-01-01) [October 1975]. "The other Turing machine". The Computer Journal. 20 (3): 269–279. doi:10.1093/comjnl/20.3.269. (11페이지)
  14. ^ a b Isaacson, Walter (18 September 2014). "Walter Isaacson on the Women of ENIAC". Fortune. Archived from the original on 12 December 2018. Retrieved 2018-12-14.
  15. ^ 전자컴퓨팅 기기의 문제 계획 및 코드화, 2부, 3부 https://library.ias.edu/files/pdfs/ecp/planningcodingof0103inst.pdf (관련 페이지인 pdf 163 페이지 참조)
  16. ^ 가이 루이스 스틸 주니어AI 메모 443. '비용 비싼 프로시저 콜' 신화 삭제 또는 절차 구현이 유해하다고 판단됨'섹션 "C.왜 프로시저 호출이 나쁜 평판을 가지는가."
  17. ^ Frank, Thomas S. (1983). Introduction to the PDP-11 and Its Assembly Language. Prentice-Hall software series. Prentice-Hall. p. 195. ISBN 9780134917047. Retrieved 2016-07-06. We could supply our assembling clerk with copies of the source code for all of our useful subroutines and then when presenting him with a mainline program for assembly, tell him which subroutines will be called in the mainline [...]
  18. ^ "ARM Information Center". Infocenter.arm.com. Retrieved 2013-09-29.
  19. ^ "x64 stack usage". Microsoft Docs. Microsoft. Retrieved 5 August 2019.
  20. ^ "Function Types". Msdn.microsoft.com. Retrieved 2013-09-29.
  21. ^ "what is meant by a free function".
  22. ^ "Microsoft Small Basic". www.smallbasic.com.
  23. ^ "4. More Control Flow Tools — Python 3.9.7 documentation".