프로그래밍 언어/C, C++

[C/C++] STL size 함수 숫자 오류

호무비 2022. 2. 26. 20:10
반응형

오늘은 C++ STL에서 size 함수 사용 시 숫자 오류가 발생하는 것에 관해 알아보고자 합니다.

 

어려운 문제는 아니고, 아마 굳이 C++이 아니더라도 다른 경우에도 쉽게 발생할 수 있는 문제라고 생각합니다. 다만, 한글로 된 자료가 별로 없는 것 같아 이렇게 글을 작성하게 되었습니다.


문제 상황

 

STL에서 제공하는 여러 자료구조 라이브러리를 보면 size 함수가 있습니다.

 

size 함수는 자료구조 객체에 포함된 원소의 개수를 리턴하는 간단한 함수입니다.

 

저 같은 경우에는 이 size 값에서 다른 숫자를 빼주면서 음수 여부를 체크하려고 했는데 코드가 이상하게 동작했습니다.

 

비슷한 다른 사례를 찾아보니 아래와 같은 코드에서 문제가 발생하는 경우가 많았습니다.

 

vector<int> v;

for (int i=0;i<v.size()-1;i++) {
    // ...
}

 

주어진 벡터의 원소 개수만큼 루프를 도는 과정인데, 개수 전체가 아니라 마지막이 제외된 상황입니다. 겉보기에는 별문제가 없어 보이는데, 만약 벡터 v의 크기가 0이라면 어떻게 될까요?

 

size가 0이므로 조건식이 음수가 되어 for 문 내용이 실행되지 않아야 합니다. 하지만, 실제 위와 같이 벡터에 내용 없이 for 문을 돌린다면 무한루프가 반복됩니다. 그 이유는 무엇일까요?

 

이해하기 쉽도록 아래와 같은 코드로 실제 값을 찍어보았습니다.

 

#include <iostream>
#include <vector>
using namespace std;

int main() {
    vector<int> v;
    cout << v.size() << endl;
    cout << v.size()-1 << endl;
    cout << 0-1 << endl;

    return 0;
}

 

확인해보니 다음과 같이 이상한 숫자가 출력되었습니다.

 

 

size가 0인 것도 잘 출력되고, 0-1은 -1로 잘 출력되는데, 왜 size-1은 4294967295라는 이상한 숫자가 나온 것일까요?

 

이를 알기 위해서는 size 함수에 관해 더 자세한 공부가 필요합니다.


size 함수 분석

 

여러 자료구조가 대부분 비슷한데, 그중 대표로 vector<int> 객체의 size 함수를 살펴보겠습니다. 다음과 같은 구조로 되어 있는데요, 여기서 중요한 것은 리턴 타입입니다.

 

 

그냥 봐서는 이 size_t가 뭔지 모르겠는데, 하나씩 찾아 올라가다 보면 이름이 몇 번 바뀌지만 최종적으로 타입을 찾을 수 있습니다.

 

#define __SIZE_TYPE__ long unsigned int

 

위와 같이 long unsigned int로 되어 있는 것을 확인할 수 있습니다. 즉, 부호가 없는 4바이트 정수입니다. (C/C++에서는 unsigned int와 같습니다.)

 

이제 앞선 4294967295라는 숫자가 조금 다르게 보일 것도 같습니다. 잘 보면 알 수 있는데, 4294967295는 2^32-1로 -1​을 unsigned int로 변환할 경우 해당하는 값입니다.

 

즉, size의 리턴값이 unsigned int이다 보니, 이를 int와 서로 연산할 때 부호 없는 쪽으로 형변환이 이뤄지면서 이런 일이 벌어진 것입니다.


해결 방법

 

이제 문제의 원인을 알았으니 해결하는 방법은 쉽습니다. 타입 캐스팅을 하여 signed로 바꿔주면 됩니다. 기본 int가 signed이므로 다음과 같이 형변환을 해주면 됩니다.

 

vector<int> v;

for (int i=0;i<(int)v.size()-1;i++) {
    // ...
}

 

모든 상황에 해당하지는 않겠지만 비교 연산의 경우 음수 연산을 하는 항의 위치를 바꿔서 아래와 같이 코드를 작성할 수도 있겠습니다.

 

vector<int> v;

for (int i=0;i+1<v.size();i++) {
    // ...
}

 

하나 의문을 가질 수 있는 점은 왜 리턴 타입을 unsigned int로 설정했는가입니다. 잘 생각해보면 아주 간단한 이유입니다. 개수가 음수가 될 수는 없으므로 signed로 설정할 필요가 없는 것입니다.


정리하자면, size 함수로 호출한 값에 음수가 될 수 있는 연산을 하게 될 경우, 꼭 강제로 타입 변환을 해줘야 합니다. 그러지 않으면 저처럼 이런 문제가 발생할 수 있으니 주의하시기 바랍니다.

 

숫자가 이상해서 당황했었는데, 아주 기초적인 실수가 있었습니다. 본인이 직접 작성한 코드가 아니라면 타입이나 파라미터 등에서 실수하기 쉬우니 항상 확인하시기 바랍니다.

 

직접 조사해서 작성하는 글이다 보니 일부 정확하지 않은 정보가 포함되어 있을 수 있습니다.

궁금한 사항이나 잘못된 내용이 있으면 댓글로 알려주세요~

구독과 좋아요, 환영합니다!

 

반응형