Call By Value, Call By Reference 차이


프로그래밍을 공부하다 보면 “내 코드는 분명 값을 바꿨는데, 왜 함수 밖으로 나오면 원래대로 돌아가 있지?”라는 의문이 생길 때가 있습니다. 이는 함수의 인자 전달 방식인 Call By Value와 Call By Reference 차이를 정확히 이해하지 못해서 발생하는 현상입니다. 이 두 개념은 단순히 값을 넘기는 방법을 넘어, 프로그램이 메모리를 어떻게 관리하는지와 직결됩니다.


목차

  1. Call By Value(값에 의한 호출)의 정의와 특징
  2. Call By Reference(참조에 의한 호출)의 메커니즘
  3. 두 방식의 결정적 차이와 메모리 변화
  4. 자바(Java)는 어떤 방식을 사용할까? 흔한 오해 풀기
  5. 언어별·상황별 올바른 활용 전략
  6. 면접 대비 핵심 요약 및 학습 도구

1. Call By Value(값에 의한 호출)의 정의와 특징

Call By Value는 함수를 호출할 때 전달되는 인자의 ‘값’을 복사하여 함수의 매개변수로 전달하는 방식입니다. 쉽게 말해, 원본 데이터의 ‘복사본’을 하나 더 만들어서 함수에게 넘겨주는 것과 같습니다.

1-1. 독립적인 메모리 공간

함수 내부에서 사용하는 매개변수는 호출한 곳의 변수와는 완전히 다른 별도의 메모리 공간(Stack 영역)에 생성됩니다. 따라서 함수 내부에서 매개변수의 값을 아무리 수정하더라도, 함수 외부의 원본 변수에는 아무런 영향을 주지 않습니다.

1-2. 안정성과 메모리 비용

이 방식의 가장 큰 장점은 ‘안전성’입니다. 외부 데이터가 함수 내부의 실수로 인해 오염될 가능성이 없기 때문입니다. 하지만 전달하려는 데이터의 크기가 매우 크다면(예: 거대한 구조체), 이를 매번 복사하는 과정에서 메모리 사용량이 늘어나고 성능이 저하될 수 있다는 단점이 있습니다.

2. Call By Reference(참조에 의한 호출)의 메커니즘

반면 Call By Reference는 인자로 전달되는 변수의 ‘참조(메모리 주소)’를 직접 전달하는 방식입니다. 복사본을 만드는 대신, 데이터가 저장된 ‘실제 위치’를 가리키는 정보를 넘겨줍니다.

2-1. 원본 데이터와의 동기화

함수 내에서 매개변수를 통해 값을 수정하면, 그 즉시 원본 데이터가 저장된 메모리 주소의 값이 변경됩니다. 즉, 함수 내부와 외부가 동일한 데이터를 공유하게 되는 셈입니다. C++에서 포인터(*)나 참조자(&)를 사용하여 구현하는 방식이 대표적인 예입니다.

2-2. 효율적인 데이터 처리

참조 방식은 데이터의 크기에 상관없이 ‘주소값’ 하나만 전달하면 되기 때문에 복사 비용이 거의 들지 않습니다. 대용량 데이터를 다룰 때 매우 효율적이지만, 함수 내부에서 의도치 않게 원본 데이터를 수정해버릴 위험이 항상 존재하므로 주의가 필요합니다.

3. 두 방식의 결정적 차이와 메모리 변화

Call By Value와 Call By Reference 차이를 가장 극명하게 보여주는 사례는 두 변수의 값을 바꾸는 swap 함수입니다.

3-1. 결과의 차이

  • Call By Value: 함수 안에서는 값이 바뀐 것처럼 보이지만, 함수가 종료되고 돌아오면 원본은 그대로입니다. 복사본만 만졌기 때문입니다.
  • Call By Reference: 함수가 종료된 후에도 원본 변수의 값이 서로 바뀌어 있습니다. 실제 주소에 접근해 값을 교체했기 때문입니다.

3-2. 사이드 이펙트(Side Effect)

프로그래밍에서 사이드 이펙트는 함수가 외부 상태를 변경하는 것을 의미합니다. Call By Reference는 사이드 이펙트가 강력하게 발생하며, 이는 장점이자 단점이 됩니다. 코드의 흐름을 추적하기 어렵게 만들 수 있지만, 여러 값을 한 번에 반환해야 하거나 상태를 직접 수정해야 할 때는 필수적입니다.

4. 자바(Java)는 어떤 방식을 사용할까? 흔한 오해 풀기

많은 개발자가 자바에서 객체를 넘길 때 값이 변하는 것을 보고 “자바는 Call By Reference다”라고 오해하곤 합니다. 하지만 결론부터 말씀드리면, 자바는 항상 Call By Value 방식으로 동작합니다.

4-1. 참조값의 ‘복사’

자바에서 객체를 인자로 넘길 때 전달되는 것은 객체 자체가 아니라, 객체를 가리키는 ‘참조값(주소값)’의 복사본입니다. 주소값이 복사되어 넘어가기 때문에 함수 내부에서 객체의 멤버 변수를 수정하면 원본 객체에 반영됩니다. 하지만 함수 내부에서 매개변수에 새로운 객체를 할당하더라도 원본 변수가 가리키는 객체는 변하지 않습니다.

4-2. 원시 타입 vs 참조 타입

  • 원시 타입(int, double 등): 실제 값이 복사되어 전달됩니다.
  • 참조 타입(Object 등): 주소값이 복사되어 전달됩니다. 결국 둘 다 ‘무언가(값 혹은 주소)’를 복사해서 넘기기 때문에 자바는 엄밀히 말해 Call By Value에 해당합니다.

5. 언어별·상황별 올바른 활용 전략

사용하는 언어의 특성에 따라 어떤 방식을 취할지 결정하는 안목이 필요합니다.

5-1. 언어별 특징

  • C/C++: 사용자가 직접 Call By Value와 Reference를 선택할 수 있습니다. 성능 최적화가 중요한 시스템 프로그래밍에서 매우 중요합니다.
  • Python/Java/JavaScript: 기본적으로 주소값 복사 방식을 사용하며, 개발자가 메모리 주소를 직접 제어하기보다는 객체의 가변성(Mutability)에 집중해야 합니다.

5-2. 최적의 선택 기준

  1. 값의 보호가 우선인 경우: Call By Value를 사용하거나, 객체를 전달할 때 ‘불변 객체(Immutable Object)’로 만들어 전달하세요.
  2. 성능이 최우선인 대용량 데이터: 주소값이나 참조를 전달하여 복사 오버헤드를 최소화하세요.
  3. 가독성 확보: 함수가 외부 변수를 직접 바꾸는 것은 예측 불가능한 버그를 만들 수 있으므로, 가급적이면 변경된 값을 리턴(Return)받는 구조를 권장합니다.

6. 전문가·기관 관점 및 추천 도구

클린 코드(Clean Code)의 저자 로버트 C. 마틴은 함수가 인자를 통해 상태를 변경하는 것을 최소화하라고 조언합니다. 이는 디버깅의 난이도를 낮추기 위함입니다.

6-1. 비주얼라이저 활용

메모리 변화를 직접 눈으로 보고 싶다면 Python Tutor나 Online C++ Debugger를 활용해 보세요. 코드가 실행될 때 스택과 힙 메모리에서 화살표가 어떻게 움직이는지 실시간으로 시각화해 줍니다.

6-2. 정적 분석 도구 활용

C++ 같은 언어에서는 Clang-Tidy를 통해 불필요한 값 복사가 일어나고 있는 부분을 찾아내고, 참조 전달로 변경하라는 가이드를 받을 수 있습니다. 이를 통해 시스템의 성능을 한층 더 끌어올릴 수 있습니다.


결론: 요약 및 핵심 전략

  • Call By Value는 값을 복사해서 전달하며 원본이 안전하지만, 메모리 복사 비용이 발생합니다.
  • Call By Reference는 주소를 전달하여 원본을 직접 수정하며 효율적이지만, 의도치 않은 변경 위험이 있습니다.
  • 자바는 주소값을 복사해서 넘기는 Call By Value 방식을 취하므로 객체 다룰 때 주의가 필요합니다.

이제 Call By Value와 Call By Reference 차이가 명확해지셨나요? 어떤 방식을 사용하느냐에 따라 프로그램의 안정성과 성능이 결정됩니다. 지금 작성 중인 함수의 파라미터가 원본에 어떤 영향을 줄지 다시 한번 생각해보는 습관을 가져보세요.

답글 남기기

이메일 주소는 공개되지 않습니다. 필수 필드는 *로 표시됩니다