매개변수 전달 방식 (Call by Value)
요약
함수 호출 시 매개변수 전달 방식인 Call by Value와 Call by Reference를 알아봅니다. 정보처리기사 실기에 자주 출제되는 swap 함수 예제와 배열 참조 전달 문제를 풀기 위한 핵심 개념을 정리합니다.
함수 호출 방식 핵심 정리
| 개념 | 설명 | 원본 변경 |
|---|---|---|
| Call by Value | 값을 복사해서 전달 | X (불가) |
| Call by Reference | 주소를 전달 | O (가능) |
| 배열 전달 | 배열 이름 자체가 주소이므로 & 없이 자동 전달 | O (가능) |
Call by Value (값에 의한 호출) 쌩기초
값에 의한 호출은 함수를 호출할 때 변수의 값을 복사해서 전달하는 방식입니다.
함수 내부에서 매개변수1를 변경해도 원본 변수에는 영향이 없습니다.

동작 원리
| 단계 | main의 a | change의 x |
|---|---|---|
| 호출 전 | 10 | - |
| 함수 진입 | 10 | 10 (복사) |
| x = 100 실행 | 10 | 100 |
| 함수 종료 후 | 10 | (소멸2) |
return을 사용한 값 변경
Call by Value로는 원본을 직접 바꿀 수 없지만, 함수가 계산한 결과를 return으로 돌려보내고, 호출한 쪽에서 그 값을 받아 변수에 다시 넣으면 값을 바꿀 수 있습니다.
| 방식 | 원본 변경 방법 |
|---|---|
| Call by Value + return | a = change(a); 반환값을 대입 |
| Call by Reference | 포인터로 직접 변경 |
swap 함수 예제 (Call by Value) 쌩기초
swap 함수는 두 변수의 값을 교환하는 함수입니다. Call by Value로 구현하면 교환이 되지 않습니다.
왜 교환이 안 될까?
| 시점 | main의 a | main의 b | swap의 a | swap의 b |
|---|---|---|---|---|
| swap 호출 전 | 11 | 19 | - | - |
| swap 진입 | 11 | 19 | 11 | 19 |
| swap 내부 교환 후 | 11 | 19 | 19 | 11 |
| swap 종료 후 | 11 | 19 | (소멸) | (소멸) |
Call by Reference (참조에 의한 호출) 기초
참조에 의한 호출은 변수의 주소를 전달하는 방식입니다. 포인터를 사용하여 원본 변수에 직접 접근할 수 있습니다.
포인터란 다른 변수의 메모리 주소를 저장하는 변수입니다. 아래 코드에서 핵심 기호 3가지를 먼저 알아둡시다.
int *x— 선언:x는 int형 변수의 주소를 저장하는 포인터*x— 사용:x가 가리키는 곳의 값 (역참조)&a—a가 저장된 메모리 주소
동작 원리
예를 들어 변수 a가 메모리의 100번지에 저장되어 있다고 가정하면:
| 단계 | main의 a (100번지) | change의 x |
|---|---|---|
| 호출 전 | 10 | - |
| 함수 진입 | 10 | 100번지 (a의 주소) |
*x = 100 실행 | 100 | 100번지 |
| 함수 종료 후 | 100 | (소멸) |
swap 함수 (Call by Reference) 기초
포인터를 사용하면 진짜 교환이 가능합니다.
동작 과정
main의 a가 100번지, b가 200번지에 있다고 가정하면:
| 단계 | main의 a (100번지) | main의 b (200번지) | *a (100번지 값) | *b (200번지 값) |
|---|---|---|---|---|
| swap 호출 전 | 11 | 19 | - | - |
| swap 진입 | 11 | 19 | 11 | 19 |
t = *a (t=11) | 11 | 19 | 11 | 19 |
*a = *b | 19 | 19 | 19 | 19 |
*b = t | 19 | 11 | 19 | 11 |
| swap 종료 후 | 19 | 11 | - | - |
포인터가 main 변수의 주소를 직접 가리키고 있으므로, *a와 *b를 통해 main의 값이 실제로 교환됩니다.
비교 정리
| 구분 | Call by Value | Call by Reference |
|---|---|---|
| 전달 방식 | 값 복사 | 주소 전달 |
| 매개변수 | int a | int *a |
| 호출 방법 | swap(a, b) | swap(&a, &b) |
| 원본 변경 | 불가 | 가능 |
배열을 함수에 전달하면? 심화
C에서 배열 이름 arr은 배열의 첫 번째 칸(arr[0])이 저장된 메모리 주소를 나타냅니다. 이것은 C언어의 특수한 규칙입니다. 따라서 함수에 배열을 전달하면 &를 붙이지 않아도 자동으로 주소가 전달되어, 함수 내에서 배열 요소를 변경하면 원본도 변경됩니다.
시험 출제 포인트 심화
정보처리기사 실기에서는 Call by Value로 작성된 swap 함수가 자주 출제됩니다. 핵심은 다음과 같습니다.
- 매개변수가
int a, int b형태인지int *a, int *b형태인지 확인 int a, int b면 값 복사이므로 원본 변경 불가- 함수 내부에서 교환해도 main의 변수는 그대로
- 배열을 전달하면
&없이도 주소가 전달되어 원본 변경 가능
매개변수 전달 방식 핵심 정리
| 개념 | 설명 | 예시 |
|---|---|---|
| Call by Value | 값의 복사본을 전달 | change(s) → s의 복사본 전달 |
| 배열 전달 | 참조(주소)의 복사본 전달, 같은 배열을 가리킴 | data[0] = "B" → 원본 변경됨 |
| String 재할당 | 참조를 새 객체로 변경, 원본에 영향 없음 | s = "Z" → 로컬 변수만 변경 |
| 변수 스코프 | 메서드마다 독립적인 변수 공간을 가짐 | main의 s와 change의 s는 다른 변수 |
Java는 항상 Call by Value 쌩기초
Java에서 메서드에 매개변수를 전달할 때, 항상 값을 복사하여 전달합니다. 이것을 Call by Value(값에 의한 호출)라고 합니다.
change(a)를 호출하면 a의 값 10이 복사되어 매개변수 x에 전달됩니다. x = 100은 복사본 x만 변경하므로, 원본 a는 그대로 10입니다.
배열을 매개변수로 전달하면? 기초
배열은 참조 타입입니다. 참조 타입이란, 변수에 실제 데이터가 직접 들어있는 것이 아니라 데이터가 있는 곳을 가리키는 화살표(주소)가 들어있는 타입입니다. 배열 {"원본"}처럼 메모리에 실제로 만들어진 데이터 덩어리를 객체라고 합니다.
배열을 매개변수로 전달하면, 주소의 복사본이 전달됩니다. 같은 집 주소가 적힌 메모지를 두 장 만든 것과 같습니다. 복사본이지만 같은 배열 객체를 가리키기 때문에, 어느 쪽에서든 배열의 요소를 수정하면 원본에도 반영됩니다.
왜 원본이 변경될까?
main의 data와 change의 data는 서로 다른 변수이지만, 같은 배열 객체의 주소를 담고 있습니다. 어느 쪽에서든 그 주소의 배열 요소를 바꾸면, 같은 객체이므로 변경이 반영됩니다.
String 변수를 재할당하면? 기초
배열은 참조 타입이라 원본이 변경되었습니다. 그런데 참조 타입이라도 하는 행동에 따라 결과가 달라집니다. String도 참조 타입이지만, 변수에 새 값을 대입(재할당)하면 원본에 영향을 주지 않습니다.
배열 요소 변경과 뭐가 다를까?
배열 요소 변경 (data[0] = "B")은 배열 객체 안의 내용을 수정하는 것입니다. 같은 객체를 가리키는 모든 변수에서 변경이 보입니다.
변수 재할당 (s = "Z")은 변수가 다른 객체를 가리키도록 바꾸는 것입니다. 해당 변수만 변경되고, 원래 객체를 가리키는 다른 변수에는 영향이 없습니다.
변수 스코프 심화
스코프(Scope)란 변수가 존재하는 범위를 말합니다. 각 메서드의 { } 중괄호 안에서 만든 변수는 그 중괄호 안에서만 사용할 수 있고, 메서드가 끝나면 사라집니다.

main의 s와 change의 s는 이름만 같을 뿐 완전히 다른 변수입니다. change에서 s = "Z"로 바꿔도 main의 s는 여전히 "B"입니다.
data[0] = s가 실행되는 시점에서 s의 값은 "B"이므로, data[0]에 "B"가 들어갑니다. 그 뒤에 s = "Z"로 바꿔도 이미 복사된 data[0]의 값은 변하지 않습니다. 대입은 그 순간의 값을 복사하는 것이지, s와 data[0]이 연결되는 것이 아닙니다.
변수 상태 추적
| 시점 | main의 data[0] | main의 s | change의 data | change의 s |
|---|---|---|---|---|
| change 호출 전 | "A" | "B" | - | - |
| change 진입 시 | "A" | "B" | 같은 배열 | "B" (복사본) |
data[0] = s 후 | "B" | "B" | 같은 배열 | "B" |
s = "Z" 후 | "B" | "B" | 같은 배열 | "Z" |
| change 종료 후 | "B" | "B" | - | - |
최종 출력: data[0] + s = "B" + "B" = BB
정리
| 전달 방식 | 동작 | 원본 영향 | 예시 |
|---|---|---|---|
| 기본 타입 (int 등) | 값 복사 | 없음 | x = 100 → 원본 유지 |
| 배열 요소 수정 | 같은 객체의 내용 수정 | 있음 | data[0] = "B" → 원본 변경 |
| 참조 변수 재할당 | 로컬 변수만 변경 | 없음 | s = "Z" → 원본 유지 |
C 배열과 Java 배열의 차이 심화
C언어와 Java에서 배열을 함수에 전달하면 둘 다 원본 배열을 수정할 수 있습니다. 하지만 내부 동작 방식은 다릅니다.
C언어: 배열 이름 = 포인터
C에서 배열 이름은 배열의 첫 번째 요소의 메모리 주소입니다. 배열 이름 자체가 주소이므로, 포인터 연산이 가능합니다. 자세한 내용은 배열 이름은 주소다를 참고하세요.
Java: 배열 변수 = 객체의 참조
Java에서 배열 변수는 힙 메모리에 생성된 배열 객체의 참조(reference)를 저장합니다. 개념적으로 "주소를 담고 있다"고 이해할 수 있지만, C의 포인터와는 다릅니다.
비교
| 구분 | C언어 | Java |
|---|---|---|
| 배열 변수의 정체 | 첫 번째 요소의 메모리 주소 (포인터) | 배열 객체의 참조 (reference) |
| 포인터 연산 | 가능 (arr + 1, *(arr + 2)) | 불가능 (컴파일 에러) |
| 주소 직접 확인 | 가능 (printf("%p", arr)) | 불가능 |
| 배열 크기 확인 | 직접 추적 필요 | arr.length로 확인 가능 |
| 함수에 전달 시 | 첫 번째 요소의 주소가 전달됨 | 배열 객체의 참조 복사본이 전달됨 |
| 함수 안에서 원본 수정 | 가능 | 가능 |