배열 범위 체크

@codemaru · August 01, 2007 · 4 min read

C/C++언어는 기본적으로 배열에 대해서 범위 체크를 하지 않습니다. 여러가지 이유가 있겠지만 주된 이유는 속도 때문일 겁니다. 아무래도 첨자 검사를 위해서는 추가적인 코드가 들어가야 할테니까요. 그런데 이런 속도 상의 손해를 감수하고도 꼭 반드시 배열 범위를 검사해야 하는 경우가 있습니다. 바로 외부 데이터를 읽어 들이는 과정입니다. 메모리 상에서 내부적으로 조작하는 데이터야 버그와 해킹이 아닌 이상 신뢰할 수 있는 값을 항상 제공하겠지만, 외부의 파일이나, 네트워크 스트림의 내용은 언제든지 위, 변조될 가능성이 있습니다. 오류로 잘못 읽어지는 경우도 있겠죠. 따라서 이러한 곳에는 반드시 범위 검사를 넣어야 합니다. 그래야 안전하죠.

배열이 한, 두개인 경우야 수동으로 범위검사를 할 수 있겠지만, 세 개만 넘어가도 더 이상 수동으로 하기 힘들어 집니다. 또한 오류 발생 가능성도 높죠. 이러한 경우에 사용할 수 있는 간단한 템플릿 클래스가 아래 나와있습니다. 이 클래스는 배열과 호환되는 클래스로 내부적으로 크기 정보를 가지고 범위 검사를 수행합니다. 범위를 벗어나면 EOutOfRange 예외를 던집니다. T에는 배열을 저장할 타입을, size에는 해당 배열 크기를 집어넣으면 됩니다.

template <typename T, size\_t size>  
class CFixedArray  
{  
private:  
    T m\_data[size];  
    const size\_t m\_size;  
  
    CFixedArray(const CFixedArray &);  
    CFixedArray &operator =(const CFixedArray &);  
  
public:  
    CFixedArray() : m\_size(size) {}  
  
    T &operator [](int index)  
    {  
        if(index < 0 || index > size)  
            throw EOutOfRange(\_T("CFixedArray: 범위 초과"));  
  
        return m\_data[index];  
    }  
  
    operator T \*() { return m\_data; }  
    size\_t Size() const { return m\_size; }  
};
```아래는 예외 클래스와 CFixedArray를 사용하는 샘플 코드 입니다.   
```cpp
class Exception  
{  
private:  
    const TCHAR \*m\_msg;  
  
public:  
    Exception(const TCHAR \*msg) : m\_msg(msg) {}  
    Exception(const Exception &r) { m\_msg = r.Msg(); }  
  
    Exception &operator =(const Exception &r)  
    {  
        if(this != &r)  
            m\_msg = r.Msg();  
  
        return \*this;  
    }  
  
    const TCHAR \*Msg() const { return m\_msg; }  
};  
  
class EOutOfRange : public Exception  
{  
public:  
    EOutOfRange(const TCHAR \*const msg) : Exception(msg) {}  
};  
  
int \_tmain(int argc, \_TCHAR\* argv[])  
{  
    try  
    {  
        CFixedArray<CFixedArray<int, 30>, 30> a;  
        CFixedArray<CFixedArray<int, 30>, 30> b;  
  
        a[3][5] = 4;  
        printf("%d\n", a[3][5]);  
    }  
    catch(Exception &ex)  
    {  
        printf("%S\n", ex.Msg());  
    }  
  
    return 0;  
}
```요즘 프로그램에서 기초적인 형태의 배열을 사용하는 경우는 거의 없습니다. 대부분 벡터나 잘 포장된 형태의 가변 길이 배열을 지원하는 켄테이너 클래스를 사용하죠. 프로그램의 버그를 줄이고 싶다면 이러한 형태의 컨테이너 클래스를 사용하는 것이 좋은 습관입니다. 결론은 별로 그닥 유용한 코드는 아니라는 것이죠. ㅋㅋㅋㅋ  
  
  

@codemaru
돌아보니 좋은 날도 있었고, 나쁜 날도 있었다. 그런 나의 모든 소소한 일상과 배움을 기록한다. 여기에 기록된 모든 내용은 한 개인의 관점이고 의견이다. 내가 속한 조직과는 1도 상관이 없다.
(C) 2001 YoungJin Shin, 0일째 운영 중