programing

빈 후 변수를 NULL로 설정

copyandpastes 2022. 8. 10. 23:05
반응형

빈 후 변수를 NULL로 설정

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

void some_func () 
{
    int *nPtr;

    nPtr = malloc (100);

    free (nPtr);
    nPtr = NULL;

    return;
}

위의 코드와 같은 경우로,NULL아무 의미도 없습니다.아니면 제가 뭘 놓치고 있는 건가요?

이 경우 의미가 없다면 품질팀과 상의하여 이 코딩 규칙을 삭제하겠습니다.조언 부탁드립니다.

사용되지 않는 포인터를 NULL로 설정하는 것은 포인터 버그로부터 보호하는 방어 스타일입니다.행잉 포인터가 해방된 후에 액세스 되면 랜덤 메모리를 읽거나 덮어쓸 수 있습니다.Null 포인터에 액세스하면 대부분의 시스템에서 즉시 크래시가 발생하고 오류가 무엇인지 즉시 알 수 있습니다.

로컬 변수의 경우 포인터가 해방된 후 더 이상 액세스되지 않는 것이 "명백한" 경우 다소 의미가 없을 수 있으므로 이 스타일은 멤버 데이터 및 전역 변수에 더 적합합니다.로컬 변수의 경우에도 메모리가 해방된 후에도 함수가 계속된다면 좋은 접근법이 될 수 있습니다.

스타일을 완료하려면 NULL에 대한 포인터를 초기화하고 나서 진정한 포인터 값을 할당해야 합니다.

대부분의 응답은 더블 프리 방지에 중점을 두고 있지만 포인터를 NULL로 설정하면 다른 이점이 있습니다.포인터를 해방하면, 그 메모리는 다른 콜에 의해서 malloc 에 재할당할 수 있습니다.원래 포인터가 아직 주변에 있으면 포인터를 사용하다가 다른 변수를 손상시키면 프로그램이 알 수 없는 상태가 되고 모든 종류의 나쁜 일이 발생할 수 있습니다(운 좋으면 크래시, 운이 나쁘면 데이터 손상).빈 후에 포인터를 NULL로 설정했을 경우, 나중에 그 포인터를 통해 읽기/쓰기를 시도하면 seg fault가 발생합니다.이것은 일반적으로 랜덤메모리 파손보다 바람직합니다.

두 가지 이유 모두 free() 후에 포인터를 NULL로 설정하는 것이 좋습니다.하지만 꼭 필요한 건 아니에요예를 들어 포인터 변수가 free() 직후에 범위를 벗어나면 NULL로 설정할 이유가 별로 없습니다.

포인터 설정NULL끝나고free명백한 잘못된 전제하에 종종 "좋은 프로그래밍" 규칙으로 널리 알려진 의심스러운 관행입니다.그것은 "올바른 소리" 범주에 속하지만 실제로는 전혀 쓸모없는 것을 성취하지 못하고 때로는 부정적인 결과를 초래하는 거짓 진실들 중 하나이다.

전해지는 바에 따르면 포인터 설정:NULL끝나고free같은 포인터 값이 에 전달될 때 두려운 "더블 프리" 문제를 방지하도록 되어 있습니다.free한 번이 아닙니다.그러나 실제로는 10개 중 9개의 경우에서 동일한 포인터 값을 가진 다른 포인터 객체가 인수로 사용될 때 실제 "더블 프리" 문제가 발생합니다.free에 포인터를 설정하는 것은 말할 필요도 없습니다.NULL끝나고free이러한 경우 문제를 방지할 수 있는 것은 전혀 없습니다.

물론, 동일한 포인터 객체를 인수로 사용하면 "더블 프리" 문제에 직면할 수 있습니다.free그러나, 현실에서는, 그러한 상황은, 단순한 우연한 「더블 프리」가 아니고, 코드의 일반적인 논리 구조에 문제가 있는 것을 나타내고 있습니다.이러한 경우 문제를 처리하는 적절한 방법은 동일한 포인터가 전달되는 상황을 피하기 위해 코드의 구조를 검토하고 재고하는 것입니다.free한 번이 아닙니다.이 경우 포인터를 다음으로 설정합니다.NULL그리고 그 문제를 "불법"으로 간주하는 것은 그 문제를 은폐하려는 시도에 지나지 않는다.코드 구조의 문제는 항상 자신을 드러내는 다른 방법을 찾기 때문에 일반적인 경우에는 작동하지 않습니다.

마지막으로, 당신의 코드가 포인터 값에 의존하도록 특별히 설계된 경우NULL그렇지 않으면.NULL, 포인터 값을 로 설정해도 전혀 문제가 없습니다.NULL끝나고free단, 일반적인 '우량한 관행' 규칙('항상 포인터를 에 설정한다'와 같다)NULL끝나고free이는 다시 한번 잘 알려져 있고 꽤 쓸모없는 가짜이며, 순수하게 종교적이고 부두교적인 이유로 종종 뒤따른다.

이는 메모리 덮어쓰기를 방지하기 위한 좋은 방법으로 간주됩니다.위의 기능에서는 불필요하지만 실행 시 애플리케이션 오류를 발견할 수 있는 경우가 많습니다.

대신 다음과 같은 방법을 사용해 보십시오.

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

DEBUG_VERSION을 사용하면 디버깅코드를 자유롭게 사용할 수 있지만 기능적으로는 동일합니다.

편집: 작업 추가...아래에 제시된 바와 같이, 감사합니다.

다음과 같은 NULL을 사용하여 포인터 변수를 선언하는 것이 좋습니다.

int *ptr = NULL;

를 들어 ptr이 0x1000 메모리주소를 가리키고 있다고 합시다.사용 후free(ptr), 포인터 변수를 NULL로 다시 선언함으로써 항상 무효로 하는 것이 좋습니다.예를 들어 다음과 같습니다.

free(ptr);
ptr = NULL;

NULL로 다시 선언되지 않은 경우에도 포인터 변수는 계속 같은 주소(0x1000)를 가리키며 이 포인터 변수를 당글링 포인터라고 합니다.다른 포인터 변수(예를 들어 q)를 정의하고 주소를 새 포인터에 동적으로 할당하면 새 포인터 변수에 의해 동일한 주소(0x1000)를 사용할 가능성이 있습니다.같은 포인터(ptr)를 사용하여 같은 포인터(ptr)가 가리키는 주소에서 값을 갱신하면 프로그램은 (p와 q가 같은 주소(0x1000)를 가리키기 때문에) q가 가리키는 위치에 값을 쓰게 됩니다.

예.

*ptr = 20; //Points to 0x1000
free(ptr);
int *q = (int *)malloc(sizeof(int) * 2); //Points to 0x1000
*ptr = 30; //Since ptr and q are pointing to the same address, so the value of the address to which q is pointing would also change.

두 가지 이유가 있습니다.

이중 프리 시 충돌 방지

RageZ의해 중복된 질문으로 작성되었습니다.

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

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

OS는 이미 해방된 메모리를 해방하려고 하고, 일반적으로 세그먼트 폴트가 발생합니다.따라서 좋은 방법은 다음과 같이 설정하는 것입니다.NULL이 메모리를 해방할 필요가 있는지 여부를 테스트하여 확인할 수 있습니다.

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

또한 주의할 점은free(NULL)아무것도 하지 않기 때문에 if 스테이트먼트를 작성할 필요가 없습니다.저는 OS 전문가가 아닙니다만, 지금도 대부분의 OS가 더블 프리로 크래쉬 합니다.

이것이 가비지 컬렉션(Java, dotnet)을 사용하는 모든 언어가 이 문제를 겪지 않고 메모리 관리 전체를 개발자에게 맡길 필요가 없다는 것을 자랑스럽게 여기는 주된 이유입니다.

이미 사용 가능한 포인터 사용 안 함

마틴 뢰위스에 의해 다른 답변으로 쓰여졌다.

사용되지 않는 포인터를 NULL로 설정하는 것은 포인터 버그로부터 보호하는 방어 스타일입니다.행잉 포인터가 해방된 후에 액세스 되면 랜덤 메모리를 읽거나 덮어쓸 수 있습니다.Null 포인터에 액세스하면 대부분의 시스템에서 즉시 크래시가 발생하고 오류가 무엇인지 즉시 알 수 있습니다.

로컬 변수의 경우 포인터가 해방된 후 더 이상 액세스되지 않는 것이 "명백한" 경우 다소 의미가 없을 수 있으므로 이 스타일은 멤버 데이터 및 전역 변수에 더 적합합니다.로컬 변수의 경우에도 메모리가 해방된 후에도 함수가 계속된다면 좋은 접근법이 될 수 있습니다.

스타일을 완료하려면 NULL에 대한 포인터를 초기화하고 나서 진정한 포인터 값을 할당해야 합니다.

이것은 사실 중요할 수 있다.메모리를 해방해도, 프로그램의 후반부에서, 공간에 착지하는 새로운 것이 할당될 가능성이 있습니다.이전 포인터가 유효한 메모리 청크를 가리킵니다.그러면 누군가가 포인터를 사용하여 잘못된 프로그램 상태가 될 수 있습니다.

포인터를 NULL 아웃하면 포인터를 사용하려고 하면 0x0이 참조 해제되어 바로 크래시됩니다.이것은 디버깅이 간단합니다.랜덤 메모리를 가리키는 랜덤 포인터는 디버깅하기 어렵습니다.물론 필수는 아니지만 베스트 프랙티스 문서에 기재되어 있습니다.

ANSI C 표준에서:

void free(void *ptr);

free 함수는 ptr이 가리키는 공간의 할당을 해제합니다.즉, 추가 할당에 사용할 수 있게 됩니다.ptr이 늘포인터일 경우 액션은 발생하지 않습니다.그렇지 않으면 인수가 calloc, malloc 또는 realloc 함수에 의해 이전에 반환된 포인터와 일치하지 않거나 공간이 free 또는 realloc 호출에 의해 할당 해제된 경우 동작은 정의되지 않습니다.

"정의되지 않은 동작"은 거의 항상 프로그램 크래시입니다.이를 피하기 위해 포인터를 NULL로 리셋하는 것이 안전합니다.free() 자체는 포인터에 대한 포인터가 아닌 포인터만 전달되므로 이 작업을 수행할 수 없습니다.포인터를 NULL로 하는 free()의 보다 안전한 버전을 쓸 수도 있습니다.

void safe_free(void** ptr)
{
  free(*ptr);
  *ptr = NULL;
}

최근에 나는 답을 찾다가 같은 질문을 받았다.나는 다음과 같은 결론에 도달했다.

이것은 베스트 프랙티스로, 모든 (내장형) 시스템에서 휴대할 수 있도록 하기 위해서는, 이것을 따를 필요가 있습니다.

free()는 라이브러리 함수입니다.플랫폼 변경에 따라 달라지므로 이 함수에 포인터를 전달한 후 메모리를 해방한 후 이 포인터가 NULL로 설정될 것으로 예상하지 마십시오.이는 플랫폼에 구현된 일부 라이브러리의 경우 해당되지 않을 수 있습니다.

그러니 항상 힘내세요.

free(ptr);
ptr = NULL;

해방된 포인터를 NULL로 설정하는 것은 필수는 아니지만 권장되는 방법입니다.이렇게 하면 1) 풀린 뾰족한 2) 풀린 견인기를 사용하지 않아도 됩니다.

이는 NULL에 대한 모든 포인터를 초기화하기 위한 인수일 수 있지만 다음과 같은 것은 매우 교활한 버그일 수 있습니다.

void other_func() {
  int *p; // forgot to initialize
  // some unrelated mallocs and stuff
  // ...
  if (p) {
    *p = 1; // hm...
  }
}

void caller() {
  some_func();
  other_func();
}

p전자와 같은 스택상의 장소에 도달하다nPtr따라서 유효한 것처럼 보이는 포인터가 포함되어 있을 수 있습니다.에의 할당*p관련 없는 모든 것을 덮어쓰고 추악한 버그로 이어질 수 있습니다.특히 컴파일러가 디버깅모드에서 로컬 변수를 0으로 초기화하지만 최적화를 켜면 초기화되지 않는 경우.디버깅 빌드에서는 버그의 징후는 보이지 않지만 릴리스 빌드는 랜덤하게 전개됩니다.

원래의 질문에 대해서:콘텐츠가 해방된 직후에 포인터를 NULL로 설정하는 것은 코드가 모든 요건을 충족하고 완전히 디버깅되어 다시 변경되지 않는 한 완전히 시간 낭비입니다.한편, 해방된 포인터를 NULL로 방어하는 것은 누군가가 부주의하게 free() 아래에 새로운 코드 블록을 추가했을 때, 원래의 모듈의 설계가 올바르지 않을 때, 그리고 컴파일은 하지만 무엇을 하고 싶은지 모르는 버그의 경우 매우 유용합니다.

어떤 시스템이든, 가장 쉽게 올바른 방법으로 만들겠다는 달성 불가능한 목표와 부정확한 측정으로 인한 절감 불가능한 비용이 있습니다.C에서는 매우 날카롭고 강력한 도구 세트를 제공하고 있습니다.이 도구는 숙련된 작업자의 손에 많은 것을 만들 수 있으며 부적절하게 다루면 온갖 종류의 은유적인 부상을 입힐 수 있습니다.어떤 것들은 이해하거나 올바르게 사용하기 어렵다.그리고 사람들은 당연히 위험을 회피하기 때문에 무료 호출을 하기 전에 NULL 값을 확인하는 것과 같은 불합리한 일을 합니다.

측정 문제는 좋은 것과 나쁜 것을 구분하려고 할 때마다 사례가 복잡해질수록 측정이 모호해질 가능성이 높다는 것입니다.만약 좋은 관행만 지키는 것이 목표라면, 몇몇 애매한 관행은 실제로 좋지 않은 관행과 함께 버려집니다.만약 여러분의 목표가 좋지 않은 것을 제거하는 것이라면, 모호함은 좋은 것에 머물러 있을 수 있습니다.좋은 것만 유지하거나 나쁜 것을 제거하는 두 가지 목표는 정반대로 보일 수 있지만, 대개 둘 다 아닌 세 번째 그룹이 있습니다.

품질 부문에 문의하기 전에 버그 데이터베이스를 조사하여 잘못된 포인터 값으로 인해 얼마나 자주 문제가 발생하는지 확인하십시오.실제 차이를 만들고 싶다면 프로덕션 코드에서 가장 일반적인 문제를 식별하고 이를 방지하기 위한 세 가지 방법을 제안하십시오.

요약하자면, 해방된 주소에 실수로 액세스 하고 싶지 않습니다.주소를 해방하면, 히프내의 그 주소를 다른 애플리케이션에 할당할 수 있기 때문입니다.

그러나 포인터를 NULL로 설정하지 않고 실수로 포인터의 참조를 해제하거나 주소의 값을 변경하려고 해도 여전히 할 수 있습니다.그러나 논리적으로는 하고 싶지 않은 작업은 할 수 없습니다.

해방된 메모리 위치에 계속 액세스할 수 있는 이유는 무엇입니까?이유:메모리를 비웠을 수도 있지만 포인터 변수에는 힙 메모리 주소에 대한 정보가 남아 있습니다.따라서 방어 전략으로 NULL로 설정해 주세요.

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

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

OS는 이미 해방된 메모리를 해방하려고 하고, 일반적으로 세그먼트 폴트가 발생합니다.따라서 좋은 방법은 다음과 같이 설정하는 것입니다.NULL이 메모리를 해방할 필요가 있는지 여부를 테스트하여 확인할 수 있습니다.

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

또한 주의할 점은free(NULL)아무것도 하지 않기 때문에 if 스테이트먼트를 작성할 필요가 없습니다.저는 OS 전문가가 아닙니다만, 지금도 대부분의 OS가 더블 프리로 크래쉬 합니다.

이것이 가비지 컬렉션(Java, dotnet)을 사용하는 모든 언어가 이 문제를 겪지 않고 메모리 관리 전체를 개발자에게 맡길 필요가 없다는 것을 자랑스럽게 여기는 주된 이유입니다.

free()d였던 포인터에 도달하면 포인터가 파손되거나 파손되지 않을 수 있습니다.그 메모리가 프로그램의 다른 부분에 재할당되면 메모리가 파손될 수 있습니다.

포인터를 NULL로 설정하면 해당 포인터에 액세스하면 프로그램이 항상 segfault와 함께 크래시됩니다.더 이상, 때로는 작동하기도 하고, 더 이상, 예측할 수 없는 방식으로 부서지기도 한다.'디버깅이 훨씬 쉽습니다.

포인터 설정free'd memory means that any attempt to access that memory through the pointer will immediately crash, instead of causing undefined behavior. It makes it much easier to determine where things went wrong.

I can see your argument: since nPtr is going out of scope right after nPtr = NULL, there doesn't seem to be a reason to set it to NULL. However, in the case of a struct member or somewhere else where the pointer is not immediately going out of scope, it makes more sense. It's not immediately apparent whether or not that pointer will be used again by code that shouldn't be using it.

It's likely the rule is stated without making a distinction between these two cases, because it's much more difficult to automatically enforce the rule, let alone for the developers to follow it. It doesn't hurt to set pointers to NULL after every free, but it has the potential of pointing out big problems.

The idea behind this, is to stop accidental reuse of the freed pointer.

I find this to be little help as in my experience when people access a freed memory allocation it's almost always because they have another pointer to it somewhere. And then it conflicts with another personal coding standard which is "Avoid useless clutter", so I don't do it as I think it rarely helps and makes the code slightly less readable.

However - I won't set the variable to null if the pointer isn't supposed to be used again, but often the higher level design gives me a reason to set it to null anyway. For example if the pointer is a member of a class and I've deleted what it points to then the "contract" if you like of the class is that that member will point to something valid at any time so it must be set to null for that reason. A small distinction but I think an important one.

In c++ it's important to always be thinking who owns this data when you allocate some memory (unless you are using smart pointers but even then some thought is required). And this process tends to lead to pointers generally being a member of some class and generally you want a class to be in a valid state at all times, and the easiest way to do that is to set the member variable to NULL to indicate it points to nothing now.

일반적인 패턴은 컨스트럭터의 모든 멤버포인터를 NULL로 설정하고 디스트럭터 호출을 설계에 의해 클래스가 소유한다고 되어 있는 데이터에 대한 포인터로 삭제하는 것입니다.분명히 이 경우 이전에 소유한 데이터가 없음을 나타내기 위해 무언가를 삭제할 때 포인터를 NULL로 설정해야 합니다.

요약하자면, 예를 들어, 어떤 것을 삭제한 후 포인터를 늘로 설정하는 경우가 많은데, 이는 코딩 표준 규칙을 맹목적으로 따르는 것이 아니라 더 큰 설계와 데이터 소유자에 대한 생각의 일부입니다.저는 당신의 예에서는 그렇게 하는 것이 유익하지 않다고 생각하고 "cutter"를 추가했습니다.이것은 제 경험상 버그와 악성코드에 대한 책임이 이와 같기 때문입니다.

이 규칙은 다음과 같은 상황을 방지하려는 경우에 유용합니다.

1) 로직과 메모리 관리가 복잡한 매우 긴 기능을 가지고 있기 때문에 나중에 삭제된 메모리에 대한 포인터를 실수로 재사용하고 싶지 않습니다.

2) 포인터는 상당히 복잡한 동작을 하는 클래스의 멤버 변수이므로 실수로 삭제된 메모리에 대한 포인터를 다른 함수로 재사용하고 싶지 않습니다.

당신의 시나리오에서는 별로 말이 되지 않지만, 기능이 길어지면 문제가 될 수 있습니다.

NULL로 설정하면 나중에 실제로 논리 오류가 마스킹될 수 있다고 주장할 수 있습니다.또한 이것이 유효하다고 생각되는 경우에는 NULL로 크래시되므로 문제가 되지 않습니다.

일반적으로 좋은 아이디어라고 생각될 때는 NULL로 설정하고, 가치가 없다고 생각될 때는 신경 쓰지 않는 것이 좋습니다.대신 짧은 기능과 잘 설계된 수업을 쓰는 데 집중하세요.

다른 사람이 말한 것에 덧붙여 포인터 사용의 좋은 방법 중 하나는 그것이 유효한 포인터인지 아닌지를 항상 확인하는 것입니다.예를 들어 다음과 같습니다.


if(ptr)
   ptr->CallSomeMethod();

해방 후에 포인터를 NULL로 명시적으로 마크하면, C/C++ 로 이러한 종류의 사용이 가능하게 됩니다.

NULL에 포인터를 설정하는 것은 이른바 double-free를 다시 보호하는 것입니다.이 경우 free()는 같은 주소로 블록을 재할당하지 않고 여러 번 호출됩니다.

Double-free leads to undefined behaviour - usually heap corruption or immediately crashing the program. Calling free() for a NULL pointer does nothing and is therefore guaranteed to be safe.

So the best practice unless you now for sure that the pointer leaves scope immediately or very soon after free() is to set that pointer to NULL so that even if free() is called again it is now called for a NULL pointer and undefined behaviour is evaded.

The idea is that if you try to dereference the no-longer-valid pointer after freeing it, you want to fail hard (segfault) rather than silently and mysteriously.

But... be careful. Not all systems cause a segfault if you dereference NULL. On (at least some versions of) AIX, *(int *)0 == 0, and Solaris has optional compatibility with this AIX "feature."

As you have a quality assurance team in place, let me add a minor point about QA. Some automated QA tools for C will flag assignments to freed pointers as "useless assignment to ptr". For example PC-lint/FlexeLint from Gimpel Software says tst.c 8 Warning 438: Last value assigned to variable 'nPtr' (defined at line 5) not used

There are ways to selectively suppress messages, so you can still satisfy both QA requirements, should your team decide so.

ReferenceURL : https://stackoverflow.com/questions/1025589/setting-variable-to-null-after-free

반응형