[c] 해제 후 변수를 NULL로 설정

우리 회사에는 메모리를 비운 후 변수를로 재설정하는 코딩 규칙이 NULL있습니다. 예를 들어 …

void some_func ()
{
    int *nPtr;

    nPtr = malloc (100);

    free (nPtr);
    nPtr = NULL;

    return;
}

위에 표시된 코드와 같은 경우로 설정하면 NULL아무런 의미가 없다고 생각합니다. 아니면 뭔가 빠졌습니까?

그러한 경우에 의미가 없다면, “품질 팀”에게 맡겨서이 코딩 규칙을 제거 할 것입니다. 조언을 부탁드립니다.



답변

사용하지 않는 포인터를 NULL로 설정하는 것은 방어적인 스타일로, 매달려있는 포인터 버그를 방지합니다. 댕글 링 포인터가 해제 된 후 액세스되면 임의 메모리를 읽거나 덮어 쓸 수 있습니다. 널 포인터에 액세스하면 대부분의 시스템에서 즉시 충돌이 발생하여 오류가 무엇인지 즉시 알려줍니다.

지역 변수의 경우, 포인터가 해제 된 후 더 이상 포인터에 액세스하지 않는 것이 “분명한”경우에는 의미가 없습니다. 따라서이 스타일은 멤버 데이터 및 전역 변수에 더 적합합니다. 로컬 변수의 경우에도 메모리가 해제 된 후에 함수가 계속되면 좋은 접근 방법 일 수 있습니다.

스타일을 완성하려면 포인터에 실제 포인터 값이 할당되기 전에 포인터를 NULL로 초기화해야합니다.


답변

NULLafter에 대한 포인터를 설정하는 free것은 특허 적으로 허위 전제에 대한 “좋은 프로그래밍”규칙으로 대중화되는 모호한 관행입니다. 그것은 “올바른 소리”범주에 속하는 가짜 진실 중 하나이지만 실제로는 아무 것도 유용하지 않습니다 (때로는 부정적인 결과를 초래합니다).

주장에 따르면, 포인터를 NULLafter로 설정 free하면 동일한 포인터 값이 free두 번 이상 전달 될 때 두려운 “더블 프리”문제를 방지 할 수 있습니다. 그러나 실제로는 10 개 중 9 개의 경우 에 동일한 포인터 값을 가진 다른 포인터 객체가에 대한 인수로 사용될 때 실제 “double free”문제가 발생합니다 free. 말할 필요도없이, NULLafter에 대한 포인터를 설정하면 free이러한 경우 문제를 방지 할 수있는 것은 없습니다.

물론에 대한 포인터와 동일한 포인터 객체를 사용할 때 “double free”문제가 발생할 수 free있습니다. 그러나 실제로는 이와 같은 상황에서는 일반적으로 코드의 일반적인 논리적 구조에 문제가있는 것이지 우연히 “더블 프리”가 아닙니다. 이러한 경우 문제를 처리하는 올바른 방법은 동일한 포인터가 free두 번 이상 전달 될 때 상황을 피하기 위해 코드 구조를 검토하고 다시 생각하는 것입니다. 이러한 경우 포인터를 설정하고 NULL문제를 “고정”한 것으로 간주하는 것은 카펫 아래에서 문제를 해결하려는 시도에 지나지 않습니다. 코드 구조에 문제가 있으면 항상 다른 방법을 찾을 수 있기 때문에 일반적인 경우에는 효과가 없습니다.

마지막으로, 코드가 포인터 값에 의존 NULL하지 않도록 특별히 설계된 NULL경우 포인터 값을 NULLafter 로 설정하는 것이 free좋습니다. 그러나 일반적인 “모범 사례”규칙 ( “항상 포인터를 ” NULL뒤에 표시 free“에서와 같이)은 잘 알려져 있고 쓸모없는 가짜이며, 순전히 종교적이고 부두적인 이유로 일부가 뒤 따릅니다.


답변

대부분의 응답은 double free를 방지하는 데 중점을 두었지만 포인터를 NULL로 설정하면 또 다른 이점이 있습니다. 포인터를 해제하면 malloc에 ​​대한 다른 호출로 해당 메모리를 재 할당 할 수 있습니다. 여전히 원래의 포인터가있는 경우 자유 변수 후 포인터를 사용하려고 시도하고 다른 변수를 손상시키려는 버그가 생길 수 있으며 프로그램이 알 수없는 상태가되고 모든 종류의 나쁜 일이 발생할 수 있습니다 ( 운이 좋지 않으면 데이터가 손상됩니다). 해제 후 포인터를 NULL로 설정 한 경우 나중에 해당 포인터를 읽거나 쓰려고하면 segfault가 발생하며 이는 일반적으로 임의의 메모리 손상보다 바람직합니다.

두 가지 이유로 free () 후에 포인터를 NULL로 설정하는 것이 좋습니다. 그러나 항상 필요한 것은 아닙니다. 예를 들어, 포인터 변수가 free () 직후 범위를 벗어나면 NULL로 설정해야 할 이유가 없습니다.


답변

메모리 덮어 쓰기를 피하는 것이 좋습니다. 위의 기능에서는 불필요하지만 종종 완료되면 응용 프로그램 오류를 찾을 수 있습니다.

대신 다음과 같이 해보십시오.

#if DEBUG_VERSION
void myfree(void **ptr)
{
    free(*ptr);
    *ptr = NULL;
}
#else
#define myfree(p) do { void ** __p = (p); free(*(__p)); *(__p) = NULL; } while (0)
#endif

DEBUG_VERSION을 사용하면 디버깅 코드에서 여유 공간을 프로파일 링 할 수 있지만 기능은 동일합니다.

편집 : 아래에 제안 된대로 do …를 추가했습니다. 감사합니다.


답변

free () d 포인터에 도달하면 깨지거나 끊어 질 수 있습니다. 메모리가 프로그램의 다른 부분에 재 할당 된 후 메모리가 손상 될 수 있습니다.

포인터를 NULL로 설정 한 경우, 액세스하면 프로그램이 항상 segfault와 충돌합니다. 더 이상, 때로는 작동하지 않습니다 .“ 더 이상, 예측할 수없는 방식으로 충돌합니다. ” 디버깅하기가 더 쉽습니다.


답변

포인터를 free‘d 메모리’로 설정하면 포인터를 통해 해당 메모리에 액세스하려고하면 정의되지 않은 동작이 발생하는 대신 즉시 충돌이 발생합니다. 문제가 발생한 위치를 훨씬 쉽게 파악할 수 있습니다.

나는 당신의 주장을 볼 수 있습니다 : nPtr직후에 범위를 벗어나기 nPtr = NULL때문에에 설정 할 이유가없는 것 같습니다 NULL. 그러나 struct멤버 또는 포인터가 즉시 범위를 벗어나지 않는 다른 곳에서는 더 의미가 있습니다. 포인터를 사용해서는 안되는 코드에서 해당 포인터를 다시 사용할지 여부는 분명하지 않습니다.

규칙이 자동으로 규칙을 시행하는 것이 훨씬 어렵 기 때문에 개발자가 규칙을 따르지 않기 때문에이 두 경우를 구별하지 않고 규칙이 명시되어있을 가능성이 있습니다. NULL매번 무료로 포인터를 설정하는 것은 아프지 않지만 큰 문제를 지적 할 가능성이 있습니다.


답변

c에서 가장 일반적인 버그는 double free입니다. 기본적으로 당신은 그런 일을

free(foobar);
/* lot of code */
free(foobar);

그리고 그것은 꽤 나빠집니다 .OS는 이미 해제 된 메모리를 해제하려고 시도하며 일반적으로 segfault입니다. 따라서 모범 사례는로 설정하는 NULL것이므로 테스트하고이 메모리를 해제해야하는지 확인할 수 있습니다.

if(foobar != null){
  free(foobar);
}

또한 free(NULL)아무것도하지 않으므로 if 문을 작성할 필요가 없습니다. 나는 실제로 OS 전문가가 아니지만 지금은 대부분의 OS가 더블 프리에서 충돌 할 것입니다.

가비지 콜렉션 (Java, dotnet)이있는 모든 언어가이 문제가없고 개발자에게 전체 메모리 관리를 맡길 필요가 없다는 점을 자랑스럽게 여기는 주된 이유이기도합니다.