삶의 공유

C++ 벡터의 capacity와 size 완전 이해하기 본문

Programing언어/C++

C++ 벡터의 capacity와 size 완전 이해하기

dkrehd 2025. 11. 18. 22:56
728x90
반응형

 

📝 C++ 벡터의 capacity와 size 완전 이해하기

— 메모리 동작부터 성능 최적화까지, STL vector 내부 구조를 제대로 알아보자

C++ std::vector는 실무와 알고리즘 구현에서 가장 자주 사용되는 컨테이너입니다. 그러나 많은 초보자들이 벡터의 핵심 개념인 capacity(용량)size(요소 개수) 의 차이를 제대로 이해하지 못한 채 사용하고 있어, 불필요한 성능 저하나 메모리 낭비로 이어지는 경우가 많습니다. 오늘은 이 두 개념을 예제로 하나씩 보여주며, 벡터가 실제로 메모리를 언제 할당하고 언제 늘리고 줄이는지 깊이 있게 설명해보겠습니다.


1. capacity와 size란 무엇인가?

벡터는 내부적으로 동적 배열(dynamic array) 을 사용하므로, 새로운 요소가 추가되면 내부 배열 크기를 늘리기 위한 메모리 재할당이 필요할 수 있습니다. 이때 재할당 비용을 줄이기 위해 벡터는 “현재 요소 개수(size)” 보다 더 큰 메모리 공간(capacity)을 미리 확보해두곤 합니다.

  • size
    → 현재 벡터가 실제로 보관하고 있는 원소의 개수
  • capacity
    → 현재 벡터가 재할당 없이 저장할 수 있는 원소의 최대 개수

즉, capacity는 실제 배열의 크기이고, size는 그 배열 중에서 실제로 사용 중인 칸의 개수입니다.


2. capacity와 size 변화를 코드로 확인해보자

아래 예제는 vector의 resize, push_back, shrink_to_fit, clear 등 다양한 연산을 통해 capacity와 size가 어떻게 변하는지 확인하는 코드입니다.

 
#include <iostream>
#include <vector>

void check(const std::vector<int>& v)
{
    std::cout << "capacity: " << v.capacity()
              << ", size: " << v.size() << '\n';
}

int main()
{
    std::vector v = { 1,2,3,4,5 };
    v.resize(3);
    check(v);

    v.push_back(0);
    v.shrink_to_fit();
    check(v);
    v.push_back(0);
    check(v);
    v.clear();
    check(v);
}
 

2-1. resize(3) 의 동작

초기 상태:

 
std::vector v = {1,2,3,4,5};
  • capacity = 5
  • size = 5

그 다음,

 
v.resize(3);

실행 결과:

 
capacity: 5, size: 3

📌 왜 size만 줄고 capacity는 그대로일까?

resize(3)은 요소 개수를 줄이는 동작이며, 내부 메모리를 해제하지 않습니다.
즉, 메모리는 그대로 둔 채 “사용 중인 요소 개수(size)”만 조정하는 것입니다.

벡터가 이렇게 설계된 이유는 간단합니다:

불필요한 메모리 재할당을 줄여 성능을 지키기 위해서

size는 감소했지만 capacity는 유지되어 다음 push_back이 빠르게 수행됩니다.


2-2. push_back(0) 실행 후

resize(3) 이후 capacity는 5이므로, 하나의 push_back은 재할당 없이 매우 빠르게 수행됩니다.

 
capacity: 5, size: 4

이 단계에서는 capacity > size 이므로 새로운 메모리 할당은 일어나지 않습니다.


2-3. shrink_to_fit() 의 동작

 
v.shrink_to_fit();

실행 결과:

 
capacity: 4, size: 4

📌 shrink_to_fit은 실제 메모리를 줄이는 함수

shrink_to_fit()은 현재 size에 맞춰 capacity를 줄입니다.
C++ 표준에서는 shrink_to_fit이 “요청(request)” 이라고만 정의하고 있어, 꼭 메모리를 줄여야 한다고 강제하지 않지만 대부분의 구현에서는 실제 메모리 축소가 이루어집니다.

즉, size == capacity인 상태로 만들어 내부 메모리 낭비를 제거합니다.


2-4. shrink_to_fit 이후 push_back 수행

이제 capacity == size == 4 인 상태에서

 
v.push_back(0);

을 호출하면,

  • capacity == size 이므로 새로운 메모리 할당 필요
  • 재할당 비용이 발생하기 때문에 느린 연산
  • 대부분의 STL 구현에서는 capacity를 여유 있게 늘립니다
    예: 4 → 6, 4 → 8 등 (구현마다 다름)

결과:

 
capacity: 6, size: 5

2-5. clear() 는 메모리를 비우지 않는다

v.clear();

 

결과:

 
capacity: 6, size: 0
  • clear()는 size만 0으로 만들고
  • 실제 메모리(capacity)는 건드리지 않습니다.

❗ 메모리도 함께 비우고 싶다면?

v.clear(); // or v.resize(0); 
v.shrink_to_fit();
 

이렇게 하면 capacity도 실제로 0 또는 최소 상태로 줄어듭니다.


3. reserve와 생성자 초기화 비교

두 번째 예제에서는 세 가지 방식으로 벡터를 생성하여 capacity와 size가 어떻게 달라지는지 확인합니다.

 
std::vector<int> v1;
check(v1);

std::vector<int> v2(5);
check(v2);

std::vector<int> v3;
v3.reserve(5);
check(v3);
 

3-1. std::vector<int> v1;

 
capacity: 0, size: 0
  • 아무 것도 없는 빈 벡터입니다.

3-2. std::vector<int> v2(5);

 
capacity: 5, size: 5
  • 5개의 요소가 0으로 초기화된 벡터 생성
  • size와 capacity 모두 5

3-3. v3.reserve(5);

 
capacity: 5, size: 0

reserve는 메모리를 미리 확보만 하는 함수입니다.

  • 요소는 없음 (size = 0)
  • 메모리만 5칸 확보 (capacity = 5)

reserve의 장점

미래에 push_back이 여러 번 발생할 것이 예상된다면,
개발자가 capacity를 미리 크게 잡아두면 성능이 크게 향상됩니다.

단점:

  • 실제 데이터를 저장하지 않아도 메모리를 점유한다는 점에서 메모리 관점에서 손해가 될 수 있습니다.

📘 전체 핵심 요약

  • size는 실제 저장된 요소 개수이고 capacity는 실제 확보한 메모리 크기이다.
  • resize()는 size만 바꾸며, 내부 메모리는 해제하지 않는다.
  • push_back()은 capacity가 충분하면 빠르고, capacity가 꽉 차 있으면 느리다.
  • shrink_to_fit()은 실제 메모리를 size에 맞춰 줄인다.
  • clear()는 size만 0으로 만들고 메모리를 해제하지 않는다.
  • reserve()는 미래에 많은 push_back이 예상될 때 유용하다.
  • 벡터의 메모리 정책을 이해하면 성능 최적화에 큰 도움을 얻을 수 있다.
반응형