삶의 공유

배열(Array) vs 벡터(Vector), 도대체 무엇을 써야 할까? (feat. std::array) 본문

Programing언어/C++

배열(Array) vs 벡터(Vector), 도대체 무엇을 써야 할까? (feat. std::array)

dkrehd 2025. 11. 30. 20:37
728x90
반응형

 

C++로 개발을 하다 보면 데이터를 담는 그릇, 즉 '컨테이너'를 선택해야 하는 순간이 옵니다. 가장 기본인 C-Style 배열, 가장 많이 쓰는 std::vector, 그리고 모던 C++의 std::array까지. 이 셋은 비슷해 보이지만 메모리 구조와 성능에서 큰 차이가 있습니다.

오늘은 이 세 가지의 차이점을 코드를 통해 뜯어보고, 상황에 맞는 최적의 선택이 무엇인지 알아보겠습니다.

1. 코드 한 줄 한 줄 뜯어보기

우선, 예시 코드를 통해 각 컨테이너가 어떻게 선언되고 사용되는지, 그리고 왜 에러가 발생하는지 자세히 살펴보겠습니다.

C++
 
#include <vector> // std::vector를 사용하기 위한 헤더
#include <array>  // std::array를 사용하기 위한 헤더

int main()
{
    // 1. C-Style 배열 (Raw Array)
    int x[5] = {1, 2, 3, 4, 5};

    // 2. std::vector (동적 배열)
    // C++17부터는 타입 추론(CTAD)으로 <int> 생략 가능
    std::vector v = {1, 2, 3, 4, 5}; 

    // 데이터 접근 (공통점)
    x[0] = 10; // 인덱스를 통한 직접 접근
    v[0] = 10; // 벡터도 동일하게 접근 가능

    // 3. std::array (모던 C++ 고정 배열)
    std::array a = {1, 2, 3, 4, 5}; 
    a[0] = 10; 
    
    // std::array의 장점: 멤버 함수 사용 가능
    auto n = a.size(); // 현재 배열의 크기(5)를 반환

    // [중요 설명 필요] 아래 코드는 컴파일 에러가 발생합니다!
    // a.resize(3);    // Error!
    // a.push_back(3); // Error!
}

🔍 상세 분석 및 "설명 필요" 구간 해설

1) 기존 배열 (int x[5]) vs 벡터 (std::vector)

  • int x[5] = ...: C언어 시절부터 사용된 가장 기본적인 배열입니다. 선언과 동시에 스택(Stack) 메모리에 5개의 정수 공간이 고정적으로 할당됩니다. 아주 빠르지만, 크기를 나중에 바꿀 수 없고 size() 같은 편리한 함수가 없습니다.
  • std::vector v = ...: C++ 표준 라이브러리(STL)의 핵심입니다. 데이터가 힙(Heap) 메모리에 저장되므로 프로그램 실행 중에 크기를 자유롭게 늘리거나 줄일 수 있습니다. 유연함이 최대 장점입니다.

2) std::array의 등장 (std::array a = ...)

C++11에서 도입된 std::array는 "배열의 성능"과 "벡터의 편리함"을 합친 하이브리드입니다.

  • 동작 원리: int x[5]처럼 데이터가 스택(Stack) 메모리에 저장됩니다. 즉, 힙 할당에 따른 오버헤드가 없어 성능이 매우 뛰어납니다.
  • 차이점: 그러면서도 a.size()처럼 객체지향적인 멤버 함수를 제공하여 관리가 훨씬 수월합니다.

3) [핵심] 왜 resize와 push_back에서 에러가 날까?

위 코드 주석에 있는 a.resize(3);와 a.push_back(3);는 std::array에서 사용할 수 없는 문법입니다. 그 이유는 다음과 같습니다.

Why?

std::array는 태생이 **'고정 크기 배열(Fixed-size Array)'**이기 때문입니다.

  • resize(3) 불가: 스택 메모리에 할당된 std::array는 컴파일 시간에 크기가 결정되어야 합니다. 실행 중에 크기를 줄이거나 늘리는 것은 불가능합니다.
  • push_back(3) 불가: 뒤에 요소를 추가하려면 메모리 공간이 늘어나야 하는데, std::array는 공간이 고정되어 있어 더 이상 데이터를 밀어 넣을 수 없습니다. (이 기능들은 유동적인 std::vector만의 특권입니다.)

2. 메모리 구조와 성능 비교 (Stack vs Heap)

이 차이를 이해하려면 메모리 구조를 알아야 합니다.

특징 Raw Array (int[]) std::array std::vector
메모리 위치 Stack (빠름) Stack (빠름) Heap (데이터), Stack (관리 객체)
메모리 구조 연속된 메모리 연속된 메모리 연속된 메모리
크기 변경 불가능 (고정) 불가능 (고정) 가능 (동적 - resize, push_back)
성능 최상 최상 (Raw Array와 거의 동일) 중간 (힙 할당/해제 오버헤드 존재)
편의성 낮음 (멤버 함수 없음) 높음 (size, iterator 등 제공) 최상 (다양한 유틸리티 제공)
  • 성능이 민감한 프로그램이라면?
  • 힙 메모리를 사용하는 std::vector는 메모리를 할당하고 해제하는 비용이 발생합니다. 반면, 스택을 사용하는 Raw Array와 std::array는 이 비용이 거의 "0"에 가깝습니다. 따라서 극한의 성능이 필요하고 데이터 개수가 정해져 있다면 std::array가 정답입니다.
  • 유연성이 필요하다면?
  • 데이터가 몇 개 들어올지 모르는 상황이라면, 고민할 것 없이 **std::vector**를 사용해야 합니다.

3. 요약 (Key Takeaways)

오늘 내용을 한 줄로 요약하면 **"목적에 맞는 도구를 써라"**입니다.

  1. std::vector: 데이터 개수가 변할 수 있거나, 너무 커서 스택에 담기 힘들 때 사용하세요. (가장 범용적)
  2. int x[5] (Raw Array): 레거시 코드나 아주 간단한 테스트가 아니면 가급적 피하세요.
  3. std::array: 데이터 개수가 고정되어 있고, 최고의 성능이 필요하면서도 코드의 안정성을 챙기고 싶다면 무조건 사용하세요. resize나 push_back은 안 되지만, 기존 배열의 완벽한 상위 호환입니다.
반응형