2014-12-08 2 views
5

내 질문 :포인터와 동적 메모리 할당

int* x = new int; 
cout << x<<"\n"; 
int* p; 
cout << p <<"\n"; 
p = x; 
delete p; 
cout << p <<"\n"; 

내가 (도에서 분실) 동적 newdelete을 포인터를 이해하고 이해하기 위해 자신 순전히이 썼다.

내 엑스 코드는 프로그램을 컴파일하고 다음과 같은 결과를 반환 할 수 있습니다

0x100104250 
0x0 
0x100104250 

나는 동적으로 할당 된 메모리에서 삭제를 호출 할 수 있습니다 알고 있습니다. 그러나 위의 프로그램에서 p에 대한 삭제를 호출하고 컴파일합니다.

아무에게도 설명 할 수 없습니까? p을 삭제할 수있는 이유가 무엇인가요?

int* x = new int; 
int* p; 
cout << p <<"\n"; 
delete p; 
cout << p <<"\n"; 

그럼 내 Xcode를 다시 컴파일하고 나를 반환 :.

0x0 
0x0 
Program ended with exit code: 0 

지금, 나는 완전히 잃었다을 :(

또한, 나는 프로그램은 다음에 변경하는 경우 발견 아무도 내게 이것을 설명해 줄 수 없습니까? p은 아무 것도 할 수 없기 때문에 왜 삭제할 수 있습니까? x?


Xcode가 성공적으로 컴파일되므로 위의 두 프로그램이 컴퓨터에 적합하다고 가정합니다. 그러나 나는 다시 동적 할당 메모리에서만 호출 삭제라고 생각합니다. 또는 아마도 포인터가 무엇인지 동적 할당 메모리가 무엇인지 완전히 이해하지 못했습니다. 온라인에서 검색 할 때 post을 찾았습니다. 하지만 제 생각과 다르다고 생각합니다.

도와주세요.


나는 하나 더 질문하고 싶습니다. 이진 검색 트리에 대한 코드는 here입니다. 28 ~ 32 행에서 하나의 자식이있는 노드를 삭제하는 작업을 처리합니다. 웹 링크가 작동하지 않는 경우를 대비하여이 코드를 여기에 넣었습니다.

if (root-> left == NULL) { struct Node * temp = root; root = root-> right; 임시 직원 삭제; }

포인터를 묻는 질문은 위의 코드를 참조하십시오. 이 게시물의 답변에 이어 다음과 같은 방법으로 코드를 이해하는 것이 맞습니까?

root의 상위 노드를 root의 오른쪽 하위 노드로 먼저 연결할 수 없습니다. 루트 노드 아래의 하위 트리도 삭제되므로 루트 노드를 삭제하십시오. 그래서 임시 포인터를 만들어서 루트가 가리키는 메모리 슬롯을 가리켜 야합니다. 그런 다음 루트의 부모 노드를 루트의 하위 노드에 연결합니다. 이제 "root"로 지정된 메모리 슬롯 (즉, 둘 다 동일한 메모리를 가리키는 temp)을 안전하게 삭제할 수 있습니다. 이렇게하면 메모리를 해제하고 부모와 자식 간의 연결을 유지합니다. 또한 임시 메모리가 여전히 있으며 "여전히"메모리 슬롯을 가리 킵니다. 삭제 후 NULL로 설정해야합니까?

다시 한번 감사드립니다.

Yaofeng

+1

두 번째 코드 샘플에서'p'가'0' 인 것은 "행운"입니다. 초기화되지 않았기 때문에 어떤 값을 가질 수 있습니다. '0' (일명'NULL')이므로'delete'를 호출하는 것이 유효합니다 (특히 할당이 실패한 오류 조건을 처리 할 때'NULL '에 대한 백만 가지 검사를 피하는 것이 유용합니다. 나머지 할당 정리 - 만약 모든 포인터가 처음에'NULL'으로 초기화된다면, 할당하지 못한 것을 걱정하지 않고 모든 것을'삭제 '만 할 수 있습니다.) –

+1

조언 만하면 int * p = 0과 같이 포인터 변수를 항상 초기화해야합니다. 또는 int * p = NULL; 이것은 디버그 빌드에서이 작업이 수행되기 때문입니다. 그러나 릴리스 빌드에서는 완료되지 않습니다. 이렇게하면 디버깅에 많은 시간을 절약 할 수 있습니다. – user743414

+1

@ user743414 레거시 코드를 유지하지 않는다면 C++ 11을 사용해야하고 따라서'int * p = nullptr;'을 사용해야합니다. (이 부분) C++ 11은 수년간 모든 주요 컴파일러에서 지원되었습니다. – Angew

답변

2

예, 당신은 단지 new을 통해 할당 된 메모리에 delete를 호출 할 수 있습니다. 포인터를 저장하는 변수가 아니라 메모리의 주소 인 (포인터의 )이 중요한 점에 유의하십시오. 그래서, 당신의 첫 번째 코드 :

int* x = new int; //(A) 
cout << x<<"\n"; 
int* p; 
cout << p <<"\n"; 
p = x; //(B) 
delete p; //(C) 
cout << p <<"\n"; //(D) 

라인 (A)는 변수 x에서이 주소를 동적으로합니다 (예를 들어, 출력 0x100104250) 일부 주소의 메모리를 할당하고 저장합니다. 메모리는 new을 통해 할당되므로 결국 주소 0x100104250에서 delete을 호출해야합니다.

라인 (B)은 주소 0x100104250 (포인터의 값 x)을 포인터 p에 할당합니다. 그런 다음 Line (C)는 delete p을 호출합니다. 이는 "p이 가리키는 메모리 할당 해제"를 의미합니다. 즉, 주소 0x100104250delete으로 전화를 걸면 모두 정상입니다. 주소가 0x100104250인 메모리는 new을 통해 할당 된 이었으므로 delete을 통해 올바르게 할당되지 않았습니다. 값을 저장하기 위해 다른 변수를 사용했다는 사실은 아무런 역할을하지 않습니다.

라인 (D)은 포인터의 값을 출력합니다. delete은 포인터 자체가 아닌 포인터가 가리키는 메모리에서 작동합니다. 포인터의 값은 동일하게 유지됩니다 - 여전히 동일한 메모리를 가리키며 그 메모리는 더 이상 할당되지 않습니다. p는 아무것도 초기화되지 않은 때 delete p를 호출하고 -


두 번째 예는 다르다. 무작위의 메모리를 가리키고 있으므로 delete을 호출하는 것은 당연히 불법입니다 (기술적으로 "정의되지 않은 동작"이 있으며 충돌 가능성이 큽니다).

특정 예제에서는 디버그 빌드를 실행 중이며 컴파일러는 직접 초기화하지 않으면 로컬 변수를 0으로 초기화하는 것으로 보입니다. 따라서 실제로는 두 번째 예제가 충돌하지 않도록합니다. 왜냐하면 null 포인터에서 delete을 호출하면 유효하기 때문에 (아무 것도하지 않기 때문에). 그러나 지역 변수는 일반적으로 암시 적으로 초기화되지 않기 때문에 프로그램에 실제로 버그가 있습니다. x (px으로 동일한 개체를 가리키는 것)로

+0

대단히 감사합니다. 그것은 내가 포인터에 대해 훨씬 편하게 느끼게했다. – Yaofeng

1
p = x; 

p가 같은 값을 포함 할 것이다. 그래서 기본적으로 당신이 x의 것과 동일 p에 의해 언급 된 주소를 삭제하고

delete p; 

그 말을 할 때. 따라서이 주소로 참조 된 객체가 new을 사용하여 할당 될 때 완전히 유효합니다.

두 번째 경우 : -

공동 incidently 포인터 p가 (당신이에 의존해서는 안)을 NULL pointer으로 컴파일러에 의해 설정됩니다. 따라서 포인터를 삭제하면 안전합니다. NULL 포인터가 아니었다면 충돌이 발생했을 것입니다.

+0

예, 가끔 충돌합니다. 나는 다른 코드를 시도 할 때 그것을 발견했다. 나는 그것을 내 질문에 포함하는 것을 잊었다. 이 정보를 확인해 주셔서 감사합니다. – Yaofeng

1

자, "delete"연산자에 대한 문서를 살펴 보겠습니다. http://en.cppreference.com/w/cpp/memory/new/operator_delete에 따르면 이전에 하나의 객체에 할당 된 스토리지 할당을 해제 삭제 표현식에 의해 불려

. 이 함수의 표준 라이브러리 구현의 동작은 ptr이 널 포인터이거나 이전에 new (size_t) 연산자 또는 new (size_t, std :: nothrow_t) 연산자의 표준 라이브러리 구현에서 가져온 포인터가 아니면 정의되지 않습니다. 당신이를 sizeof (int)를 할당 메모리의 int 변수에 대한 바이트 new 연산자를 호출됩니다

그래서 무슨 일 다음이다. 메모리의 해당 부분은 포인터 x에 의해 참조됩니다. 그런 다음 동일한 메모리 주소를 가리키는 다른 포인터 p를 만듭니다. 삭제를 호출하면 메모리가 해제됩니다. p와 x는 여전히 같은 위치에있는 주소을 가리키고 있습니다. 단, 그 위치의 은 이제 가비지입니다. 다음과 같이 쉽게이 이해할 수 있도록하기 위해, 나는 코드를 수정 :

실행 한 후, 나는 다음과 같은 결과를 얻었다
#include <iostream> 
using namespace std; 

int main() { 
    int * x = new int;    // Allocate sizeof(int) bytes, which x references to 
    *x = 8;       // Store an 8 at the newly created storage 
    cout << x << " " << *x << "\n"; // Shows the memory address x points to and "8". (e.g. 0x21f6010 8) 
    int * p;       // Create a new pointer 
    p = x;       // p points to the same memory location as x 
    cout << p << " " << *p << "\n"; // Shows the memory address p points to (the same as x) and "8". 
    delete p;      // Release the allocated memory 
    cout << x << " " << p << " " 
     << *x << " " << *p << "\n"; // p now points to the same memory address as before, except that it now contains garbage (e.g. 0x21f6010 0) 
    return 0; 
} 

:

0x215c010 8 
0x215c010 8 
0x215c010 0x215c010 0 0 

그래서 당신이 메모리를 해제 삭제 사용하여, 기억,하지만를 포인터는 여전히 같은 주소를 가리 킵니다. 따라서 포인터를 NULL로 설정하는 것이 일반적으로 안전한 방법입니다. 이것이 조금 더 이해되기를 바랍니다 :-)

+0

그것은 나를 많이 돕는다. 내 Xcode는 마침내 0 0 대신 8 8을 반환하지만. 그러나 나는 내가 그 요지를 가지고 있다고 생각한다. – Yaofeng

0

답변 전에 다음 사항을 이해해야합니다.

  1. 은 당신이 delete 포인터, 그것은 필요 일단0에 할당 할 수 없습니다. 그것은 일 수 있습니다.
  2. 해를 끼치 지 않고 0 또는 NULL을 삭제할 수 있습니다.
  3. 정의되지 않은 동작은 아무 일도 발생할 수없는 동작입니다. 아무 일도하지 않는 경우, 는 등, 어떤 임의의 결과가 발생할 수 있습니다대로 프로그램은

는 그러나, 나는 위의 프로그램에서 페이지의 삭제라고하며 컴파일 제대로 작동 할 수도, 중단 될 수 있습니다.

아무에게도 설명 할 수 없습니까? p를 왜 삭제할 수 있습니까?

new에 의해 할당 된 메모리 주소를 x을 통해 할당했기 때문입니다. (p = x;) x (또는 p)은 유효한 메모리 위치이며 삭제할 수 있습니다.

이제 xDangling pointer이라고합니다. 더 이상 유효하지 않은 메모리를 가리키고 있기 때문입니다. 삭제 후 x에 액세스하는 것은 정의되지 않은 동작입니다.

아무에게도 설명해 줄 수 없습니까? x와 아무 관계가 없기 때문에 p를 왜 삭제할 수 있습니까?

p에는 0이 할당되어 있기 때문에 이는 정의되지 않은 동작입니다. 그러나 초기화되지 않은 포인터의 값은 0 또는 NULL입니다. 이 시점에서 제대로 작동하는 것으로 보이지만 여기서는 정의되지 않은 동작을 통해 넘어 가고 있습니다.

+0

포인터를 매달아 정보를 공유해 주셔서 감사합니다. – Yaofeng