source

C를 위한 스마트 포인터/안전한 메모리 관리

gigabyte 2022. 8. 19. 20:56
반응형

C를 위한 스마트 포인터/안전한 메모리 관리

저나 다른 많은 사람들은 스마트 포인터를 사용하여 안전하지 않은 메모리 작업을 C++로 정리하고 RAII 등을 사용하여 큰 성공을 거뒀다고 생각합니다.단, 랩 메모리 관리는 디스트럭터, 클래스, 연산자 오버로드 등이 있는 경우 구현이 용이합니다.

raw C99로 글을 쓰는 사람에게 안전한 메모리 관리에 도움이 되는 (말장난 의도가 없는) 장소를 알려주실 수 있습니까?

고마워요.

질문이 좀 오래되었지만 GNU 컴파일러용 스마트 포인터 라이브러리(GCC, Clang, ICC, MinGW 등)에 링크하는 데 시간을 할애해야겠다고 생각했습니다.

이 실장은 범위를 벗어나면 메모리를 자동으로 해방시키기 위해 Cleanup variable 속성인 GNU 확장에 의존합니다.따라서 ISO C99가 아니라 GNU 확장이 있는 C99입니다.

예:

심플1.c:

#include <stdio.h>
#include <csptr/smart_ptr.h>

int main(void) {
    smart int *some_int = unique_ptr(int, 1);

    printf("%p = %d\n", some_int, *some_int);

    // some_int is destroyed here
    return 0;
}

컴파일 및 밸린더 세션:

$ gcc -std=gnu99 -o simple1 simple1.c -lcsptr
$ valgrind ./simple1
==3407== Memcheck, a memory error detector
==3407== Copyright (C) 2002-2013, and GNU GPL\'d, by Julian Seward et al.
==3407== Using Valgrind-3.10.0 and LibVEX; rerun with -h for copyright info
==3407== Command: ./simple1 
==3407==
0x53db068 = 1
==3407==
==3407== HEAP SUMMARY:
==3407==     in use at exit: 0 bytes in 0 blocks
==3407==   total heap usage: 1 allocs, 1 frees, 48 bytes allocated
==3407==
==3407== All heap blocks were freed -- no leaks are possible
==3407==
==3407== For counts of detected and suppressed errors, rerun with: -v
==3407== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)

자, 여기 선택사항이 있습니다.이상적으로는 더 나은 결과를 얻기 위해 이들을 결합하는 것입니다.C의 경우 편집증도 괜찮습니다.

컴파일 시간:

  1. GCC에서 cleanup variable Atribute를 사용합니다.그 이후에는 GCC를 고수해야 합니다.GCC가 존재하는 플랫폼만 대상으로 할 수 있기 때문에 코드 이식성이 제한됩니다.
  2. Windows 에서는 SEH(구조화된 예외 처리)를 사용합니다.Microsoft 컴파일러를 사용해야 하므로 휴대성이 더욱 제한됩니다.타겟이 Windows 전용인 경우는, 유효합니다.
  3. 메모리 누수 가능성을 나타내는 정적 코드 분석 도구를 사용합니다.완벽하게 작동하지는 않지만 사소한 누출을 찾는데 도움이 될 수 있습니다.휴대성에는 영향을 주지 않습니다.

실행 시:

  1. malloc/free를 자체 구현으로 대체하고 메모리 사용량을 추적하는 디버깅메모리 할당 라이브러리를 사용합니다.이렇게 하면 할당되었지만 해제되지 않은 블록을 볼 수 있습니다.Solaris 용으로 성공했습니다(이름을 기억해 보겠습니다).
  2. 가비지 콜렉터를 사용합니다.소스 코드가 없는 누출이 심한 C 어플리케이션을 수정하면서 Hans-Boehm GC에서 좋은 경험을 했습니다.메모리 소비량이 얼마나 증가하는지 알 수 있었는데 GC가 작동하자 메모리 소비량이 급감했습니다.

C에서는 필요한 구문을 제공하지 않기 때문에 스마트포인터를 실행할 수 없지만 실천을 통해 누수를 방지할 수 있습니다.리소스 릴리스 코드를 할당한 직후에 작성합니다. 이 글을 쓸 malloc 그럼 여기에 하는 것을 합니다.free바로 청소 구역에서요

C에서는 'GOTO cleanup' 패턴이 많이 보입니다.

int foo()
{
    int *resource = malloc(1000);
    int retVal = 0;
    //...
    if (time_to_exit())
    {
        retVal = 123;
        goto cleanup;
    }
cleanup:
    free(resource);
    return retVal;
}

C에서는, 많은 콘텍스트를 사용하고, 그 콘텍스트에도 같은 룰을 적용할 수 있습니다.

int initializeStuff(Stuff *stuff)
{
    stuff->resource = malloc(sizeof(Resource));
    if (!stuff->resource) 
    {
        return -1; ///< Fail.
    }
    return 0; ///< Success.
}

void cleanupStuff(Stuff *stuff)
{
    free(stuff->resource);
}

이는 객체 생성자 및 소멸자와 유사합니다.할당된 리소스를 다른 개체에 할당하지 않는 한 해당 리소스는 유출되지 않으며 포인터도 중단되지 않습니다.

블록을 않습니다.atexit.

할당된 리소스에 대한 포인터가 필요한 경우 해당 리소스에 대한 래퍼 컨텍스트를 생성할 수 있으며 각 개체는 리소스 대신 래퍼 컨텍스트를 소유합니다.이러한 래퍼는 리소스와 카운터 개체를 공유합니다.이러한 개체는 사용량을 추적하여 아무도 사용하지 않을 때 개체를 해방합니다.의 C++11은 이렇게 .shared_ptr ★★★★★★★★★★★★★★★★★」weak_ptr효과가 있습니다. 자세한 내용은 여기에 기재되어 있습니다.weak_ptr의 구조

raw C에서는 사용을 백업하는 언어 구문이 없기 때문에 스마트 포인터를 처리하기가 어렵습니다.지금까지 본 대부분의 시도는 효과가 없습니다.왜냐하면 오브젝트가 스코프를 벗어날 때 디스트럭터가 실행된다는 장점이 없기 때문입니다.그것이 스마트 포인터를 유효하게 하는 것입니다.

이 문제가 정말로 우려되는 경우 가비지 컬렉터를 직접 사용하여 스마트 포인터 요구 사항을 모두 우회하는 것을 고려해 볼 수 있습니다.

Apache에서 사용하는 풀링 메모리 접근 방식도 고려할 수 있습니다.이는 요청 또는 기타 단시간 개체와 관련된 동적 메모리 사용량이 있는 경우 특히 잘 작동합니다.요청 구조에 풀을 생성하여 항상 풀에서 메모리를 할당하고 요청 처리가 완료되면 풀을 해방할 수 있습니다.조금 사용해보니 파워가 떨어지는 것 같아요.그것은 거의 RAII만큼 좋다.

BEGIN 및 END 등의 매크로를 정의하여 괄호 대신 사용할 수 있으며 해당 범위를 벗어나는 리소스의 자동 파괴를 트리거할 수 있습니다.이를 위해서는 이러한 모든 리소스가 오브젝트의 소멸자에 대한 포인터를 포함하는 스마트 포인터에 의해 지시되어야 합니다.실장에서는 스마트포인터 스택을 힙메모리에 보관하고 스코프 엔트리 스택포인터를 기억하고 스코프 출구 시 기억된 스택포인터 위에 있는 모든 자원의 디스트럭터를 호출합니다(반환용 END 또는 매크로 치환).이는 setjmp/longjmp 예외 메커니즘을 사용하는 경우에도 정상적으로 동작하며 catch-block과 예외가 발생한 범위 사이의 모든 중간 범위도 청소합니다.실장에 대해서는, https://github.com/psevon/exceptions-and-raii-in-c.git 를 참조해 주세요.

Win32에서 코딩하는 경우 구조화된 예외 처리를 사용하여 유사한 작업을 수행할 수 있습니다.다음과 같은 작업을 수행할 수 있습니다.

foo() {
    myType pFoo = 0;
    __try
    {
        pFoo = malloc(sizeof myType);
        // do some stuff
    }
    __finally
    {
        free pFoo;
    }
}

RAII만큼 쉽지는 않지만 정리 코드를 모두 한 곳에 모아 확실하게 실행할 수 있습니다.

Sometimes i use this approach and it seems good :)

Object *construct(type arg, ...){

    Object *__local = malloc(sizeof(Object));
    if(!__local)
        return NULL;
    __local->prop_a = arg;
    /* blah blah */


} // constructor

void destruct(Object *__this){

   if(__this->prop_a)free(this->prop_a);
   if(__this->prop_b)free(this->prop_b);

} // destructor

Object *o = __construct(200);
if(o != NULL)
   ;;

// use

destruct(o);

/*
  done !
*/

부목이나 Gimpel PC-Lint와 같은 정적 코드 분석 툴이 도움이 될 수 있습니다.또, 그것들을 자동 「연속 통합」스타일의 빌드 서버에 배선하는 것으로, 적당히 「예방적」으로 할 수도 있습니다(이것들 중 하나가 있습니까?: 라이선스:)

이 테마에는 다른 (더 비싼) 변종도 있습니다.

언급URL : https://stackoverflow.com/questions/799825/smart-pointers-safe-memory-management-for-c

반응형