2014-09-27 3 views
1

메모리를 비우면 해당 메모리를 가리키는 포인터는 어떻게됩니까? 즉시 유효하지 않게됩니까? 후에 나중에 다시 유효하게되면 어떻게됩니까?realloc() 매달린 포인터 및 정의되지 않은 동작

틀림없이 포인터가 유효하지 않게되고 "유효"하게되는 일반적인 경우는 이전에 사용 된 메모리에 할당되는 다른 객체가 될 것이며 포인터를 사용하여 메모리에 액세스하면 분명히 정의되지 않은 동작입니다. 매달린 포인터 메모리는 수업 1을 거의 덮어 씁니다.

그러나 동일한 할당에 대해 메모리가 다시 유효하게되면 어떻게됩니까? 그 일이 일어날 수있는 유일한 표준 방법은 realloc()입니다. malloc() '메모리 블록 오프셋 (offset) > 1에 포인터가있는 경우, realloc()을 사용하여 블록을 오프셋보다 작게 축소하면 포인터가 유효하지 않게됩니다. 그런 다음 realloc()을 다시 사용하여 적어도 매달려있는 포인터가 가리키는 객체 유형을 덮어 씌우려면 다시 블럭을 확장하십시오. 어느 경우에도 realloc()은 메모리 블록을 이동시키지 않았고 다시 매달린 포인터가 유효합니까?

이것은 C 또는 C++ 표준을 이해하는 방법을 실제로 어떻게 해석해야하는지 모를 정도입니다. 아래는 그것을 보여주는 프로그램입니다.

#include <stdio.h> 
#include <stdlib.h> 
#include <string.h> 

int main(void) 
{ 
    static const char s_message[] = "hello there"; 
    static const char s_kitty[] = "kitty"; 

    char *string = malloc(sizeof(s_message)); 
    if (!string) 
    { 
     fprintf(stderr, "malloc failed\n"); 
     return 1; 
    } 

    memcpy(string, s_message, sizeof(s_message)); 
    printf("%p %s\n", string, string); 

    char *overwrite = string + 6; 
    *overwrite = '\0'; 
    printf("%p %s\n", string, string); 

    string[4] = '\0'; 
    char *new_string = realloc(string, 5); 
    if (new_string != string) 
    { 
     fprintf(stderr, "realloC#1 failed or moved the string\n"); 
     free(new_string ? new_string : string); 
     return 1; 
    } 
    string = new_string; 
    printf("%p %s\n", string, string); 

    new_string = realloc(string, 6 + sizeof(s_kitty)); 
    if (new_string != string) 
    { 
     fprintf(stderr, "realloC#2 failed or moved the string\n"); 
     free(new_string ? new_string : string); 
     return 1; 
    } 

    // Is this defined behavior, even though at one point, 
    // "overwrite" was a dangling pointer? 
    memcpy(overwrite, s_kitty, sizeof(s_kitty)); 
    string[4] = s_message[4]; 
    printf("%p %s\n", string, string); 
    free(string); 
    return 0; 
} 
+0

글쎄, 내 프로그램에 버그를 작성하면 어떻게되는지 물어볼 수 있습니다. 동일한 메모리 블록이 다른 malloc 후에 다시 할당 된 경우에도 포인터는 유효하지 않지만 역 참조는 UB입니다. –

+0

free'ed 메모리에 대한 포인터가 유효하지 않을 수도 있지만 여전히 작동 할 수 있습니다. 이것은 메모리가 변경되었는지 여부에 달려 있습니다. "free'ed"이지만 여전히 동일한 값 (대개의 경우)이 포함되어 있으면 해당 메모리가 변경 될 때까지 코드가 작동합니다.이 경우 프로그램이 충돌합니다 ... 그렇지 않기 때문에 버그 추적이 어려워집니다 결정 론적이다. 프로그램을 실행하면 X를 다시 실행할 때 충돌이 발생하며 포인터가 업데이트되지 않았기 때문에 충돌이 발생하지 않습니다. – AbstractDissonance

답변

7

메모리를 비우면 해당 메모리를 가리키는 포인터는 어떻게됩니까? 즉시 유효하지 않게됩니까?

예. C 표준의 섹션 6.2.4에서 :

수명 오브젝트의 저장이 예약을 보장 되는 동안의 프로그램 실행 부이다.개체가 있고 주소가 일정하며 수명 기간 동안 마지막으로 저장된 값인 을 유지합니다. 객체가 수명 ( ) 외부에서 참조되면 비헤이비어가 정의되지 않습니다. 포인터가 가리키는 대상 (또는 바로 과거)이 해당 수명 기간의 끝에 도달하면 포인터의 값이 불확실 해집니다.

그리고 섹션 7.22.3.5에서

는 :

realloc과 기능은 기존의 객체가 ptr이 가리키는 크기가 지정한 크기를 가진 새 개체에 포인터를 반환 할당을 취소합니다. 새로운 객체의 내용은 할당 해제 이전의 객체와 동일해야하며, 새로운 크기와 이전 크기 중 더 작은 숫자 인 중 작은 것까지입니다. 새 개체의 이전 개체 크기를 초과하는 바이트의 값은 입니다.

참고는 realloc과에서 다시 무엇을 얻을 표준에 의해 된 객체새로운 객체 ...에 대한 참조 당신이 전에 한 것보다 다른 객체이다; free을 수행 한 다음에 malloc을 수행하는 것과 다르지 않으며 새 크기가 < = 이전 크기 인 경우에도 두 객체가 동일한 주소를 가질 것이라는 보장은 없습니다. 실제 구현에서는 크기가 다른 객체는 서로 다른 자유 목록에서 가져옵니다.

나중에 다시 유효하게되면 어떻게됩니까?

동물이 없습니다. 유효성은 발생하는 이벤트가 아니라 C 표준에 의해 추상화 된 조건입니다. 일부 구현에서는 포인터가 작동하지만, 일단 가리킨 메모리를 해제하면 모든 내기가 꺼집니다.

그러나 동일한 할당에 대해 메모리가 다시 유효하게되면 어떻게됩니까? 그럴 수있는 표준 방법은 하나뿐입니다. realloc()

죄송합니다. C Standard에는 아무런 영향을 미치지 않는 언어가 포함되어 있습니다. 당신이 표지 다음)으로 realloc을 (사용 다시 적어도 다시 블록을 성장 경우

오브젝트 유형은 메모리 블록을 이동) (댕글 링 포인터가 가리키는하고, 어느 경우 realloc을했다

표준이 그와 같은 것을 보장하지 않는다는 것을 당신은 알 수 없습니다. 특히 작은 크기로 재 할당 할 때 대부분의 구현은 단축 된 블록 바로 다음에 메모리를 수정합니다. 원래 크기로 다시 할당하면 추가 된 부분에 약간의 쓰레기가 생길 수 있습니다. 축소되기 전에는 원래 크기가 아닐 것입니다. 일부 구현에서 일부 블록 크기는 해당 블록 크기의 목록에 보관됩니다. 다른 크기로 다시 할당하면 완전히 다른 메모리가 생깁니다. 그리고 다중 스레드를 가진 프로그램에서 해제 된 메모리는 두 개의 realloc 사이의 다른 스레드에 할당 될 수 있습니다.이 경우 더 큰 크기의 realloc은 객체를 다른 위치로 이동해야합니다.

다시 유효한 매달린 포인터입니까?

위 참조; 무효입니다. 돌아갈 길이 없다.

이것은 C 또는 C++ 표준을 해석하는 방법을 알지 못하는 그런 구석의 사례입니다.

그것은 어떤 종류의 코너 케이스도 아니며 표준에서 볼 수있는 내용을 알지 못합니다. 자유로운 메모리가 불확실한 내용을 가지고 있고 그 안에있는 포인터의 값이 또한 불명확하며 나중에 realloc으로 마술처럼 복원되었다고 주장하지 않습니다.

현대적인 최적화 컴파일러는 정의되지 않은 동작에 대해 알기 위해 작성되었으며 이점을 활용합니다. 문자열을 다시 할당하자마자 overwrite은 유효하지 않으며 컴파일러는이를 휴지통으로 자유롭게 사용할 수 있습니다. 예를 들어 컴파일러가 임시 또는 매개 변수 전달을 위해 다시 할당하는 레지스터에있을 수 있습니다. 컴파일러가이 작업을 수행하든간에 객체의 수명이 끝나면 객체에 대한 포인터에 대한 표준이 매우 명확하기 때문에 정확히 이 될 수 있습니다.

+0

메모리가 다른 위치로 이동할 수있는 모든 경우에 대해 알고 있습니다. 그래서 내 코드가 포인터 값을 변경하는지 확인하고, 이런 경우에는 중단합니다. 그것은 또한 휴지통 바이트를 다시 채 웁니다. 내가 관심 있었던 마지막 단락이었습니다. 그러나 다른 메모리 블록에 대한 포인터 비교가 지정되지 않았기 때문에 할당 이동 여부에 대한 검사조차도 유효하지 않습니다. 하지만 정의되지 않은) 결과는 이론적 인 구현에서 if 문이 의미가 없음을 의미합니다. – Myria

-1

"유효한"정의에 따라 다릅니다. 당신은 상황을 완벽하게 묘사했습니다. "유효"하다고 생각하면 유효합니다. "유효"하다고 생각하지 않으려면 유효하지 않습니다. 당신이 표지 다음)으로 realloc을 (사용 다시 적어도 다시 블록을 성장 경우

+1

C 표준은 그 것이 유효하지 않다고 생각합니다. – Christoph

+0

@Christoph Nonsense. 다음을 고려하십시오 :'if (realloc (foo, 32) == foo) {/ * 여기} /}'. 물론, 'foo'는 realloc에 ​​전달되었지만 완벽하게 유효합니다. realloc에 ​​대한 후속 호출은 우리가 액세스하기 전에 동일한 포인터를 반환했다는 것을 기억하십시오. –

+1

다시 C 표준에 따르지 않는다 :'realloc()'이 실패하지 않는 한, 객체는 할당 해제되고, 수명이 끝나고 추상 언어 의미론에 관한 한 모든 포인터가 무효가된다. 확실히, 그것은 실제로 작동 할 것이지만 그것이 표준이 될 때까지는 유효하지 않습니다. – Christoph

0

오브젝트 유형은 허상 포인터가 가리키는하고, 어느 경우 realloc을했다() 메모리 블록을 이동, 유효 허상 포인터입니다 다시?

아니요. realloc()이 널 포인터를 반환하지 않는 한 호출은 할당 된 객체의 수명을 종료하므로 그 포인터를 가리키는 모든 포인터가 유효하지 않게됩니다. realloc()이 성공하면 개체의 주소를 반환합니다.

물론 이전 주소와 동일한 주소 일 수 있습니다. 이 경우, 낡은 오브젝트에의 무효 인 포인터를 사용해 새로운 오브젝트에 액세스하면 (자), 일반적으로는, 최적화되어 있지 않은 C 언어의 구현으로 동작합니다.

그래도 공격적으로 최적화 된 컴파일러에서는 여전히 정의되지 않은 동작이며 might actually fail입니다.

C 언어는 불완전하며 일반적으로 프로그래머가 불변량을 유지해야합니다. 그렇게하지 않으면 컴파일러와 함축적 인 계약이 깨져 부정확 한 코드가 생성 될 수 있습니다.

+0

"새 C 언어는 대부분의 C 언어 구현에서 작동합니다"- 실제로는 대부분의 구현이 해제 된 섹션을 수정하므로 실제로는 그렇지 않습니다. "C 언어는 불건전하다"- 사실! –

+0

@JimBalter : 객체의 * value *가 수정되었는지 여부에 관계없이 (그리고'free()'가 아닌'realloc()'에 대한 어떤 이유도 보이지 않습니다 - 그렇게하려면 이 특정 예에서는 메타 데이터가 일반적으로 앞쪽에 유지되는 반면 후미 바이트에 대해서는 설명합니다. * 포인터 *는 개체 내의 올바른 위치를 가리 킵니다 – Christoph

+0

Ahem. 블록을 더 작은 크기 (블록의 끝을 자유롭게 함)로 재 할당하면 두 개의 블록이 있고 두 번째 블록의 앞면은 원래 큰 블록의 중간에 있습니다 ... 다시 할당하면 메타 데이터. "포인터가 객체 내의 올바른 위치를 가리 킵니다"- 그렇지 않거나 다를 수 있습니다. realloc 쌍이 동일한 메모리를 다시 확보한다는 보장은 없습니다. 더 작은 크기로 다시 할당하더라도 전혀 다른 메모리를 반환 할 수 있습니다. –

관련 문제