Parameter(매개변수) vs. Argument(인자) 차이점

Parameter Passing Techniques

아래의 질문에 잘 대답할 수 있는가?

❓ 자바는 call by reference 인가? call by value인가?

❓ python의 parameter passing 방법은 무엇인가?

은근히 헷갈리는 parameter passing 방법에 대해서 다뤄본다.

Parameter vs Argument

함수나 메소드를 선언한 후 사용할 때 값을 넘겨주게 된다.
이때 매개변수(Parameter), 인자(Arguement) 라는 용어가 등장하게 된다.
대충 함수에 넘겨주고 함수가 다루게 되는 값을 담는 곳! 이라는 말로 넘어가기엔 찜찜하다.

ANSI 문서에서 두 용어의 정의를 찾아보면 아래와 같다.

Parameter (매개변수)
object declared as part of a function declaration or definition that acquires a value on entry to the function, or an identifier from the comma-separated list bounded by the parentheses immediately following the macro name in a function-like macro definition
= formal paremeter
= formal argument (deprecated)


함수 선언 및 정의의 한 부분으로 선언된 객체 또는 매크로 함수의 정의 부분에서 매크로 이름 바로 뒤에 괄호로 묶여 쉼표로 구분된 목록의 식별자

함수 선언부에서 함수명 뒤에 괄호(parentheses)로 묶여 표현된 것이 parameter다. 여기서 주의할 점은, formal parameter, formal argument와 동의어라는 것이다.

정의를 통해 2가지를 유추할 수 있다.

  • formal이라는 개념이 함수 선언부와 유관하다
  • parameterargument라는 용어를 혼용해서 사용한다

다만 formal argument라는 용어가 deprecated 된다고 하니(혼용되지만 지양했으면 하는 표현), 가장 명확한 표현은 formal parameter 일 것이라고 결론을 내려본다

Argument (인자)
expression in the comma-separated list bounded by the parentheses in a function call expression, or a sequence of preprocessing tokens in the comma-separated list bounded by the parentheses in a function-like macro invocation
= actual argument
= actual parameter (deprecated)


함수 호출 표현식에서 괄호로 묶여 쉼표로 구분된 목록의 표현식 또는 매크로 함수를 사용할 때 괄호로 묶여 쉼표로 구분된 목록의 전처리할 토큰 시퀀스

함수 호출부에서 괄호로 묶인 것이 argument 이다. 여기서도 동의어가 소개되는데 acutal argumentactual parameter 가 같은 뜻이며, actual parameter는 지양한다고 되어 있다.

유추해 볼 수 있는 점은 아래와 같다.

  • actual 이라는 개념이 함수 호출부와 유관하다

여러 용어가 혼용되지만, 함수 호출부 식별자를 지칭하는 가장 명확한 표현은 actual argument 일 것이다.

이를 간단한 코드로 정리해보면 아래와 같다.

함수 선언부에 붙은 것이 formal parameter이고 함수 호출부에 붙은 것이 actual argument이다. 각각 한국어로 형식매개변수와 실인수라고도 한다. 간단하게 함수 선언 시 parameter(매개변수), 함수 호출 시 argument(인자)라고 기억해도 좋다.

Memory Management: Stack Segment

argumentparameter를 구분하는 것일까?
메모리에서 스택이 어떻게 관리되는 지 보면 살펴보면 이해할 수 있다.

Activation Record라고 하는 호출 스택은 함수를 호출할 때 이전까지 정보를 유지하고 관리하는 자료구조체이다.

일명 Stack Frame이라고도 한다. context switching을 하기 전에 함수 상태를 기록하고 복원하기 위한 것으로써, 해당 함수의 리턴 값, 값 파라미터, 지역 변수, 복귀 주소 등의 정보가 기록된다.

프로그램이 실행되면 CPU가 Program Pointer를 이용해 코드를 훑게 되고, 함수 호출부를 만나면 호출된 함수의 정보를 유지하기 위해 스택 공간에 메모리를 할당한다.

할당되는 메모리는 복귀 주소가 들어갈 공간을 포함하여, 함수의 매개 변수와 함수 블록 내에서 선언된 지역 변수의 전체 크기에 의해 결정된다.

그림으로 살펴보면 아래와 같다.

  1. 현재 프로그램인 void f()은 복귀 주소를 포함하여 함수 블록 내에서 선언된 지역 변수의 전체 크기에 의해 공간을 할당 받는다.
  2. sum() 함수를 만나면, activation record가 분화하며 새 영역을 할당한다. sum() 함수는 복귀 주소를 포함하여 매개 변수의 크기에 의해 공간을 할당 받는다.
  3. 메모리 할당 이후, void f() 함수의 인자(x, y)가 앞서 할당됐던 sum() 함수의 매개 변수(a, b)에 대입된다.

즉 매개변수는 인자를 복사하여 사용하는 것으로, 둘의 위치는 다르지만 성질은 같다. 이러한 특성 때문에 매개변수와 인자를 헷갈리는 것 같다.

Parameter - Arugment 호출 규약

메모리 할당 이후, 함수의 인자가 앞서 할당됐던 매개 변수에 대입된다고 했다. 이때 매개 변수에 인자가 어떤 순서로 대입되는지는 언어별 호출 규약마다 상이하다.

복수의 파라미터를 가진 함수에 복수의 인자를 넘겨줄 때, 각각을 어떻게 매핑할 것인가?

여기에는 크게 2가지 방법이 있다.

Positional Parameters

가장 많은 언어에서 사용하는 방식으로, 선언한 순서에 따라 n번째 인자가 n번째 파라미터에 매칭된다.

def sub(a, b):
	print(a - b)
    
>>> sub(3, 1)
2
>>> sub(1, 3)
-2

인자 순서가 바뀌면 결과가 바뀌거나 에러가 발생할 수 있다.

Named Parameters (Keyword Parameters)

어떤 파라미터에 들어갈 인자인지 명시해주는 방식이다. 이때 순서는 바뀌어도 무방하다.

python, ada, fotran, dylan 같은 언어가 이 방식을 지원한다.

def repeat(msg, num):
	print(msg * num)
    
>>> repeat(msg='Python', num=3)
'PythonPythonPython'

위의 두 방식을 배타적으로 사용하는 것만은 아니다. 예시로 든 python은 두 방식을 모두 사용할 수 있다. 심지어 형 변환도 알아서 해준다.

def add(*params):
	sum = 0;
    for num in params:
    	sum += num
    print(sum)
>>> add(1, 3, 5, 7, 9)
25

python, c, c++, javascript, java, perl에서는 unbounded한 길이의 인자를 허용한다. unlimited parameter lists라고 하는데, 이때 성질은 모두 동일해야 한다.

그러나 알아서 형 변환을 해주는 python은 여러 성질을 섞어도 에러가 나지 않는다.

def repeat(msg, num):
	return msg * num
    
>>> repeat('Python', 3)
'PythonPythonPython'
>>> repeat(3, 'Python')
'PythonPythonPython'
>>> repeat(3, 3)
9

stringint 값이 섞여서 들어오면 모두 string으로 처리하고 int 값만 들어오면 int로 처리해준다.

Optional Paremeters

positionalnamed 방식을 섞어서 사용하면 이런 것도 가능하다. actual argument가 들어오지 않더라도 미리 정의된 parameter 값을 사용하는 것이다.

인자값이 새로 들어오면 반영하고, 들어오지 않는 인자값은 기본 parameter 값을 사용한다.

def f(a, b=2, c=3):
	print(a, b, c)
 
>>> f(1)
(1, 2, 3)
>>> f(1, b=2)
(1, 2, 3)
>>> f(1, b=3, c=2)
(1, 3, 2)
>>> f(1, c=3)
(1, 2, 3)
>>> f(b=2)
TypeError: f() takes at least 1 argument (1 given) 

모든 positional arguments는 명명된 인자보다 먼저 위치해야 하고, 미리 값이 선언되지 않은 파라미터 a는 인자로 아무런 값이 주어지지 않으면 에러가 발생한다.

이런 paratmer passing 방식을 call by name이라고 한다. 인수 전달 시 식별자를 그대로 전달하는 방식이다. 중간 변환 과정이 없고 호출 속도가 빠르다. C와 C++ 에서 #define 인수 전달법이 call by name이다.

그러나 이 방식은 인수에 수식을 적용했을 때, 수식 전체가 전달되기 때문에 함수의 고유성이 깨진다는 것이다.

다음편 예고

위에서 언급한 문제 때문에 인자를 그대로 전달하는 것이 아니라 인자를 연산한 결과만을 전달하기도 한다.

예를 들면 python은 immutable arguments을 넘겨줄 때는 call by value를 사용하고 mutable arguments를 넘겨줄 때는 call by object를 사용하기도 한다.

parameter passing 종류는 생각보다 많다.
각각 call by [something] 으로 명명한다.

새로운 용어가 많이 등장할 것 같으니 다음 글에서 이어서 작성하도록 하겠다.

참고 자료

[Adam Brooks Webber] Modern Programming Languages 2nd Edition

좋은 웹페이지 즐겨찾기