C++ 배열을 함수에 전달하는 법: 포인터, 크기 전달, 템플릿까지 정리
📌 배열을 함수에 넘기는 다양한 방법을 정확히 이해하고 코드 안정성까지 확보하세요
C++에서 배열을 함수에 전달할 때, 다른 변수와는 조금 다른 규칙이 적용된다는 사실 알고 계셨나요?
배열은 함수의 인자로 넘길 때 자동으로 포인터로 변환되어 전달됩니다.
그래서 함수 내부에서는 배열의 크기를 알 수 없게 되는데요, 이를 잘못 이해하고 있다면 의도치 않은 버그를 만들 수도 있어요.
특히 초보자 분들에겐 이 부분이 꽤 헷갈리는 개념이기도 하죠.
이 글에서는 배열을 함수에 전달할 때의 정확한 원리와 함께,
배열 크기를 함께 전달하는 방식, std::array나 템플릿을 활용해 타입 안정성과 유연성을 높이는 방법까지 자세히 안내드릴게요.
단순히 문법을 소개하는 것이 아니라, 실제로 어떻게 쓰이고, 왜 그렇게 설계됐는지까지 함께 설명드리니,
지금부터 하나씩 따라오시면 어렵지 않게 이해하실 수 있을 거예요.
📋 목차
🔗 배열을 함수에 넘기면 무슨 일이 생길까요?
C++에서 배열을 함수에 인자로 넘기면, 배열 전체가 아닌 첫 번째 요소의 주소만 전달됩니다.
즉, 함수에서는 배열 자체를 받는 것이 아니라 배열이 시작되는 메모리 위치만 알 수 있게 되는 것이죠.
이러한 동작 방식은 C 언어부터 이어져 내려오는 전통적인 처리 방식입니다.
이 때문에 함수 내부에서는 sizeof 연산자를 사용하더라도 배열의 전체 크기를 정확히 알 수 없습니다.
예를 들어, 메인 함수에서 int arr[5]를 선언하고 이를 다른 함수에 넘기면,
그 함수는 단지 int* 포인터를 받는 셈이 되기 때문에 배열의 크기를 잃어버리게 됩니다.
void printArray(int arr[]) {
std::cout << sizeof(arr) << std::endl; // 포인터 크기 출력 (예: 8)
}
위 코드처럼 배열을 함수로 넘겼을 때 sizeof(arr)는 실제 배열 크기가 아닌 포인터 크기만 반환합니다.
이는 곧 배열의 원래 길이를 함수 내에서 알 수 없다는 뜻이기도 하죠.
⚠️ 주의: 배열을 함수에 넘길 때는 크기를 반드시 별도로 함께 전달하거나, 다른 방식으로 전달 구조를 바꿔야 안전합니다.
이제 왜 배열을 함수에 넘길 때 크기를 함께 전달해야 하는지 이해되셨죠?
다음 단계에서는 이 크기를 안전하게 다루는 방법을 설명해드릴게요.
🛠️ 배열 크기를 함께 전달해야 하는 이유
배열을 함수에 전달하면 포인터만 전달되기 때문에, 함수 내부에서는 배열의 길이를 알 수 없습니다.
그렇기 때문에 배열의 요소 개수(크기)를 별도로 인자로 전달하는 것이 일반적인 방식입니다.
이렇게 하면 함수 안에서도 안전하게 루프를 돌거나, 유효한 범위 내에서 배열을 다룰 수 있게 되죠.
아래 예제는 배열과 그 길이를 함께 전달하는 가장 기본적인 패턴입니다.
void printArray(int arr[], int size) {
for (int i = 0; i < size; i++) {
std::cout << arr[i] << " ";
}
std::cout << std::endl;
}
int main() {
int data[] = {1, 2, 3, 4, 5};
int len = sizeof(data) / sizeof(data[0]);
printArray(data, len);
}
- 🔢배열을 함수에 넘길 때는 크기를 항상 인자로 같이 넘깁니다.
- 📏
sizeof(arr)는 포인터로 전달된 순간 정확하지 않으므로 사용 주의가 필요합니다. - 🧩함수 호출 시에는
sizeof(arr) / sizeof(arr[0])방식으로 길이를 계산합니다.
이제 배열의 크기를 함께 전달해야 하는 이유와 방법을 알게 되었어요.
하지만 배열이 단순히 1차원이 아니라면 이야기가 달라지겠죠?
다음 섹션에서는 다차원 배열을 함수에 전달할 때 어떤 점이 다른지 살펴볼게요.
⚙️ 다차원 배열은 어떻게 전달하나요?
1차원 배열과 달리 2차원 이상 배열을 함수에 전달할 때는 조금 더 신경 써야 할 부분이 있습니다.
바로 두 번째 차원 이상의 크기를 반드시 명시해주어야 한다는 점인데요, 이는 C++ 컴파일러가 메모리 주소 계산을 위해 필요한 정보이기 때문입니다.
아래 코드를 보며 어떻게 선언하고 넘겨야 하는지 살펴볼게요.
void printMatrix(int matrix[][3], int rows) {
for (int i = 0; i < rows; i++) {
for (int j = 0; j < 3; j++) {
std::cout << matrix[i][j] << " ";
}
std::cout << std::endl;
}
}
int main() {
int mat[2][3] = { {1, 2, 3}, {4, 5, 6} };
printMatrix(mat, 2);
}
이처럼 2차원 배열을 넘기려면 int matrix[][3]처럼 반드시 두 번째 차원의 크기를 명시해야 합니다.
만약 템플릿을 사용하지 않는다면, 이 제한은 피할 수 없어요.
💎 핵심 포인트:
2차원 배열을 함수에 넘길 때는 반드시 고정된 열 크기를 함께 선언해야 합니다. 그렇지 않으면 컴파일 오류가 발생합니다.
그렇다면 이런 불편함을 줄이기 위한 방법은 없을까요?
다음에서는 std::array를 활용해 보다 안전하고 명확하게 배열을 전달하는 방법을 살펴보겠습니다.
🔌 std::array로 안전하게 넘기기
C++11부터 도입된 std::array는 고정 길이 배열을 안전하고 명확하게 다룰 수 있도록 도와주는 컨테이너입니다.
일반 배열과 달리 크기 정보를 내부에 포함하고 있기 때문에, 함수에 전달해도 크기 손실 없이 사용할 수 있다는 장점이 있죠.
기존 배열보다 타입 안정성이 뛰어나고, STL과의 연동도 쉬워 다양한 상황에서 유용하게 활용할 수 있습니다.
사용법도 매우 직관적이에요.
#include <array>
#include <iostream>
void printArray(const std::array<int, 5>& arr) {
for (int n : arr) {
std::cout << n << " ";
}
std::cout << std::endl;
}
int main() {
std::array<int, 5> data = {1, 2, 3, 4, 5};
printArray(data);
}
- 🧾
std::array는 크기 정보를 컴파일 타임에 유지합니다. - 🔒함수에서 크기를 다시 계산할 필요가 없으며 안전하게 순회할 수 있습니다.
- ⚙️기존 C 배열과 다르게 STL 함수와 함께 쓸 수 있습니다.
std::array는 고정된 크기의 배열을 다룰 때 매우 유용하지만, 크기가 런타임에 결정되는 경우에는 사용할 수 없습니다.
그럴 땐 템플릿을 이용한 더 유연한 방식도 고려해볼 수 있어요.
다음 섹션에서 그 방법을 소개할게요.
💡 템플릿을 활용한 배열 전달 패턴
배열을 함수에 안전하게 전달하면서도 유연성과 재사용성을 확보하고 싶다면, 템플릿을 사용하는 것이 탁월한 방법입니다.
템플릿은 배열의 크기를 타입 정보로 가져올 수 있어, 런타임이 아닌 컴파일 타임에 크기를 유지할 수 있도록 해줍니다.
이 방식은 특히 타입 안정성을 높이고, sizeof나 별도 인자 없이도 배열 전체를 정확히 다룰 수 있는 장점이 있어요.
template <typename T, size_t N>
void printArray(const T(&arr)[N]) {
for (size_t i = 0; i < N; ++i) {
std::cout << arr[i] << " ";
}
std::cout << std::endl;
}
int main() {
int numbers[] = {10, 20, 30, 40, 50};
printArray(numbers); // 크기 전달 없이도 동작
}
💎 핵심 포인트:
템플릿을 활용하면 배열의 크기를 자동으로 추론할 수 있어 별도의 길이 인자를 전달하지 않아도 됩니다. 이로 인해 코드의 안정성과 간결함이 크게 향상됩니다.
이처럼 템플릿은 배열 크기 문제를 우아하게 해결할 수 있는 강력한 도구입니다.
특히 C++17 이상에서는 std::span도 함께 활용하면 더 큰 유연성을 얻을 수 있어요.
이제 배열 전달에 관한 다양한 방식들을 살펴봤으니, 자주 묻는 질문들도 정리해볼게요.
❓ 자주 묻는 질문 (FAQ)
배열을 함수에 전달하면 포인터가 된다고 했는데, 복사가 되는 건가요?
함수 안에서 배열의 크기를 알 수 있는 방법은 없나요?
std::array는 일반 배열보다 무거운가요?
2차원 배열을 함수로 넘길 때 가변 길이 배열은 안 되나요?
std::vector는 배열 전달에 더 좋은가요?
템플릿을 쓰면 코드가 너무 복잡해지지 않나요?
std::span은 어떤 상황에서 사용하나요?
배열을 참조로 전달할 수도 있나요?
🧭 C++ 배열 전달의 모든 것, 안정성과 유연성까지 챙기세요
C++에서 배열을 함수에 전달하는 방식은 단순히 문법 차원을 넘어 코드의 안정성과 유지보수성에 큰 영향을 미칩니다.
배열은 함수에 넘길 때 포인터로 처리되기 때문에 크기 정보를 잃는다는 점을 꼭 기억해야 하고,
이로 인해 함수에서 안전하게 배열을 다루기 위해서는 크기를 함께 전달하거나, std::array 또는 템플릿을 활용해야 합니다.
이 글에서는 기본 배열 포인터 방식부터 다차원 배열 전달, std::array의 장점, 템플릿을 활용한 고급 기법까지 자세히 살펴보았습니다.
코드의 가독성과 안정성을 높이고 싶다면, 배열 전달 방식 하나만으로도 충분히 변화된 결과를 만들어낼 수 있어요.
지금까지 다뤄본 내용을 실제 코드에 적용해보면서 자연스럽게 익혀보시길 추천드립니다.
🏷️ 관련 태그:C++배열, 배열함수전달, 포인터기초, std::array, 템플릿함수, 배열크기전달, 다차원배열, C++기초문법, 함수인자, C++20span