C/C++에서 size_t를 사용하는 이유

hwhyeons·2024년 7월 10일

C++을 사용하다보면, size_t라는 타입을 자주 볼 수 있습니다.

저는 VS Code에서 for문 자동 완성 기능을 사용하면서 for문의 변수로
int가 아닌 size_t가 기본으로 설정 되어 있는 것을 자주 보면서
왜 int가 아닌 굳이 size_t라는 타입을 사용하는지 궁금했습니다.




size_t 타입이란?

size_t는 C++에서 unsigned 정수형으로 정의된 타입입니다.
주 사용처는 루프, 인덱싱, 크기를 나타낼 때 사용합니다.

가장 중요한 부분은 "unsigned"라는 부분입니다.

즉, 음수가 표현이 안되므로, "크기", "인덱스" 등을 표현하기에
적절합니다.


cppreferecne의 [링크](https://en.cppreference.com/w/cpp/types/size_t)

을 참고하면,

"std::size_t can store the maximum size of a theoretically possible object of any type (including array)"

배열을 포함해서 어떠한 오브젝트의 크기도 표현할 수 있음을 알 수 있습니다.




굳이 size_t를 사용하는 이유

size_t는 이름에 맞게 "크기"를 나타낸다는 점을 직관적으로 알 수 있습니다.
따라서 이전에 작성한 코드를 나중에 다시 보거나,
또는 다른사람들이 코드를 읽었을 때 일관성을 유지할 수 있습니다.

하지만, unsigned의 특징이 필요하다면, unsigned int를 사용하면 되는 것이 아닐까요?

왜 굳이 unsigned int 대신 size_t를 사용해야 좋은 상황이 있는지 찾아봤습니다.

이 역시 cppreferecne의 링크를 참고하면,

std::size_t is commonly used for array indexing and loop counting. Programs that use other types, such as unsigned int, for array indexing may fail on, e.g. 64-bit systems when the index exceeds UINT_MAX or if it relies on 32-bit modular arithmetic.

즉, unsigned int형의 경우, 32비트 시스템과 64비트 시스템간의 차이로 의도치 않게 문제가 발생할 수 있음을 알 수 있습니다.




size_t 사용시 주의점

위에서 언급했듯이, size_t는 looping에도 자주 사용됩니다.

하지만, for loop에서 size_t를 사용하게 되는 상황에서, 인덱스가 감소하는
방향으로 사용시, 무한루프가 발생할 수 있습니다

int main()
{
    for (size_t i =10; i>=0; i--) {
        std::cout << i << "\n";
    }
    return 0;
}

이 코드는 size_t만 int로 바뀌면 역방향 for loop로 배우는 아주 기본적인 형태지만,

unsigned 타입의 특성상 0에서 더 값이 낮아지면, 음수가 되지 않고,
양수 표현 최대값에서 더 작아지는 방향으로 동작합니다.

따라서, 위와 같은 for loop에서는, 개발자가 의도한 방향은
i가 -1이 되면서 i >= 0이라는 조건이 False가 되면서 for loop가 종료되는 상황이지만,

실제로는 i가 -1이 아닌 18446744073709540068와 같은 값으로 바뀌기 때문에
무한루프로 빠질 수 있음을 주의해야합니다.



이와 같은 unsigned와 signed 차이에 의해, 함수의 매개변수로 값을 넘기는 상황에서

자동형변환이 이루어지는 경우 또 문제가 발생할 수 있습니다.

void func(size_t a) {
    std::cout << "func a : " << a;
}

int main()
{
    int k = -1;
    func(-1);
    return 0;
}

코드를 수행하면 결과는
18446744073709551615 입니다.




자동 형변환이 꼭 이렇게 함수의 인자로 넘기는 상황만 존재하는 것은 아닌데,
직관적으로 파악하기 어려운 부분 중에 하나가 대소비교 상황입니다.
벡터 등의 STL 컨테이너에서 원소의 개수를 얻기 위해서는 size()를 사용하는데, 이 size()의 리턴 타입은 size_t타입입니다.

vector<int> v;
int a = -1;
cout << boolalpha;
cout << (a < v.size()) << "\n";  // false
cout << (a < (int)v.size()) << "\n"; // true

이렇게 a < v.size()와 같이 대소비교에도 a가 형변환이 발생하는데 이때
a가 음수이므로 언더플로우가 발생해서 a는 매우 큰수가 됩니다.
따라서 첫번째 출력문의 경우 형변환이 발생하지 않아 의도치않은 결과가 발생할 수 있습니다.

0개의 댓글