programing tip

메모리를 확보하는 좋은 방법입니까?

itbloger 2021. 1. 8. 07:54
반응형

메모리를 확보하는 좋은 방법입니까?


의 인스턴스를 해제하는 기능 struct Foo은 다음과 같습니다.

void DestroyFoo(Foo* foo)
{
    if (foo) free(foo);
}

내 동료가 대신 다음을 제안했습니다.

void DestroyFoo(Foo** foo)
{
    if (!(*foo)) return;
    Foo *tmpFoo = *foo;
    *foo = NULL; // prevents future concurrency problems
    memset(tmpFoo, 0, sizeof(Foo));  // problems show up immediately if referred to free memory
    free(tmpFoo);
}

NULL해제 후 포인터를 설정하는 것이 더 낫다는 것을 알지만 다음에 대해 잘 모르겠습니다.

  1. 포인터를 임시 포인터에 할당해야합니까? 동시성 및 공유 메모리 측면에서 도움이됩니까?

  2. 전체 블록을 0으로 설정하여 프로그램을 강제 종료하거나 최소한 상당한 차이가있는 결과를 출력하는 것이 정말 좋은 생각입니까?


포인터를 임시 포인터에 할당해야합니까? 동시성 및 공유 메모리 측면에서 도움이됩니까?

동시성 또는 공유 메모리와 관련이 없습니다. 무의미합니다.

전체 블록을 0으로 설정하여 프로그램을 강제 종료하거나 최소한 상당한 차이가있는 결과를 출력하는 것이 정말 좋은 생각입니까?

아뇨.

동료가 제안한 해결책은 끔찍합니다. 그 이유는 다음과 같습니다.

  • 전체 블록을 0으로 설정해도 아무것도 얻지 못합니다. 누군가 실수로 free () 블록을 사용하고 있기 때문에 블록의 값을 기반으로 그것을 알지 못할 것입니다. 그것이 일종의 블록 calloc()리턴입니다. 따라서 새로 할당 된 메모리 ( calloc()또는 malloc()+memset())인지 또는 이전에 코드에 의해 free () 된 메모리인지 알 수 없습니다. 프로그램이 free ()되는 모든 메모리 블록을 제로화하는 것은 추가 작업입니다.

  • free(NULL);잘 정의되고 작동하지 않으므로의 if조건은 if(ptr) {free(ptr);}아무것도 달성하지 못합니다.

  • 작동 free(NULL);하지 않기 때문에 포인터를로 설정하면 NULL실제로 해당 버그를 숨길 수 있습니다. 왜냐하면 일부 함수가 실제로 free()이미 free () 포인터를 실제로 호출 하면 그들은 그것을 알 수 없기 때문입니다.

  • 대부분의 사용자 함수는 시작시 NULL 검사를 가지며 NULL오류 조건으로 전달 하는 것을 고려하지 않을 수 있습니다.

void do_some_work(void *ptr) {
    if (!ptr) {
        return; 
    }

   /*Do something with ptr here */
}

따라서 모든 추가 검사와 제로 아웃은 실제로 아무것도 개선하지 않았지만 "견고 함"이라는 가짜 감각을 제공합니다. 그것은 단지 하나의 문제를 다른 문제로 대체하여 성능과 코드 팽창에 대한 추가 비용을 초래했습니다.

따라서 free(ptr);래퍼 함수없이 호출하는 것은 간단하고 견고합니다 (대부분의 malloc()구현은 double free에서 즉시 충돌이 발생하므로 좋은 일입니다).

"실수로" free()두 번 이상 전화하는 것을 쉽게 피할 수있는 방법은 없습니다 . 할당 된 모든 메모리를 free()적절하게 추적하는 것은 프로그래머의 책임 입니다. 누군가가 이것을 다루기 어렵다고 생각한다면 C는 아마도 그들에게 적합한 언어가 아닐 것입니다.


동료가 제안하는 것은 함수가 두 번 호출되는 경우 코드를 "안전하게"만들 것입니다 (sleske 주석 참조 ... "안전한"이 모든 사람에게 동일한 의미가 아닐 수 있으므로 ... ;-).

코드를 사용하면 충돌 할 가능성이 큽니다.

Foo* foo = malloc( sizeof(Foo) );
DestroyFoo(foo);
DestroyFoo(foo); // will call free on memory already freed

동료의 코드 버전을 사용하면 충돌하지 않습니다.

Foo* foo = malloc( sizeof(Foo) );
DestroyFoo(&foo);
DestroyFoo(&foo); // will have no effect

이제이 특정 시나리오의 경우 tmpFoo = 0;(내에서 DestroyFoo)하는 것으로 충분합니다. memset(tmpFoo, 0, sizeof(Foo));Foo에 메모리가 해제 된 후 잘못 액세스 될 수있는 추가 속성이있는 경우 충돌을 방지합니다.

내가 네 말을 그래서, 그렇게 할 수있는 좋은 방법이 될 수 있습니다 .... 그러나 그것은 단지 종류의 (호출 할 이유가 분명이 없기 때문에 나쁜 관행이 개발자에 대한 보안 DestroyFoo을 재 할당하지 않고 두 번이) 에 ... 끝, 당신은 DestroyFoo"안전"하지만 느리게 만듭니다 (나쁜 사용을 방지하기 위해 더 많은 일을합니다).


두 번째 솔루션은 과잉 엔지니어링 된 것 같습니다. 물론 어떤 상황에서는 더 안전 할 수도 있지만 오버 헤드와 복잡성이 너무 큽니다.

안전한 편이 되려면 메모리를 해제 한 후 포인터를 NULL로 설정해야합니다. 이것은 항상 좋은 습관입니다.

Foo* foo = malloc( sizeof(Foo) );
DestroyFoo(foo);
foo = NULL;

게다가, 왜 사람들이 free ()를 호출하기 전에 포인터가 NULL인지 확인하는 이유를 모르겠습니다. free ()가 당신을 위해 일하기 때문에 이것은 필요하지 않습니다.

메모리를 0 (또는 다른 것)으로 설정하는 것은 free ()가 메모리를 지우지 않기 때문에 어떤 경우에는 좋은 습관입니다. 다시 사용할 수 있도록 메모리 영역을 여유로 표시 할뿐입니다. 아무도 읽을 수 없도록 메모리를 지우려면 수동으로 정리해야합니다. 그러나 이것은 매우 무거운 작업이므로 모든 메모리를 해제하는 데 사용해서는 안됩니다. 대부분의 경우 지우기없이 해제하는 것만으로도 충분하며 불필요한 작업을 수행하기 위해 성능을 희생 할 필요가 없습니다.


void destroyFoo(Foo** foo)
{
    if (!(*foo)) return;
    Foo *tmpFoo = *foo;
    *foo = NULL;
    memset(tmpFoo, 0, sizeof(Foo));
    free(tmpFoo);
}

동료 코드가 잘못되었습니다.

  • 경우 충돌 것 foo입니다NULL
  • 추가 변수를 생성 할 필요가 없습니다.
  • 값을 0으로 설정하는 것은 의미가 없습니다.
  • 해제해야하는 것이 포함 된 경우 구조체를 직접 해제하는 것은 작동하지 않습니다.

동료가 염두에 둔 것은이 사용 사례라고 생각합니다.

Foo* a = NULL;
Foo* b = createFoo();

destroyFoo(NULL);
destroyFoo(&a);
destroyFoo(&b);

In that case, it should be like this. try here

void destroyFoo(Foo** foo)
{
    if (!foo || !(*foo)) return;
    free(*foo);
    *foo = NULL;
}

First we need to take a look at Foo, let's assume that it looks like this

struct Foo
{
    // variables
    int number;
    char character;

    // array of float
    int arrSize;
    float* arr;

    // pointer to another instance
    Foo* myTwin;
};

Now to define how it should be destroyed, let's first define how it should be created

Foo* createFoo (int arrSize, Foo* twin)
{
    Foo* t = (Foo*) malloc(sizeof(Foo));

    // initialize with default values
    t->number = 1;
    t->character = '1';

    // initialize the array
    t->arrSize = (arrSize>0?arrSize:10);
    t->arr = (float*) malloc(sizeof(float) * t->arrSize);

    // a Foo is a twin with only one other Foo
    t->myTwin = twin;
    if(twin) twin->myTwin = t;

    return t;
}

Now we can write a destroy function oppose to the create function

Foo* destroyFoo (Foo* foo)
{
    if (foo)
    {
        // we allocated the array, so we have to free it
        free(t->arr);

        // to avoid broken pointer, we need to nullify the twin pointer
        if(t->myTwin) t->myTwin->myTwin = NULL;
    }

    free(foo);

    return NULL;
}

Test try here

int main ()
{
    Foo* a = createFoo (2, NULL);
    Foo* b = createFoo (4, a);

    a = destroyFoo(a);
    b = destroyFoo(b);

    printf("success");
    return 0;
}

Unfortunately, this idea is just not working.

If the intent was to catch double free, it is not covering cases like the following.

Assume this code:

Foo *ptr_1 = (FOO*) malloc(sizeof(Foo));
Foo *ptr_2 = ptr_1;
free (ptr_1);
free (ptr_2); /* This is a bug */

The proposal is to write instead:

Foo *ptr_1 = (FOO*) malloc(sizeof(Foo));
Foo *ptr_2 = ptr_1;
DestroyFoo (&ptr_1);
DestroyFoo (&ptr_2); /* This is still a bug */

The problem is that the second call to DestroyFoo() will still crash, because ptr_2 is not reset to NULL, and still point to memory already freed.

ReferenceURL : https://stackoverflow.com/questions/34651234/is-this-a-good-way-to-free-memory

반응형