delete []는 그것이 배열임을 어떻게 알 수 있습니까?
자, 우리는 전달 된 내용에 따라 다음 코드에서 발생하는 일이 정의되지 않은 것에 동의합니다.
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 만 삭제하고 그 시점 이후의 나머지 메모리를 삭제하여 특정 유형의 'killing spree'를 수행하지 않을만큼 똑똑합니다 (와 종료되지 strlen
않은 \0
문자열 과 대조 -계속 될 때까지 계속 진행됩니다) 조회수 0).
이 일을 기억하는 것이 누구의 일입니까? OS가 백그라운드에서 어떤 유형의 레코드를 유지합니까? (나는 무슨 일이 일어 났는지는 정의되지 않았다고 말 함으로써이 게시물을 시작했다는 것을 알고 있지만 실제로는 '죽이는 행위'시나리오가 발생하지 않으므로 실제 세계에서 누군가 가 기억하고 있습니다.)
컴파일러는 배열인지 알지 못하며 프로그래머를 신뢰합니다. 하나의 포인터를 삭제 int
와 함께하는 것은 delete []
정의되지 않은 동작이 발생할 것입니다. 두 번째 main()
예제는 즉시 충돌하지 않더라도 안전하지 않습니다.
컴파일러는 어떻게 든 삭제해야하는 오브젝트 수를 추적해야합니다. 배열 크기를 저장하기에 충분히 초과 할당하여이를 수행 할 수 있습니다. 자세한 내용은 C ++ Super FAQ를 참조하십시오 .
지금까지 주어진 답변으로 해결하지 못하는 한 가지 질문 : 런타임 라이브러리 (실제로 OS가 아닌)가 배열의 수를 추적 할 수 있다면 왜 delete[]
구문이 필요한가? delete
모든 삭제를 처리하는 데 단일 양식을 사용할 수없는 이유는 무엇 입니까?
이에 대한 대답은 C 호환 언어 인 C ++의 근본으로 거슬러 올라갑니다 (더 이상 실제로는 노력하지 않습니다) Stroustrup의 철학은 프로그래머가 사용하지 않는 기능에 대해 프로그래머가 비용을 지불 할 필요가 없다는 것입니다. 배열을 사용하지 않는 경우 할당 된 모든 메모리 청크에 대해 개체 배열 비용을 부담하지 않아도됩니다.
즉, 코드가 단순히
Foo* foo = new Foo;
할당 된 메모리 공간은의 foo
배열을 지원하는 데 필요한 추가 오버 헤드를 포함하지 않아야합니다 Foo
.
추가 배열 크기 정보를 전달하도록 배열 할당 만 설정되었으므로 오브젝트를 삭제할 때 해당 정보를 찾도록 런타임 라이브러리에 지시해야합니다. 우리가 사용해야하는 이유
delete[] bar;
그냥 대신
delete bar;
bar가 배열에 대한 포인터 인 경우
우리 대부분은 (나 자신을 포함하여), 몇 바이트의 메모리에 대한 까다로운 점은 요즘 기이 한 것 같습니다. 그러나 (아직 많은 수의 메모리 블록이 될 수있는 것에서) 몇 바이트를 저장하는 것이 중요한 상황이 여전히 있습니다.
예, OS는 '배경'에 몇 가지 사항을 유지합니다. 예를 들어
int* num = new int[5];
OS는 4 개의 추가 바이트를 할당하고 할당 된 메모리의 처음 4 바이트에 할당 크기를 저장하고 오프셋 포인터를 반환합니다 (즉, 1000에서 1024까지의 메모리 공간을 할당하지만 포인터는 1004를 가리킴). 할당의 크기를 저장하는 1003). 그런 다음 delete가 호출되면 포인터가 전달되기 전에 4 바이트를보고 할당 크기를 찾을 수 있습니다.
할당 크기를 추적하는 다른 방법이 있다고 확신하지만 이것이 하나의 옵션입니다.
이것은 이 질문과 매우 유사하며 찾고있는 세부 사항이 많이 있습니다.
그러나 이것으로 추적하는 것은 OS의 일이 아닙니다. 실제로 런타임 라이브러리 또는 배열의 크기를 추적하는 기본 메모리 관리자입니다. 일반적으로 추가 메모리를 미리 할당하고 해당 위치에 배열 크기를 저장하면됩니다 (대부분 헤드 노드 사용).
다음 코드를 실행하여 일부 구현에서 볼 수 있습니다.
int* pArray = new int[5];
int size = *(pArray-1);
delete
또는 delete[]
할당 된 메모리 (메모리가 지적한)를 모두 해제 할 수 있지만 큰 차이점은 delete
배열에서 배열의 각 요소의 소멸자를 호출하지 않는다는 것입니다.
어쨌든, 혼합 new/new[]
하고 delete/delete[]
아마도 UB입니다.
배열인지 알 수 없으므로 delete[]
일반 old 대신 공급해야합니다 delete
.
나는 이것과 비슷한 질문을했다. C에서는 malloc () (또는 다른 유사한 함수)을 사용하여 메모리를 할당하고 free ()로 삭제하십시오. malloc ()이 하나만 있는데, 이는 단순히 특정 바이트 수를 할당합니다. 하나의 free () 만 있으며, 매개 변수로 포인터를 가져옵니다.
그렇다면 왜 C에서 포인터를 무료로 넘길 수는 있지만 C ++에서는 배열인지 단일 변수인지 알려 주어야합니까?
내가 배운 대답은 클래스 소멸자와 관련이 있습니다.
MyClass 클래스의 인스턴스를 할당하면 ...
classes = new MyClass[3];
삭제로 삭제하면 MyClass의 첫 번째 인스턴스에 대한 소멸자 만 호출 할 수 있습니다. delete []를 사용하면 배열의 모든 인스턴스에 대해 소멸자가 호출됩니다.
이것이 중요한 차이점입니다. 단순히 표준 유형 (예 : int)으로 작업하는 경우 실제로이 문제가 표시되지 않습니다. 또한 new [] 및 delete []에서 delete를 사용하는 동작은 정의되어 있지 않으므로 모든 컴파일러 / 시스템에서 동일한 방식으로 작동하지 않을 수도 있습니다.
free를 사용하여 표준 C에서 malloc으로 작성된 배열을 삭제할 수있는 것과 동일한 방식으로 메모리 할당을 담당하는 런타임에 달려 있습니다. 각 컴파일러는 다르게 구현한다고 생각합니다. 한 가지 일반적인 방법은 배열 크기에 추가 셀을 할당하는 것입니다.
그러나 런타임은 배열 또는 포인터인지 여부를 감지하기에 충분히 똑똑하지 않으며, 알려 주어야하며, 실수하면 올바르게 삭제하지 않습니다 (예 : 배열 대신 ptr). 크기와 관련이없는 값을 가져와 심각한 손상을 초래할 수 있습니다.
컴파일러의 접근 방법 중 하나는 헤드 요소에 약간의 메모리를 할당하고 요소 수를 저장하는 것입니다.
수행 방법 예 : 여기
int* i = new int[4];
컴파일러는 sizeof (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 연산자는 모든 포인터를 "먹을"수 있습니다. 그러나 단일 객체에 대한 포인터가에 제공되면 delete[]
UB가 발생하여 시스템 충돌 또는 전혀 발생하지 않는 모든 일이 발생할 수 있습니다.
C ++은 프로그래머가 할당 해제의 주제에 따라 적절한 버전의 삭제 연산자를 선택하도록 요구합니다 : 배열 또는 단일 객체.
컴파일러가 delete 연산자로 전달 된 포인터가 포인터 배열인지 여부를 자동으로 판별 할 수 있으면 C ++에는 단 하나의 delete 연산자 만 있으면 두 경우 모두에 충분합니다.
컴파일러가 배열인지 여부를 알지 못한다는 데 동의하십시오. 프로그래머에게 달려 있습니다.
컴파일러는 때때로 배열 크기를 저장할만큼 충분히 할당하여 삭제해야하는 오브젝트 수를 추적하지만 항상 필요한 것은 아닙니다.
추가 스토리지가 할당 될 때의 전체 사양은 C ++ ABI (컴파일러 구현 방법)를 참조하십시오. Itanium C ++ ABI : Array Operator new Cookies
배열에는 delete 를 사용할 수 없으며 비 배열에는 delete [] 를 사용할 수 없습니다 .
"정의되지 않은 행동"은 단순히 언어 사양이 어떻게 될지에 대한 보증을하지 않음을 의미합니다. 그렇다고 나쁜 일이 일어날 것이라는 의미는 아닙니다.
이 일을 기억하는 것이 누구의 일입니까? 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);
객체 배열을 반복하고 각각에 대해 소멸자를 호출합니다. 이 간단한 코드 스위치는 new [] 및 delete [] 표현식을 오버로드하고 필요한 경우 메모리를 할당 해제하고 각 객체에 대해 소멸자를 호출하는 템플릿 함수를 제공합니다.
// 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 );
}
///////////////////////////////////////////
just define a destructor inside a class and execute your code with both syntax
delete pointer
delete [] pointer
according to the output u can find the solutions
The answer:
int* pArray = new int[5];
int size = *(pArray-1);
Posted above is not correct and produces invalid value. The "-1"counts elements On 64 bit Windows OS the correct buffer size resides in Ptr - 4 bytes address
참고URL : https://stackoverflow.com/questions/703691/how-does-delete-know-its-an-array
'Programming' 카테고리의 다른 글
Windows의 원격 컴퓨터에서 서비스를 다시 시작하려면 어떻게합니까? (0) | 2020.06.27 |
---|---|
Android 에뮬레이터의 스토리지를 늘리는 방법은 무엇입니까? (0) | 2020.06.27 |
jest.setTimeout에 의해 지정된 5000ms 시간 초과 내에 비동기 콜백이 호출되지 않았습니다. (0) | 2020.06.27 |
Visual Studio에서 만든 Windows 서비스 설치 (0) | 2020.06.27 |
CSS로 전체 웹 페이지를 확장하려면 어떻게해야합니까? (0) | 2020.06.27 |