메모리 할당을 사용자에게 맡기는게 좋을까요 내부에서 처리하는게 좋을까요

gurugio의 이미지

A라는 데이터 구조가 있고, 사용자에게 A 데이터를 제공하는 API를 만들어야 합니다.
꼭 API가 되야하는 것은 아니지만 두고두고 유연하게 사용하고 싶습니다.
두가지 방법이 있을것 같습니다.

1) 사용자가 A 데이터 구조를 선언하고, API에는 초기화 함수만 제공한다.
대강 이렇게 사용되겠지요.
struct A a;
init(&a);

이렇게 설계하면 경우에 따라 사용자가 직접 메모리 관리를 해야합니다.

struct A *a;
a = malloc(sizeof(struct A));
init(a);

2) 사용자는 포인터를 선언하고 API에서 생성해주는 데이터를 받아서 쓴다.
데이터를 얻는 것과 해지하는 한쌍의 API가 필요할것 같습니다.
struct A *a;
a = get();
put(a);

예전에는 1번으로 많이 구현했고, 다른 소스나 라이브러리들도 1번 형태가 많았던것 같은데
요즘들어 getaddrinfo/freeaddrinfo처럼 2번 형태가 늘어나는것 같습니다.

어떤게 더 유연해질 수 있을까요?

아니면 어떤 경우에 1번이 어떤 경우에 2번이 효율적일까요?

SoulreaveR의 이미지

아무래도 내부가 좋지 않을까요? 사용자가 처리한다는 의미는 libc를 고친다거나 소스를 들어엎지 않는 이상 문제가 되는 부분을 찾거나 함수를 다르게 바꾸거나 하는게 힘들지만, 2번의 경우에는 굳이 외부의 것을 사용하지 않는다고 쳐도 내부적으로 함수를 만들어서 쓴다던지 해서 나중에 바꾸기 편하지 않을까 생각되네요. 이를테면 위에서 예로 든 get(), put()을 처음에는 그냥 내부적으로 malloc()으로만 해 놓은 다음에 문제가 생기거나 좋은 생각이 나면 다른 알고리즘을 적용한다던지, 그렇게 바꾸는 경우가 발생하지 않을까 합니다. 뭐 물론 왠만한 libc 같은 경우에야 함수들이 weak라서 지정한 다른 함수 쓴다고 별 문제될 케이스는 안생기겠지만, 그래도 개발하는 분들 입장에서 확실히 malloc()같이 자신이 '직접' 뭔가 관리해야 된다고 생각하는 것과 그 외에 지정한 어떤 함수를 써야 하는 것은 차이가 나지 않을까 싶습니다.

chadr의 이미지

두가지 다 제공하는게 좋을것 같습니다. 간편한 목적으로 사용할때는 메모리 관리에 신경 안쓰고 쓰고 싶겠지만
내부적으로 메모리 풀등을 만들어서 쓸 경우에는 라이브러리의 메모리 할당 하나하나 컨트롤하고 싶으니까요.
-------------------------------------------------------------------------------
It's better to appear stupid and ask question than to be silent and remain stupid.

-------------------------------------------------------------------------------
It's better to appear stupid and ask question than to be silent and remain stupid.

mirheekl의 이미지


꼭 둘 중 하나를 택해야 한다면 전자가 그나마 낫지 않을까 합니다.

후자는 직접 메모리 최적화를 하고싶을때 방법이 없으니까요. 언젠가 분명 사용자로부터 관련사항에 대한 요청을 받을겁니다.

--
This is for you new people. I have just one rule :
Everyone fights, no one quits. If you don't do your job, I'll shoot you myself. Do you get me?

--

yielding의 이미지

저도 전자를 추천합니다만 더 중요한 것은 일관된 인터페이스를 유지하는 것입니다.

프로그램이 커지면 두 가지 경우가 동시에 나타날 수 밖에 없을텐데 다른 사람 뿐만 아니라 나자신을 위해서라도 일관성을 가지고 인터페이스를 만들어놓아야만 유지보수/디버깅이 쉬울겁니다.

Life rushes on, we are distracted

Life rushes on, we are distracted

bacon의 이미지

어느게 더 유용한지는 상황에 따라 달라지므로 저는 둘 다 제공 합니다.

typedef struct foo_t foo_t가 있다면

foo_t* foo_open (arguments...);
void foo_close (foo_t*);
 
foo_t* foo_init (foo_t*, arguments...);
void foo_fini (foo_t*);

foo_open에서는 메모리 할당하고, 초기화를 위해 foo_init을 호출합니다. 사용자의 상황에 따라 foo_open이나 foo_init을 선택하여 사용할수 있도록 합니다.

foo_t가 다른 struct에 포함되어야 한다면

struct xxx_t
{
   foo_t foo;
   ...
}
 
xxx_t* xxx_open ()
{
    xxx_t* xxx = malloc (sizeof(xxx_t));
    if (xxx == null) return null;
    ...
    if (xxx_init (xxx) == null) { free(xxx); return null; }
    ...
    return xxx;
}
xxx_t* xxx_init (xxx_t* xxx)
{
    ....
    foo_init (&xxx->foo);
    ....
    return xxx;
}

대충 이런식으로 합니다.