본문 바로가기

Programming/Linux_Kernel

_alloca 와 smart point

얼마전 stack 에 동적 메모리를 할당하는 _alloca 라는 함수를 들었다.
힙을 스택처럼 사용하는 smart point 와 스택을 힙처럼 사용하는 _alloca 중 어떤것이 더 효율적일까?

< 요약 >
_alloca 는 스택에 공간을 미리 할당해서 힙처럼 사용할 수 있도록 해준다.
smart point 는 힙을 사용하되 스택풀기에 들어가면 할당한 memory 를 자동으로 해제시켜 주는 코드를 포함하도록 힙포인터를 수정한것이다.

자세한 내용은 아래의 포스팅을 참고..


< 결론부터 말하자면,... >
_alloca 는 smart point 에 대해 비교적 가볍지만, 사용상 주의할 점이 존재한다.
이 주의할 점이 좀 애매한 편이여서 편하게 코딩하다가는 자칫 알수없는 오류를 만들어 내기 쉬운 부분이 있다.

smart point 는 매우 유용하고 사용자의 입맛대로 수정할수도 있다.(동작하는 두개의 스래드에서 접근하는 힙 메모리에 대한 smart point도 있다.) 하지만, 코드가 _alloca 에 비해 느린편이고, OOP에서는 매우 구현이 용이하지만, C에서는 복잡해 진다는 단점이 있다.(어디에선가 C로 구현한 smart point가 있다는 이야기를 들은적이 있지만, 찾아본적은 없다.)


먼저 _alloca() 소개
------------------------------------------------------------------------------------------
원문 : http://www.debuglab.com/knowledge/stackdynamic.html
------------------------------------------------------------------------------------------

스택에 동적으로 메모리 할당하기


1.요약

스택에 동적으로 메모리 할당하기란 이렇습니다.
동적이라는 것은 new, malloc 처럼 할당하는 크기를 동적으로 대입할 수 있다는 의미이고,
스택에.. 라는 것은 배열처럼 스택에 자리잡기 때문에 따로 delete, free 해줄 필요가 없다는 의미입니다.


2.본문

_alloca 라는 CRT 함수가 바로 그 일을 해줍니다.
사용법은 예제에 나오는 것 처럼 평범합니다.

그러므로, 여기서는 _alloca의 간단한 구현원리와 의점을 알아보도록 하죠.

일단 _alloca는 함수가 아닙니다 - 함수라면 stack에 공간을 확보할 수가 없다는 점은 당연하겠죠?

그냥 매크로 정도로 생각하시면 됩니다.(매크로도 아닙니다)

_alloca 가 해주는 일은 단순히 stack pointer를 필요한 만큼 감소시켜서 공간을 확보하는 일입니다. 그 확보된 공간의 시작주소(stack pointer의 값이겠죠?)를 반환하는게 전부입니다.

---------

_alloca는 USES_CONVESION; 하면 생각나는 그 문자열 컨버전 매크로들이 사용하고 있는 그것입니다.(A2W, OLE2T, ...)

그렇게 때문에 다음과 같은 점을 주의해야 하겠습니다.

1. 매크로를 사용해 변환한 문자열을 함수 밖으로 내보내서는 안된다( return, 이나 포인터 타입의 인자를 통해서)
2. loop 안에서 매크로를 사용하게되면 계속해서 스택 포인터를 증가시키므로 스택 오버플로우가 발생할 염려가 있습니다.

그리고 스택에 밀접한 연관을 가지고 있는 구조적인 예외 처리를 사용하실 때도 다음과 점을 주의해야 하겠습니다.

1. __except(_alloca()) 처럼 예외 필터에서 사용 금지
2. __finally(_alloca()) 처럼 종료 처리기에서 사용 금지
3. C++의 catch 에서 사용 금지


3.예제


#include <malloc.h> 



int main(int argc, char* argv[]) 

{ 

    int n = 30; 

    char* p = (char*)_alloca(n); 

    

    // p는 해제할 필요가 없습니다. 

    return 0; 

} 



- 2001.08.19 Smile Seo -

--------------------------------------------------------------------------------

이거 인터넷에서 찾고 보니 우석이형이 2001년에 써놓은 글이다.

언제까지 그형 발자취만 쫒아가는 느낌일까.. - -;;

다음은 smart point
---------------------------------------------------------------------------------
원문 : http://blog.naver.com/junglehare?Redirect=Log&logNo=10000336434
---------------------------------------------------------------------------------
메모리 관리 관점에서 c와 c++프로그래머의 에로사항중 하나는 malloc및 new로 할당한 메모리를 free, delete로 꼭 해제해야 한다는 것이다. 한 함수 내에서나 규모가 작은 프로그램이라면 상관없지만 할당자가 따로 있고 사용후 꼭 해제하는 프로그램을 작성하다 보면 곳곳에 해제하는 함수가 넘쳐나기 마련이다.

 스마트 포인터(smart pointer)는 포인터 자체가 파괴될때 가지고 있는 리소스를 항상 해제할수 있어 이러한 경우에 유용할 수 있다. 더구나 로컬변수로 작성된 경우라면 함수가 정상적으로 종료되든 예외로 인하여 종료되든 함수가 종료될 때 포인터가 자동으로 소멸되게 되어 수월한 메모리 관리가 이루어지게 된다. c++에서 auto_ptr을 지원하나 나름대로 구현해 사용하는 것도 좋은 방법이다.

예)

countptr.hpp


#ifndef COUNTED_PTR_HPP
#define COUNTED_PTR_HPP

template <class T>
class CountedPtr
{
        private:
                T* ptr;
                long* count;

        public:
        explicit CountedPtr(T* p=0):ptr(p), count(new long(1)){
        }

        CountedPtr(const CountedPtr<T>& p) throw():ptr(p.ptr), count(p.count)
        {
                ++*count;
        }

        ~CountedPtr() throw(){
                dispose();
        }

        CountedPtr<T>&operator=(const CountedPtr<T>& p) throw()
        {
                if(this != &p){
                        dispose();
                        ptr = p.ptr;
                        count = p.count;
                        ++*count;
                }
                return *this;
        }

        T& operator*() const throw(){
                return *ptr;
        }

        T* operator->() const throw(){
                return ptr;
        }

        private:
        void dispose()
        {
                if(--*count == 0){
                        delete count;
                        delete ptr;
                }
        }
};

#endif

 

 

countptr.cpp

 

#include <iostream>
#include <list>
#include <deque>
#include <algorithm>
#include <iterator>

#include "countptr.hpp"

using namespace std;

void printCountedPtr(CountedPtr<int> elem)
{
        cout<< *elem <<' ';
}

int main()
{
        static int values[]={3,5,9,1,6,4};

        typedef CountedPtr<int> IntPtr;
        deque<IntPtr>coll1;
        list<IntPtr>coll2;

        for(int i=0; i<sizeof(values)/sizeof(values[0]); ++i)
        {
                IntPtr ptr(new int(values[i]));
                coll1.push_back(ptr);
                coll2.push_front(ptr);
        }

        for_each(coll1.begin(), coll1.end(), printCountedPtr);
        cout<<endl;

        for_each(coll2.begin(), coll2.end(), printCountedPtr);
        cout<<endl<<endl;

        *coll1[2] *= *coll1[2];
        (**coll1.begin()) *= -1;
        (**coll2.begin()) = 0;


        for_each(coll1.begin(), coll1.end(), printCountedPtr);
        cout<<endl;

        for_each(coll2.begin(), coll2.end(), printCountedPtr);
        cout<<endl<<endl;
}

실행결과)

3 5 9 1 6 4
4 6 1 9 5 3

 

-3 5 81 1 6 0
0 6 1 81 5 -3

 

여기서 주목할 점은, coll1이 가진 모든 원소를 제거하더라도 coll2의 모든 원소들은 유효하다는 것이다. 단지 레퍼런스 카운터만 감소할 뿐이다.

 

아울러 이 예제는 auto_ptr과는 달리 한 포인터 값에 대해 둘 이상의 소유자가 존재하는 것을 허용해 주며, 포인터를 참조하던 마지막 객체가 파괴되는 순간에만 이 할당된 포인터를 해제하도록 해준다.

 

참조:C++ Standard Library 튜토리얼-레퍼런스--Nicolai M. Josuttis


---------------------------------------------------------------------------------