Intro
C++에서 함수에 배열을 매개변수로 사용할 때, 다음과 같은 syntax를 따르게 됩니다.
여기서 주의해야 하는 것이 하나 있습니다.
void print_array(int numbers []);
배열을 인자로 사용하는 법
우리가 알다시피 C++에서 함수에 매개변수를 전달하는 것은 직관적입니다.
정해진 자료형에 대한 값을 괄호 내에 입력하면 됩니다.
그리고 이는 벡터 또한 마찬가지입니다.
하지만 C++ 배열을 매개 변수로 사용할 때는 조금 다릅니다.
C++에서 배열의 이름은 해당 배열의 첫번째 값이 들어있는 데이터상 주소를 나타냅니다.
다시 말하자면, 배열의 이름은 해당 배열의 시작 지점의 주소를 나타냅니다.
그렇기 때문에, 함수에 배열을 인자로 사용하게 되면, 다른 자료형과는 다르게 자료가 통째로 복사되어 전달되는 형태가 아닙니다.
해당 배열의 첫번째 자료의 데이터상 위치 (주소)가 전달됩니다.
이런 이유로 당연하게도 함수는 해당 배열에 얼마나 많은 자료가 들어있을지 알 수 없습니다.
프로그램은 해당 배열에 대해 얼마나 많은 반복을 해야 할 지 모르게 되는 겁니다.
이런 문제점 때문에 C++에서 함수에 배열을 전달할 때는 해당 배열에 얼마나 많은 자료가 들어있는지,
자료의 길이 또한 포함하여 전달해야 합니다.
틀린 예시를 하나 보여드리겠습니다.
void print_array(int numbers []);
int main() {
int my_numbers[] {1, 2, 3, 4, 5,};
print_array(my_numbers);
return 0;
}
void print_array(int numbers []){
// 배열에 얼마나 많은 자료가 있는지 (길이) 를 모릅니다.
// 배열의 크기를 입력받을 필요가 있습니다.
}
그렇다면 배열의 크기를 전달하는 것은 어떻게 하면 될까요?
다음과 같은 방법으로 하면 해결됩니다.
void print_array(int numbers [], size_t size);
int main() {
int my_numbers[] {1, 2, 3, 4, 5};
print_array(my_numbers, 5);
return 0;
}
void print_array(int numbers [], size_t size){
for (size_t i{0}; i < size; i++){
cout << numbers[i] << endl;
}
}
이번에는 size_t 의 자료형으로 배열의 크기를 나타내는 size라는 인자를 받았습니다.
(size_t는 대략 음수로 변할 일 없는, 인덱스와 같은 값을 표현할때 주로 사용되는 unsigned integer 자료형으로 이해하면 좋습니다.)
이렇게 하면 문제가 해결됬습니다.
추가적으로 배열의 크기를 계산하는 방법 중 대표적으로 사용되는 방법은 다음과 같습니다.
int size = sizeof(arr) / sizeof(arr[0])
그런데, 위에서 말했다시피 우리는 인자를 복사해서 처리하는 방식이 아니고,
배열의 첫번째 자료의 주소를 전달한 형태입니다.
여기서 생기는 차이점은 바로 '함수 내에서 직접적으로 인자로 전달된 배열을 변경시킬 수 있다' 는 점입니다.
마치 Pass by reference 처럼요.
다음 코드에서 이를 직접적으로 확인할 수 있습니다.
#include <iostream>
using namespace std;
void zero_array(int numbers[], size_t size) {
for (size_t i{0}; i < size; i++) {
numbers[i] = 0;
}
}
void print_array(int numbers[], size_t size) {
for (size_t i{}; i < size; i++) {
cout << numbers[i] << endl;
}
}
int main() {
int my_numbers[] = {1, 2, 3, 4, 5};
print_array(my_numbers, 5);
cout << endl;
zero_array(my_numbers, 5);
print_array(my_numbers, 5);
return 0;
}
zero_array 함수를 통해서 my_numbers 배열의 값들이 0으로 변경된 것을 알 수 있습니다.
다른 자료형을 다룰 때 처럼 return 된 값을 다시 변수에 저장한다거나, 그런 modification을 거치지 않았음에도 그렇습니다.
이런 특징은 유용하게 활용될 수도 있는 반면, 원치 않는 결과를 낳을 수도 있을 것 입니다.
그렇다면 이를 해결할 방법을 알아보겠습니다.
원본 배열의 변화를 방지하는 방법
원본 배열의 자료들을 변화시키지 않고, 인자로 사용하는 방법은 매우 간단합니다.
함수를 정의할 때 입력하는 자료형 앞에 const 를 붙이는 것 입니다.
상수에 대해 공부했던 적이 있다면, const 키워드에 대해 익숙할 것 같습니다.
상수형 자료는 const 키워드를 앞에 붙여서 선언되고, 선언 이후에 읽을 수는 있지만 변형되지 않습니다.
다음의 간단한 코드로 완벽하게 이해할 수 있습니다.
const int num {30};
cout << num << endl; // 30
num = 15; // Compiler Error
결과에서 알 수 있듯이 읽을 수는 있으나, 변형할 수는 없습니다.
동일하게 우리의 배열 함수에 적용한다면 역시 같은 효과가 발생합니다.
배열을 읽을 수는 있지만, 변형할 수 없는 것입니다.
즉, 다음과 같은 코드는 컴파일러 에러가 발생합니다.
void print_array (const int numbers []; size_t size) {
for (size_t i {0}; i < size; i++{
cout << numbers[i] << endl;
}
numbers[i] = 0; // 배열의 값을 변경시키려는 모든 시도는 compiler error를 발생시킵니다.
}
요약
C++에서,
1. 함수에 배열을 인자로 전달하면 배열의 첫번째 자료의 주소가 전달된다.
2. 함수에 배열을 인자로 사용하기 위해서는 배열 자체 뿐 아니라, 배열의 크기 (size)를 함께 전달하는게 좋다.
3. 함수에 전달된 배열은 자료가 복사된 것이 아니기 때문에 원 배열 값에 직접적 변화를 준다.
4. 이를 방지하기 위해서는 const 키워드를 함수 선언 시 배열 명 이전에 사용하면 된다.
'C++ > 함수 (Function)' 카테고리의 다른 글
[C++] 함수에서 포인터를 반환하기 (Returning a Pointer from a Function) (1) | 2023.12.22 |
---|---|
[C++] 재귀 함수 (Recursive Function) (0) | 2023.12.18 |
[C++] 참조로 전달하기 (Pass by Reference) (1) | 2023.12.17 |
[C++] 함수 오버로딩 (Function Overloading) (1) | 2023.12.17 |
[C++] 함수 프로토타입 (Function Prototype) & 디폴트 매개 변수 (Default Argument Values) (0) | 2023.12.16 |