Computer >> 컴퓨터 >  >> 프로그래밍 >> C++

C/C++ 배열 붕괴(Array Decay)란 무엇이고 왜 발생할까?

C와 C++에서 배열(array)포인터(pointer)는 매우 유사하게 동작합니다. 하지만 두 개념 사이에는 미묘하면서도 중요한 차이점이 존재합니다. 대표적인 예가 바로 sizeof 연산자의 동작 방식입니다.

배열과 포인터의 sizeof 차이

배열 이름을 포인터로 변환(전달)하는 순간, 원래 배열의 크기 정보를 잃어버리게 됩니다. 다음 예제를 통해 이를 확인할 수 있습니다.

예제 코드

#include<iostream>

int main() {
    const int a[] = { 2, 3, 5, 7, 11 };
    const int* p = a;
    std::cout << ( sizeof(p) != sizeof(a) );
}

실행 결과

1

위 코드에서 sizeof(p) != sizeof(a)의 결과가 1(참)로 출력됩니다. 이는 포인터에 적용된 sizeof가 배열 전체의 크기가 아니라 포인터 변수 자체의 크기(일반적으로 4바이트 또는 8바이트)를 반환하기 때문입니다. 반면 sizeof(a)는 배열 전체 크기인 5 × sizeof(int), 즉 보통 20바이트를 반환합니다.

배열 붕괴(Array Decay)란?

이처럼 배열이 포인터로 변환되면서 배열의 크기 정보와 타입 정보를 잃는 현상배열 붕괴(decay)라고 부릅니다. C/C++에서는 다음과 같은 상황에서 배열 붕괴가 발생합니다.

  • 배열을 함수의 인자로 전달할 때 — 함수 매개변수의 배열 타입은 자동으로 포인터 타입으로 조정됩니다.
  • 배열 이름을 값으로 다른 포인터에 대입할 때
  • a + 1과 같은 포인터 산술 연산을 수행할 때

예를 들어 아래 두 함수 선언은 완전히 동일하게 해석됩니다.

void func(int arr[10]);   // int* arr로 붕괴됨
void func(int* arr);      // 위 선언과 동일

따라서 함수 내부에서는 sizeof(arr)로 배열의 실제 크기를 알 수 없으며, 별도의 크기 매개변수를 함께 전달해야 합니다.

배열 붕괴를 피하는 방법

C++에서는 이러한 문제를 피하기 위해 다음과 같은 방법을 사용할 수 있습니다.

  • 참조(reference)로 전달: void func(int (&arr)[10])처럼 배열 참조를 사용하면 크기 정보가 유지됩니다.
  • 템플릿 활용: 템플릿으로 배열 크기를 컴파일 타임에 추론할 수 있습니다.
  • std::array 사용: C++11부터 제공되는 std::array는 크기 정보를 항상 가지는 컨테이너로, 배열 붕괴 문제를 근본적으로 해결합니다.

결론적으로, 배열 붕괴는 C/C++의 배열과 포인터 간 암묵적 변환 규칙에서 비롯되는 현상으로, sizeof 결과의 차이에서 그 차이를 명확히 확인할 수 있습니다. 크기 정보가 중요한 코드에서는 참조 전달이나 std::array 사용을 권장합니다.