문제

좋아, 나는 우리 모두가 전달 된 내용에 따라 다음 코드에서 발생하는 일이 정의되지 않았다는 데 동의합니다.

void deleteForMe(int* pointer)
{
     delete[] pointer;
}

포인터는 모든 종류의 다른 것들 일 수 있으므로 무조건적인 일을 수행 할 수 있습니다. delete[] 그것은 정의되지 않았습니다. 그러나 우리가 실제로 배열 포인터를 통과한다고 가정 해 봅시다.

int main()
{
     int* arr = new int[5];
     deleteForMe(arr);
     return 0;
}

내 질문은이 경우 포인터가 ~이다 배열, 이것을 아는 사람은 누구입니까? 언어/컴파일러의 관점에서 arr 배열 포인터 대 단일 INT에 대한 포인터입니다. 도대체, 그것은조차 모른다 arr 동적으로 만들어졌습니다. 그러나 대신 다음을 수행하면

int main()
{
     int* num = new int(1);
     deleteForMe(num);
     return 0;
}

OS는 하나의 int 만 삭제할 수있을만큼 똑똑하고 그 지점을 넘어서 나머지 메모리를 삭제하여 어떤 유형의 '킬링 스파리'를 사용하지 않을 정도로 스마트합니다 (대조적으로 대조하십시오. strlen 그리고 비\0-종료 된 문자열 -0)까지 계속 진행됩니다).

그래서 이런 것들을 기억하는 것은 누구의 일입니까? OS는 어떤 유형의 레코드를 백그라운드에서 유지합니까? (내 말은, 나는이 게시물이 정의되지 않았다고 말 함으로써이 게시물을 시작했다는 것을 알고 있지만, 사실은 '살인 사건'시나리오가 일어나지 않으므로 실제 세계에서는 일어나지 않습니다. 누구 기억하고 있습니다.)

도움이 되었습니까?

해결책

컴파일러는 그것이 배열이라는 것을 알지 못하고 프로그래머를 신뢰하고 있습니다. 단일에 대한 포인터를 삭제합니다 int ~와 함께 delete [] 정의되지 않은 행동을 초래할 것입니다. 두 번째 main() 즉시 충돌하지 않더라도 예는 안전하지 않습니다.

컴파일러는 어떻게 든 삭제 해야하는 객체 수를 추적해야합니다. 배열 크기를 저장하기에 충분히 지나치게 할당함으로써이를 수행 할 수 있습니다. 자세한 내용은 C ++ 슈퍼 FAQ.

다른 팁

지금까지 주어진 답변이 해결되지 않는 한 가지 질문 : 런타임 라이브러리 (OS가 아님)가 배열의 물건 수를 추적 할 수 있다면 왜 우리가 필요한가? delete[] 구문? 왜 싱글이 될 수 없어요 delete 양식을 사용하여 모든 삭제를 처리합니까?

이에 대한 대답은 C ++의 뿌리로 거슬러 올라가는 C 호환 언어 (더 이상 실제로는 노력하지 않습니다.) Stroustrup의 철학은 프로그래머가 사용하지 않는 기능에 대해 비용을 지불 할 필요가 없다는 것이 었습니다. 배열을 사용하지 않는 경우 할당 된 모든 메모리 덩어리에 대해 객체 배열 비용을 전달할 필요가 없습니다.

즉, 코드가 단순히하는 경우입니다

Foo* foo = new Foo;

그런 다음 할당 된 메모리 공간 foo 배열을 지원하는 데 필요한 추가 오버 헤드를 포함해서는 안됩니다. Foo.

추가 배열 크기 정보를 전달하기 위해 배열 할당 만 설정되므로 런타임 라이브러리에 객체를 삭제할 때 해당 정보를 찾도록 지시해야합니다. 그것이 우리가 사용해야하는 이유입니다

delete[] bar;

그냥 대신

delete bar;

바가 배열에 대한 포인터 인 경우.

우리 대부분 (나 자신 포함)에게, 요즘 몇 바이트의 기억에 대한 소란은 기이 한 것 같습니다. 그러나 여전히 몇 바이트를 저장하는 (매우 많은 수의 메모리 블록이 될 수있는)를 저장하는 것이 중요 할 수있는 상황이 여전히 있습니다.

예, OS는 몇 가지를 '배경'에 유지합니다. 예를 들어, 실행하는 경우

int* num = new int[5];

OS는 4 개의 추가 바이트를 할당하고 할당 된 메모리의 첫 4 바이트에 할당 크기를 저장하고 오프셋 포인터를 반환 할 수 있습니다 (즉, 메모리 공간 1000 ~ 1024를 할당하지만 포인터는 위치 1000- 1003 할당 크기 저장). 그런 다음 삭제가 호출되면 포인터가 전달되기 전에 4 바이트를 볼 수 있습니다.

할당 크기를 추적하는 다른 방법이 있다고 확신하지만, 이것이 하나의 옵션입니다.

이것은 매우 유사합니다 이것 질문과 귀하가 찾고있는 많은 세부 사항이 있습니다.

그러나 말할 것도없이, 이것을 추적하는 것은 OS의 임무가 아닙니다. 실제로 배열의 크기를 추적하는 런타임 라이브러리 또는 기본 메모리 관리자입니다. 이것은 일반적으로 추가 메모리를 전면에 할당하고 해당 위치에 배열의 크기를 저장하여 수행됩니다 (대부분 헤드 노드를 사용).

다음 코드를 실행하여 일부 구현에서 볼 수 있습니다.

int* pArray = new int[5];
int size = *(pArray-1);

delete 또는 delete[] 아마도 메모리가 할당 된 메모리를 자유롭게 할 것입니다 (메모리가 가리킹) 큰 차이점은 delete 배열에서 배열의 각 요소의 소멸자를 호출하지 않습니다.

어쨌든, 믹싱 new/new[] 그리고 delete/delete[] 아마도 UB 일 것입니다.

그것이 배열인지 모르겠습니다. 그래서 당신이 공급해야합니다. delete[] 일반 늙은 대신 delete.

나는 이것과 비슷한 질문을했다. C에서는 malloc () (또는 다른 유사한 함수)에 메모리를 할당하고 free ()로 삭제합니다. Malloc ()은 하나 뿐이며, 단순히 특정 수의 바이트를 할당합니다. Free ()는 하나 뿐이며 단순히 파라미터로 포인터를 취합니다.

그렇다면 왜 C에서는 포인터를 무료로 넘겨 줄 수 있지만 C ++에서는 배열인지 단일 변수인지 여부를 알려야합니까?

내가 배운 대답은 계급 소멸자와 관련이 있습니다.

클래스 MyClass 인스턴스를 할당하면 ...

classes = new MyClass[3];

삭제로 삭제하면 MyClass 호출의 첫 번째 인스턴스에 대한 소멸 자만 얻을 수 있습니다. delete []를 사용하면 배열의 모든 인스턴스에 대해 소멸자가 호출 될 수 있습니다.

이것이 중요한 차이입니다. 단순히 표준 유형 (예 : INT)으로 작업하는 경우이 문제를 실제로 보지 못할 것입니다. 또한 New []에서 Delete를 사용하고 New에서 []를 삭제하는 동작은 정의되지 않은 것으로 기억해야합니다. 모든 컴파일러/시스템에서 동일한 방식으로 작동하지 않을 수 있습니다.

무료를 사용하여 표준 C로 Malloc으로 생성 된 배열을 삭제할 수있는 것과 같은 방식으로 메모리 할당을 담당하는 런타임에 달려 있습니다. 각 컴파일러가 다르게 구현한다고 생각합니다. 일반적인 방법 중 하나는 어레이 크기에 추가 셀을 할당하는 것입니다.

그러나 런타임은 배열인지 포인터인지 여부를 감지 할만 큼 똑똑하지 않으며, 정보를 알려야하며, 착각 한 경우 올바르게 삭제하지 않으면 (예 : 배열 대신 PTR) 또는 당신은 결국 크기에 대해 관련없는 가치를 취하고 상당한 피해를줍니다.

컴파일러의 접근 방식 중 하나는 헤드 요소에 약간 더 메모리를 할당하고 요소를 저장하는 것입니다.

예를 들어 어떻게 할 수 있는지 : 여기

int* i = new int[4];

컴파일러는 (int)*5 바이트의 크기를 할당합니다.

int *temp = malloc(sizeof(int)*5)

보관합니다 4 처음에 sizeof(int) 바이트

*temp = 4;

그리고 설정 i

i = temp + 1;

그래서 i 5가 아닌 4 개의 요소 배열을 가리 킵니다.

그리고

delete[] i;

다음과 같은 방법으로 처리됩니다

int *temp = i - 1;
int numbers_of_element = *temp; // = 4
... call destructor for numbers_of_element elements if needed
... that are stored in temp + 1, temp + 2, ... temp + 4
free (temp)

의미 적으로 C ++의 삭제 연산자 모두 포인터를 "먹을"수 있습니다. 그러나 단일 객체에 대한 포인터가 주어진 경우 delete[], 그러면 UB가 발생하여 시스템 충돌 또는 전혀 아무것도없는 것을 포함하여 모든 일이 발생할 수 있음을 의미합니다.

C ++는 프로그래머가 거래의 주제에 따라 배열 또는 단일 객체에 따라 적절한 버전의 삭제 연산자를 선택해야합니다.

컴파일러가 삭제 연산자에게 전달 된 포인터가 포인터 배열인지 자동으로 결정할 수 있다면 C ++에는 하나의 삭제 연산자 만 있으면 두 경우 모두 충분합니다.

컴파일러가 배열인지 아닌지 알지 못한다는 데 동의합니다. 프로그래머에게 달려 있습니다.

컴파일러는 때때로 배열 크기를 저장하기에 충분히 지나치게 할당하여 얼마나 많은 객체를 삭제 해야하는지 추적하지만 항상 필요한 것은 아닙니다.

추가 스토리지가 할당 될 때 완전히 사양하려면 C ++ ABI (컴파일러 구현 방법)를 참조하십시오. Itanium C ++ ABI : 어레이 운영자 새 쿠키

당신은 사용할 수 없습니다 삭제 배열의 경우 사용할 수 없습니다 삭제 [ 비 어선을 위해.

"정의되지 않은 행동"은 단순히 언어 사양이 어떤 일이 일어날 지에 대한 가우 란티를 만들지 않는다는 것을 의미합니다. 그것은 나쁜 일이 일어날 것이라는 것을 부인적으로 의미하지는 않습니다.

그래서 이런 것들을 기억하는 것은 누구의 일입니까? OS는 어떤 유형의 레코드를 백그라운드에서 유지합니까? (내 말은, 나는이 게시물이 정의되지 않았다고 말 함으로써이 게시물을 시작했다는 것을 알고 있지만, 사실은 '살해'시나리오가 일어나지 않기 때문에 실용적인 세상에서 누군가가 기억하고 있습니다.)

여기에는 일반적으로 두 개의 레이어가 있습니다. 기본 메모리 관리자 및 C ++ 구현.

일반적으로 메모리 관리자는 할당 된 메모리 블록의 크기를 (무엇보다도) 기억할 것입니다. 이것은 C ++ 구현이 요청한 블록보다 클 수 있습니다. 일반적으로 메모리 관리자는 할당 된 메모리 블록 전에 메타 데이터를 저장합니다.

C ++ 구현은 일반적으로 자체 목적으로 필요한 경우 배열의 크기 만 기억합니다.

따라서 사소한 파괴자가있는 유형의 경우 "Delete"및 "Delete []의 구현은 일반적으로 동일합니다. C ++ 구현은 단순히 기본 메모리 관리자에게 포인터를 전달합니다. 같은 것

free(p)

반면에 사소한 파괴자 "delete"및 "delete []가있는 유형의 경우 다른 경우가 다를 수 있습니다. "삭제"는 같은 일입니다 (여기서 t는 포인터가 가리키는 유형입니다)

p->~T();
free(p);

"delete []는 같은 것입니다.

size_t * pcount = ((size_t *)p)-1;
size_t count = *count;
for (size_t i=0;i<count;i++) {
  p[i].~T();
}
char * pmemblock = ((char *)p) - max(sizeof(size_t),alignof(T));
free(pmemblock);

Hey ho는 유형 또는 클래스 / 구조에 빌드 배열을 할당 할 때 새 [] 표현으로 할당하는 것에 달려 있으며 생성자 및 소멸자를 제공하지 않으면 연산자가 크기 "sizeof (object)*로 취급합니다. NumObjects "객체 배열 대신이 경우 할당 된 객체의 번호는 어디서나 저장되지 않지만 객체 배열을 할당하고 행동 변경보다 객체에 생성자와 소멸자를 제공하면 새 표현식은 4 바이트 및 저장 번호를 할당합니다. 처음 4 바이트의 객체를 사용하여 각각의 파괴자를 호출 할 수 있으므로 새로운 [] 표현식은 메모리가 반환 될 때보 다 4 바이트로 이동 한 포인터를 반환합니다. 객체의 배열을 통해 각각의 객체에 대한 소멸자를 호출하십시오. 이 간단한 코드 마녀 오버로드를 만들고 [] 표현식을 삭제하고 필요한 경우 메모리를 처리하고 각 객체에 대해 메모리를 호출하는 템플릿 함수를 제공합니다.

// overloaded new expression 
void* operator new[]( size_t size )
{
    // allocate 4 bytes more see comment below 
    int* ptr = (int*)malloc( size + 4 );

    // set value stored at address to 0 
    // and shift pointer by 4 bytes to avoid situation that
    // might arise where two memory blocks 
    // are adjacent and non-zero
    *ptr = 0;
    ++ptr; 

    return ptr;
}
//////////////////////////////////////////

// overloaded delete expression 
void static operator delete[]( void* ptr )
{
    // decrement value of pointer to get the
    // "Real Pointer Value"
    int* realPtr = (int*)ptr;
    --realPtr;

    free( realPtr );
}
//////////////////////////////////////////

// Template used to call destructor if needed 
// and call appropriate delete 
template<class T>
void Deallocate( T* ptr )
{
    int* instanceCount = (int*)ptr;
    --instanceCount;

    if(*instanceCount > 0) // if larger than 0 array is being deleted
    {
        // call destructor for each object
        for(int i = 0; i < *instanceCount; i++)
        {
            ptr[i].~T();
        }
        // call delete passing instance count witch points
        // to begin of array memory 
        ::operator delete[]( instanceCount );
    }
    else
    {
        // single instance deleted call destructor
        // and delete passing ptr
        ptr->~T();
        ::operator delete[]( ptr );
    }
}

// Replace calls to new and delete
#define MyNew ::new
#define MyDelete(ptr) Deallocate(ptr)

// structure with constructor/ destructor
struct StructureOne
{
    StructureOne():
    someInt(0)
    {}
    ~StructureOne() 
    {
        someInt = 0;
    }

    int someInt;
};
//////////////////////////////

// structure without constructor/ destructor
struct StructureTwo
{
    int someInt;
};
//////////////////////////////


void main(void)
{
    const unsigned int numElements = 30;

    StructureOne* structOne = nullptr;
    StructureTwo* structTwo = nullptr;
    int* basicType = nullptr;
    size_t ArraySize = 0;

/**********************************************************************/
    // basic type array 

    // place break point here and in new expression
    // check size and compare it with size passed 
    // in to new expression size will be the same
    ArraySize = sizeof( int ) * numElements;

    // this will be treated as size rather than object array as there is no 
    // constructor and destructor. value assigned to basicType pointer
    // will be the same as value of "++ptr" in new expression
    basicType = MyNew int[numElements];

    // Place break point in template function to see the behavior
    // destructors will not be called and it will be treated as 
    // single instance of size equal to "sizeof( int ) * numElements"
    MyDelete( basicType );

/**********************************************************************/
    // structure without constructor and destructor array 

    // behavior will be the same as with basic type 

    // place break point here and in new expression
    // check size and compare it with size passed 
    // in to new expression size will be the same
    ArraySize = sizeof( StructureTwo ) * numElements;

    // this will be treated as size rather than object array as there is no 
    // constructor and destructor value assigned to structTwo pointer
    // will be the same as value of "++ptr" in new expression
    structTwo = MyNew StructureTwo[numElements]; 

    // Place break point in template function to see the behavior
    // destructors will not be called and it will be treated as 
    // single instance of size equal to "sizeof( StructureTwo ) * numElements"
    MyDelete( structTwo );

/**********************************************************************/
    // structure with constructor and destructor array 

    // place break point check size and compare it with size passed in
    // new expression size in expression will be larger by 4 bytes
    ArraySize = sizeof( StructureOne ) * numElements;

    // value assigned to "structOne pointer" will be different 
    // of "++ptr" in new expression  "shifted by another 4 bytes"
    structOne = MyNew StructureOne[numElements];

    // Place break point in template function to see the behavior
    // destructors will be called for each array object 
    MyDelete( structOne );
}
///////////////////////////////////////////

클래스 내부에서 소멸자를 정의하고 두 구문으로 코드를 실행하십시오.

delete pointer

delete [] pointer

출력에 따르면 솔루션을 찾을 수 있습니다

대답:

int* parray = 새로운 int [5];

int size = *(parray-1);

위에 게시 된 것은 정확하지 않으며 잘못된 값을 생성합니다. "-1"은 64 비트 Wind

라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top