반응형
반응형
반응형
Buffer Manipulation을 지원하는 다양한 함수가 있고, 이에 대한 사용법은 정확히 알고 있으면 매우 편리하다. 지난시간에 설명한 memset에 대한 추가적인 설명으로 오늘 마무리 하고자 한다.

배열, struct, class를 초기화할 때 memset을 이용하면 한 번에 초기화 할 수 있음을 지난시간에 배웠다. 이번시간에는 memset을 사용할 때 매우 주의해야할 점에 대해서 알아보자.

1.memset을 사용할 때 주의할 점
- 1Bytes 변수(char, unsigned char 등)를 제외한 변수를 초기화 할 때에는 0이외의 값으로 초기화를 절대 하지마라.
- new, malloc 등을 이용하여 동적으로 배열을 생성하는 변수가 있는 struct, class에서는 초기화할 때 조심해라.
- CString은 절대 memset으로 초기화를 하지마라.
- virtual function을 가지고 있는 struct, class에서는 절대 memset으로 초기화를 하지마라.

memset을 사용할 때 위 4가지 경우만 기억을 하고 있으면 문제없다. 반드시 기억하고 있어야 한다. 각각에 대해서 간단하게 살펴보도록 하자.

1.1 1Bytes 변수(char, unsigned char 등)를 제외한 변수를 초기화 할 때에는 0이외의 값으로 초기화를 절대 하지마라.

이는 지난 시간에 설명을 한 부분이다. 다시 살펴보면,
int n;
memset(&n, 1, sizeof(int));
으로 하면 Byte단위로 처리가 되어 n = [00000001000000010000000100000001] = 16843009의 원하지 않는 값으로 초기화가 된다는 것을 명심해두자. 1Byte의 변수를 제외하고는 0으로만 초기화를 하는데 이용하자.

1.2 new, malloc 등을 이용하여 동적으로 배열을 생성하는 변수가 있는 struct, class에서는 초기화할 때 조심해라.

문제가 되는 경우를 살펴보면,
struct A {
   int i;
   char* c;
};

void main()
{
   A a;
   a.c = new char[100];
   memset(&a, 0, sizeof(A));
   if(a.c != NULL) {
      delete[] a.c;
      a.c = NULL;
   }
}

여기서 sizeof(A)는 struct member alignment가 어떤 값이든 4(int i) + 4(char* c, address는 4) = 8Bytes가 된다. 그러므로 위의 소스는 동적으로 생성한 변수는 초기화가 되지 못하고, char* c가 NULL로 초기화가 됨으로써, 이전에 생성한 메모리는 메모리 누수가 발생하게 된다.

그러므로 위와 같이 동적으로 생성하는 경우 각각을 분리하여 초기화를 하여야겠다.
a.i = 0;
memset(a.c, 0, sizeof(char)*100);

1.3 CString은 절대 memset으로 초기화를 하지마라.

1.2와 같은 경우로 CString은 내부적으로 m_pchData 변수를 동적으로 생성하여 문자열을 저장한다. 이 변수에 대한 직접적인 접근은 private로 막혀있다. 그래서 CString, 또는 CString을 member variable을 가지고 있는 struct, class를 memset을 이용하여 초기화를 하지마라.

CString을 memset으로 초기화를 하면, 1.2와 같이 메모리 누수뿐만 아니라, run-time error도 발생을 한다. 나를 포함해서 이것 때문에 고생을 한 사람이 꽤 많다는 것을 기억해두자.

1.4 virtual function을 가지고 있는 struct, class에서는 절대 memset으로 초기화를 하지마라.

여기서 virtual은 run-time에 실행함수를 binding하는 역할을 하는 것이다.
한번 잊어버렸던 기억을 되살리는 의미로 예제를 살펴보면,

class A {
public:
   void fun() { printf("A::fun() "); }
};

class B: public A {
public:
   void fun() { printf("B::fun() "); }
};

void main()
{
   A* a = new B();
   a->fun();
}

위의 경우 “A::fun()”이 출력된다.
하지만 상속을 하여 재정의를 한다는 목적은 재정의를 한 함수가 호출되기를 바라기 때문일 것이다. 이때 아래와 같이 class A만 간단히 변경하여 virtual만 추가를 하면,
class A {
public:
   virtual void fun() { printf("A::fun() "); }
};

재정의한 함수가 실행이 되어 “B::fun()”이 출력이 된다. 이는 a->fun();가 실행이 될 때, 이 함수가 virtual이므로 a의 실제 instance(=new B)에 대응하는 실제 함수를 run-time으로 binding 되기 때문이다.

이정도로만 virtual 동작에 대해서 기억을 되살려보는 것으로 마무리를 하고, 다시 memset으로 넘어오면,

class A {
   int i;
   char* c;
   void fun();
};
을 sizeof(A)를 하면 4(int i)+4(char*c, address)=8Bytes로 member function은 영향을 주지 않는다. 하지만,
class A {
   int i;
   char* c;
   virtual void fun();
};
의 경우는 다르다. 위의 8Bytes외에 실제 fun()이 binding을 위한 실행함수 주소를 저장할 공간 4Bytes를 추가적으로 가지므로, sizeof(A)는 총 12Bytes가 된다.

이때
class A {
public:
   void fun() { printf("A::fun() "); }
};

class B: public A {
public:
   void fun() { printf("B::fun() "); }
};

void main()
{
   A* a = new B();
   memset(a, 0, sizeof(B));
   a->fun();
}
와 같이 memset을 이용하여 초기화를 하면, virtual function이 NULL영역으로 binding이 되어, a->fun();에서 run-time에 알 수 없는 에러가 발생한다는 것을 반드시 기억해두자.

memset은 매우 편리하고, 강력하면서도, 조심해서 사용해야 한다는 것을 기억해두자.
반응형

'프로그래밍 > C 언어' 카테고리의 다른 글

c++ 에서 new, delete 메모리 할당 해제 관련글  (0) 2009.05.07
debuging 하기. (아파치에서)  (0) 2009.02.11
c언어 함수 요약  (0) 2009.02.11
날짜 구하기  (0) 2009.02.11

+ Recent posts