2010-05-12 3 views
11

저는 몇몇 프로젝트에서 석사 학위를 받았지만 프로덕션 소프트웨어를 결코 만들지 않았습니다. (NET & 자바는 제 빵과 버터입니다.) 분명히 free() 메모리가 필요합니다. malloc()은 C에서 매우 중요합니다. 하나의 루틴에서 두 가지 작업을 모두 수행 할 수 있다면 좋을 것입니다. 그러나 프로그램이 성장하고 구조가 심화되면서 무엇이 되었는가를 추적합니다. malloc 어디에 자유롭고 적절한 것이 더 힘들어지고 힘들어집니다.malloc/무료 짐승 길들이기 - 팁 & 트릭

나는 interwebs를 둘러 보았고 이에 대한 몇 가지 일반적인 권장 사항 만 발견했습니다. 제가 생각하기에 오랜 C 코더 중 일부는이 프로세스를 단순화하고 악의를 앞에두고 자신 만의 패턴과 관행을 생각해 냈습니다.

그래서 어떻게 동적 할당이 메모리 누수가되지 않도록 C 프로그램을 구성하는 것이 좋습니다?

+4

이 오랜 시간 C 코더는 C++로 전환되어 문제가 해결되었습니다. –

+1

@Neil Butterworth : 문제가 훨씬 악화되었습니다. – sharptooth

+1

@sharptooth : C++에 대한 팬이 아니지만, 전체 메모리 관리 문제를 해결해줍니다. 물론 어려운 것들을 떠난다. :-) – JesperE

답변

8

계약으로 디자인. 모든 함수 주석이 메모리 위생에 대해 명시 적인지 확인하십시오. 즉, malloc과 할당 된 것을 해제하는 책임, 전달 된 항목의 소유권을 가져 오는 지의 여부입니다. 그리고 함수와 일관성을 유지하십시오.

예를 들어, 헤더 파일 같은 것을 포함 할 수 있습니다 : 나는 진심으로 Valgrind의를 사용하도록 조언을 보증

/* Sets up a new FooBar context with the given frobnication level. 
* The new context will be allocated and stored in *rv; 
* call destroy_foobar to clean it up. 
* Returns 0 for success, or a negative errno value if something went wrong. */ 
int create_foobar(struct foobar** rv, int frobnication_level); 

/* Tidies up and tears down a FooBar context. ctx will be zeroed and freed. */ 
void destroy_foobar(struct foobar* ctx); 

을, 그것은 메모리 누수를 추적하는 정말 훌륭한 도구가 유효하지 않은 메모리에 액세스합니다. Linux에서 실행하지 않는 경우 Electric Fence은 기능이 적지 만 비슷한 도구입니다.

+0

나는 이것을 좋아한다. 나는 그것이 많은 상황에서 잘 작동하는 것을 볼 수있다. – roufamatic

+2

destroy_foobar (struct foobar ** ctx)를 만들면 원본에서 포인터 값을 NULL로 만들 수 있습니다. 또한 호출간에 대칭을 제공합니다. struct foobar * myFoobar; create_foobar (& myFoobar, 0); do_stuff(); destroy_foobar (& myFoobar); 두 통화가 비슷하게 보입니다. – jmucchiello

+0

참고로이 접근법 + jmucchiello의 대칭 destrcutor를 사용하고 valgrind를 사용하여 내 작업을 확인했습니다. 아직 갈 길이가 조금 있지만이 방법을 사용하여 정밀도가 2 MB로 종료시 내 메모리 공간을 줄일 수 있습니다. 또는 아이들이 말하면서, w00t! – roufamatic

3

Valgrind은 내 기억 관리를 건강하게 유지하는 데 엄청난 도움이 될 것으로 나타났습니다. 그것은 할당되지 않은 메모리와 메모리 할당을 잊어 버리는 곳 (그리고 많은 것들)에 접근하는 곳을 알려줍니다.

C에서 메모리 관리를 수행하는 상위 수준 방법도 있습니다 (예 : 내 메모리 풀 사용) (예 : Apache APR 참조).

+1

+1 : Valgrind는 매우 유용합니다. – tur1ng

4

확실하지는 않지만 (아마도 C로 예상 됨) 많은 기존 코드로는 어려울 수 있지만 명확하게 코드를 문서화하고 할당 된 사람을 정확히 명시하면 도움이됩니다. 메모리 및 그것을 해제 할 책임이있는 사람 (그리고 할당 자/할당 취소자가 무엇인지). 또한, goto을 사용하여 리소스를 할당하지 않는 기능에 대해 단일 입력/단일 종료 관용구를 시행하는 것을 두려워하지 마십시오.

5

대용량 프로젝트는 종종 "풀"기술을 사용합니다.이 경우 각 할당은 풀과 연결되며 풀이있을 때 자동으로 해제됩니다. 하나의 임시 풀을 사용하여 복잡한 처리를 수행 할 수 있다면 편리합니다. 완료되면 임시 풀을 해제 할 수 있습니다. 일반적으로 서브 풀이 가능합니다. 다음과 같은 패턴을 자주 보게됩니다.

void process_all_items(void *items, int num_items, pool *p) 
{ 
    pool *sp = allocate_subpool(p); 
    int i; 

    for (i = 0; i < num_items; i++) 
    { 
     // perform lots of work using sp 

     clear_pool(sp); /* Clear the subpool for each iteration */ 
    } 
} 

문자열 조작으로 인해 작업이 훨씬 쉬워졌습니다. 문자열 함수는 반환 값이 될 반환 인수 값을 할당하는 풀 인수를 취합니다.

단점은 : 당신이 취소 또는 해제 할 풀을 기다릴 필요가 있기 때문에 객체의

  • 할당 된 수명이 조금 더 길어질 수 있습니다.
  • 당신은 여분의 풀 인수를 함수에 전달하게됩니다. (필요한 할당을하기위한 어딘가).
+0

+1, 이것은 스스로 배우기 힘든 일종입니다! – roufamatic

2

각 유형에 대한 요약 할당 자와 할당 취소 자. 타입 정의

typedef struct foo 
{ 
    int x; 
    double y; 
    char *z; 
} Foo; 

을 감안하면 할당 기능

Foo *createFoo(int x, double y, char *z) 
{ 
    Foo *newFoo = NULL; 
    char *zcpy = copyStr(z); 

    if (zcpy) 
    { 
    newFoo = malloc(sizeof *newFoo); 
    if (newFoo) 
    { 
     newFoo->x = x; 
     newFoo->y = y; 
     newFoo->z = zcpy; 
    } 
    } 
    return newFoo; 
} 

복사 기능

Foo *copyFoo(Foo f) 
{ 
    Foo *newFoo = createFoo(f.x, f.y, f.z); 
    return newFoo; 
} 

과 역할 당기 기능을 차례로 createFoo()가를 부르는

void destroyFoo(Foo **f) 
{ 
    deleteStr(&((*f)->z)); 
    free(*f); 
    *f = NULL; 
} 

주를 만들 copyStr() 함수는 문자열을 저장하고 문자열의 내용을 복사하는 역할을합니다. copyStr()이 실패하고 NULL을 반환하면 newFoo은 메모리 할당을 시도하지 않고 NULL을 반환합니다. 마찬가지로 destroyFoo()은 구조체의 나머지 부분을 해제하기 전에 z의 메모리를 삭제하는 함수를 호출합니다. 마지막으로 destroyFoo()은 f의 값을 NULL로 설정합니다.

핵심 요소는 멤버 요소에도 메모리 관리가 필요한 경우 할당 자 및 할당 취소자가 책임을 다른 함수에 위임한다는 것입니다. 당신의 유형은 더 복잡 그래서, 당신과 같이 그 할당자를 다시 사용할 수 있습니다 :

typedef struct bar 
{ 
    Foo *f; 
    Bletch *b; 
} Bar; 

Bar *createBar(Foo f, Bletch b) 
{ 
    Bar *newBar = NULL; 
    Foo *fcpy = copyFoo(f); 
    Bletch *bcpy = copyBar(b); 

    if (fcpy && bcpy) 
    { 
    newBar = malloc(sizeof *newBar); 
    if (newBar) 
    { 
     newBar->f = fcpy; 
     newBar->b = bcpy; 
    } 
    } 
    else 
    { 
    free(fcpy); 
    free(bcpy); 
    } 

    return newBar; 
} 

Bar *copyBar(Bar b) 
{ 
    Bar *newBar = createBar(b.f, b.b); 
    return newBar; 
} 

void destroyBar(Bar **b) 
{ 
    destroyFoo(&((*b)->f)); 
    destroyBletch(&((*b)->b)); 
    free(*b); 
    *b = NULL; 
} 

물론,이 예제는 회원들이 용기 외부의 수명이없는 것으로 가정합니다. 그렇다고 항상 그런 것은 아니므로 적절하게 인터페이스를 설계해야합니다. 그러나 이것은 당신에게해야 할 일의 맛을 주어야합니다.

이렇게하면 메모리 관리에서 전투의 80 % 인 일관되고 잘 정의 된 순서로 개체 메모리를 할당 및 할당 취소 할 수 있습니다. 나머지 20 %는 모든 할당 자 호출이 해제 코드에 의해 균형을 이루고 있는지 확인합니다. 내가 올바른 유형을 전달하고 있습니다 있도록

편집

delete* 함수 호출을 변경.

관련 문제