C

C / 함수의 인자 평가 순서

발아현미 2025. 4. 9. 17:01

- 함수의 인자 평가는 순서가 없다.

어떤 함수의 인자가 연속적으로 나열되었을 땐, 컴파일러가 값을 처리하는 순서가 존재하지 않는다.

#include <stdio.h>

int main() {
	int a[3] = { 1, 2, 3 };
	int* ptr = a;

	printf("%d, %d, %d\n", *ptr++, *ptr++, *ptr++);
	
	return 0;
}

 위 코드는 printf에서 *ptr++을 사용하여 a배열의 원소를 순차적으로 나열하는 로직을 지녔다. 언뜻 보기에는 정상적으로 1, 2, 3이 출력될 것으로 보이지만... 결과는 아래와 같다.

 

 이유는, C 언어에서 함수 인자의 평가 순서는 정해져 있지 않기 때문이다. printf에서 한 줄로 나열된 3개의 *ptr++ 중 어느 것부터 실행하여 값을 반환할 지 정해지지 않았다는 말이다. 

 n 번째 %d는 n 번째 *ptr++과 잘 연결되었지만, 첫 번째 / 두 번째 / 세 번째 *ptr++ 중 어느 것부터 수행할 지는 완전한 컴파일러의 마음이라는 것이다. 

- 왜 뒤의 연산부터 시행하는 것일까 (MSVC 기준)

 보통 글자도 왼쪽에서 오른쪽으로 읽으니까, 연산도 왼쪽부터 수행하면 좋지 않을까 생각이 든다. 작성자는 마이크로소프트 비주얼 스튜디오를 사용 중에 있지만, 다른 컴파일러를 사용하는 경우 의도한 대로 나올 수 있다. 컴파일러마다 값이 다르게 도출될 수 있다는 점 유의하자!

 

 왜 뒤의 연산부터 시행하는 것일까에 대한 궁금증을 해결하기 위해서 컴파일러의 입장에서, 차근차근 생각해 보아야 한다.

  1. printf, scanf 등과 같은 '가변 인자 함수'는 사전에 인자를 몇 개 가져올 지 모른다. 그렇기 때문에 printf에게 인자를 넘기기 전에 필요한 인자들을 stack에 push해 놓고, 피호출자에게 전달한다.
  2. MSVC는 여러 개의 인자가 동시에 들어왔을 때, 오른쪽부터 왼쪽 순서로, 역순으로 연산을 진행한다. C에서 공식적으로 인자 평가 순서가 없다고 했기 때문에, 컴파일러의 종류마다 순서가 상이할 수 있다.
  3. 피호출자(printf)는 *ptr++ 3개를 순서대로 실행하고 이 값을 stack영역에 저장한다. 이 때 stack영역은 high address -> low address 순으로, 마치 종유석이 천장에서부터 자라는 것처럼 할당된다. MSVC의 term에 따라 *ptr++의 연산은 아래 순서와 같이 push되어 stack영역에 저장된다.
  4. stack된 값 중 가장 나중에 저장된 값이 첫 번째 %d에 대응되어 3, 2, 1 순서로 출력된다. (push / pop은 LIFO; Last In First Out. 늦게 push될 수록 먼저 pop된다.)

- 로직 오류 눈치를 못 챌 수도 있다

 printf와 같은 자주 사용하는 함수의 경우 인자평가 순서 관련 오류 판단이 쉽게 될 수 있겠지만, 여러 함수를 선언하여 쓰는 경우 눈치채지 못할 수 있다. 가령 여러 함수를 사용하는데, 여러 함수 안에 동일한 전역변수가 사용되어 각 함수가 전역변수의 값을 여러 번 초기화하는 코드가 있다고 생각하자. 이 때 인자 평가 순서에 따라 값이 달라질 수 있다. 

 또한, 함수와 연산자 우선순위가 다른 연산과 같이 사용되면 값이 바뀔 수 있다. 

 

- 해결법

 뾰족한 방법은 없다. 인자 평가 순서를 사용자가 직접 지시할 수 없기 때문이다. 번거롭겠지만 원하는 순서대로 나오도록 차근차근 함수를 호출하는 수밖에 없다. 

printf("%d", *p++);
printf("%d", *p++);
printf("%d", *p++);

 또는, 인자들을 미리 변수에 저장해 두는 방법도 있다.

#include <stdio.h>

int main() {
	int a[3] = { 1, 2, 3 };
	int* ptr = a;

	int x = *ptr++;
	int y = *ptr++;
	int z = *ptr++;

	printf("%d, %d, %d\n", x, y, z);
	
	return 0;
}

 

 만약 컴파일을 할 때 원하는 값이 아닌 생뚱맞은 값이 도출된다면, 인자평가 순서 때문이 아닐 지 한번 의심해 볼 만 하다. 시간을 많이 줄일 수 있을 것이다!

 

- 정리

1. 함수의 인자평가는 순서가 없다.

함수 안에 여러 개의 인자가 사용되었을 때, stack에 push하고 pop하는 순서를 잘 고려해야 한다.

 

2. 원하는 순서대로 함수를 여러 번 호출하자

번거로울 수 있지만 여러 번 호출하는 건이 안정적일 수 있다. 컴파일러마다 인자 순서가 다르기 때문이다.