C언어에는 포인터라는 개념이 있어요. 포인터 변수를 줄여 부르는 말로, 메모리 주소를 가리키는 변수예요. * 기호와 함께 사용되는데, 선언할 때와 호출할 때의 의미가 달라져요.
선언할 때 사용되는 * 은 해당 변수가 포인터 변수라는 것을 의미하고, 호출할 때 사용되는 * 은 해당 포인터 변수가 가리키는 값을 의미해요. 또한 메모리 주소를 다루는 변수이므로 주소를 나타내는 & 기호와 함께 사용돼요.
int a = 10;
int* pA = &a;
printf("%d", a); // 10
printf("%d", *pA); // 10
int arr[] = {1, 2, 3};
int * p = &arr;
printf("%d", &arr); // arr[0]의 주소
printf("%d", p); // arr[0]의 주소
printf("%d", *(p+1)); // 2
Call by Reference는 값이 아닌 주소값이 복사된다는 것을 의미해요. 자바스크립트에서는 원시 타입은 Call by Value로, 그 이외의 객체 타입은 Call by Reference 방식으로 동작해요.
함수에서 사용되는 인수에는 실제로 값을 가지는 실인수와, 실인수를 받아 값을 복사하는 형식인수가 있어요.
함수를 호출하며 인수로 값을 넘길 때, 넘겨진 인수가 함수 내부에서 직접 처리된다고 생각하기 쉽지만 실제로는 그렇지 않아요. 넘겨진 인수의 값이나 주소를 복사해서 사용하게 돼요. 코드를 통해 이로 인해 생기는 변화를 살펴볼게요.
function test(b) { // b는 형식인수
b += 3;
console.log(b); // 13
}
let a = 10;
test(a); // a는 실인수
console.log(a); // 10
위의 경우는 내부에서 복사된 값에 변경이 일어났지만, 실제 넘겨줬던 인수의 값에는 변화가 없는 것을 확인할 수 있어요. 변화된 값을 외부에서도 사용하기 위해서는 return 을 통해 반환해 주어야 해요.
function test2(array) {
array = 10;
console.log(array); // 10
}
let arr = [1, 2, 3];
test2(arr);
console.log(arr); // [ 1, 2, 3 ]
이 코드도 마찬가지예요. 함수 내부의 array 변수가 외부 arr 변수의 주소를 복사해 갔어요. 하지만 내부에서 array 에 10이라는 새로운 값을 재할당하면서, 외부 arr 의 실제 값은 변함없이 그대로 유지되고 있어요.
function test3(array) {
array[0] += 2;
}
let arr = [1, 3];
test3(arr);
console.log(arr); // [ 3, 3 ]
마지막 코드는 앞선 예시들과 다르게 동작해요. 함수 내부에서 받아온 array 의 요소를 변경했는데, 실제 외부 arr 의 요소에도 그 변경이 적용된 것을 볼 수 있어요. 이는 arr 와 array 가 완전히 같은 메모리 주소를 참조하고 있기 때문이에요.
| 변수명 | 스택 (주소) | 스택 (값) | 힙 (주소) | 힙 (값) |
|---|---|---|---|---|
| arr | 0x01 | 0x001 | 0x001 | [1, 3] |
| array | 0x10 | 0x001 |
위 표를 보면 arr 와 array 가 동일한 힙 영역 주소(0x001)를 가리키고 있어요. 그래서 array 로 요소의 값을 변경하면 동일한 주소를 참조하는 arr 의 값도 함께 변경돼요.
반면, 두 번째 예시처럼 내부에 새로운 값을 아예 재할당하는 경우에는 아래 표처럼 동작해요.
| 변수명 | 스택 (주소) | 스택 (값) | 힙 (주소) | 힙 (값) |
|---|---|---|---|---|
| arr | 0x01 | 0x001 | 0x001 | [1, 3] |
| array | 0x10 | 10 |
두 번째 예시의 경우, array 에 새로운 값 10을 재할당하면서 기존의 주소 참조가 끊어지고 array 가 가진 스택의 값만 10으로 변경된 것을 확인할 수 있어요.