차이 C++에서 크기가 0 인 할당을 위해 고유 한 주소를 반환하는 이유는 무엇입니까?




c++ 어려움 (2)

그 이유는 코드가 경계 조건을 특별히 처리 할 필요가 없어야하기 때문입니다. 대부분, 알고리즘은 경계 조건으로 크기가 0 인 객체를 처리해야합니다. 객체에 대한 포인터를 비교하여 동일한 객체인지 확인하는 알고리즘은 덜 일반적이지만 크기가 0 인 객체에도 여전히 작동해야합니다.

그러나 귀하의 질문은 이것이 변화라고 가정합니다. 1980 년대 후반의 짧은 틈을 제외하고 내가 알고있는 모든 C 및 C ++ 구현은 항상 이와 같이 행동했습니다.

dmr에 의한 원래의 C 컴파일러는 이와 같이 동작하지만, 1987 년경에는 C 표준에 의해 0 크기의 객체의 malloc이 NULL을 반환하도록 지정되었습니다. 이것은 정말 기괴한 것이었고 최종 C-89 표준조차도 구현 정의로 만들었지 만 이후이 끔찍한 일을 한 적이 없습니다.

나는 "Malloc Madness"섹션의 블로그 에서 이에 대해 더 이야기한다.

C ++에서 크기가 0 인 할당을 위해 고유 한 주소를 반환하는 이유는 무엇입니까?

배경 : C11 표준에서 malloc (7.20.3 메모리 관리 기능)에 대해 말합니다.

요청 된 공간의 크기가 0이면 동작은 구현 정의됩니다. null 포인터가 반환되거나 반환 된 포인터가 객체에 액세스하는 데 사용되지 않는다는 점을 제외하고는 크기가 0이 아닌 값인 것처럼 동작합니다.

즉, 내가 본 것처럼 malloc 은 0 크기 할당의 포인터로 할 수있는 이후로 항상 0 크기 할당에 성공합니다. 그 이유는 0 크기 할당의 경우 free 와 같은 다른 메모리 할당 함수를 호출하기 때문입니다.

  • mallocNULL 반환 NULL free(NULL) 는 ok이므로 성공이라고 간주 될 수 있습니다.
  • 그것은 다른 값을 반환하는 경우, 그것은 또한 성공 (그것은 NULL 이 아니기 때문에), 유일한 조건은 또한 값이 작동해야합니다 free 입니다.

또한 C11 (7.20.3도)은 malloc에서 반환 된 주소가 고유해야한다는 것을 지정하지 않고 분리 된 메모리 영역을 가리켜 야한다는 점만 지정합니다.

할당이 성공할 경우 반환되는 포인터는 모든 유형의 객체에 대한 포인터에 할당 된 다음 할당 된 공간에서 그러한 객체 또는 배열에 액세스하는 데 사용되도록 적절하게 정렬됩니다 (공간이 명시 적으로 할당 취소 될 때까지) . 할당 된 객체의 수명은 할당에서 할당 해제까지 확장됩니다. 그러한 각 할당은 다른 객체와 분리 된 객체에 대한 포인터를 산출해야한다.

크기가 0 인 모든 객체는 분리 된 AFAICT이며, 이는 malloc 이 여러 개의 0 크기 할당 (예 : NULL 은 괜찮을 수 있음) 또는 매번 다른 포인터 또는 일부 포인터와 같은 포인터를 반환 할 수 있음을 의미합니다.

그런 다음 C ++ 98에는 두 가지 원시 메모리 할당 함수가 있습니다.

void* operator new(std::size_t size);
void* operator new(std::size_t size, std::align_val_t alignment);

이 함수는 원시 메모리 만 반환합니다. 즉, AFAICT 유형의 객체를 만들거나 초기화하지 않습니다.

당신은 이것을 다음과 같이 부릅니다.

#include <iostream>
#include <new>
int main() {
    void* ptr = operator new(std::size_t{0});
    std::cout << ptr << std::endl;
    operator delete(ptr, std::size_t{0});
    return 0;
}

C ++ 17 표준의 [new.delete.single] 섹션에서는 이들을 설명하지만, 핵심적인 보증은 [basic.stc.dynamic.allocation] .

요청 된 공간의 크기가 0 인 경우에도 요청이 실패 할 수 있습니다. 요청이 성공하면 반환 된 값은 이전에 반환 된 값 p1과 다른 null이 아닌 포인터 값 (7.11) p0이됩니다. 단, 그 값 p1이 이후에 연산자 삭제에 전달되지 않는 한. 또한 21.6.2.1과 21.6.2.2의 라이브러리 할당 함수의 경우 p0는 호출자가 액세스 할 수있는 다른 객체에 대한 저장소와 분리 된 저장소 블록의 주소를 나타냅니다. 크기가 0 인 요청으로 반환 된 포인터를 통한 간접 효과는 정의되지 않았습니다.

즉, 성공할 때마다 항상 다른 포인터를 반환해야합니다. 그건 malloc 에서 조금 바뀐 것입니다.

내 질문입니다 : 이 변화의 근거는 무엇입니까? (즉, C ++에서 크기가 0 인 할당에 대한 고유 주소를 반환하는 것)

이상적으로 대답은 대체물을 탐구하고 그 의미를 자극 한 종이 (또는 다른 소스)에 대한 링크 일뿐입니다. 일반적으로 C ++의 디자인과 진화를 위해이 C ++에 대한 질문을하고 있지만 Section 10 (메모리 관리)에는 그것에 대해 언급하지 않았습니다. 그렇지 않으면 권위있는 참고 문헌이 좋을 것입니다.

면책 조항 : 나는 reddit에 질문 했지만 충분히 유용하지 않은만큼 충분히 묻지 않았습니다. 가설 만 갖고 있다면 대답으로 게시 할 수 있지만 가설 일 뿐이라는 점을 자유롭게 생각해 주시기 바랍니다.

또한 reddit 사람들은 표준을 변경하라는 제안이 있든 없든간에 0 크기의 형식을 계속 사용했습니다.이 질문은 크기가 0 인 경우 원시 메모리 할당 함수의 의미에 대한 것입니다. 제로 크기 유형과 같은 주제가 귀하의 답변과 관련이 있다면 포함 시키십시오! 그러나 접선 문제로 너무 탈선하지 않도록 노력하십시오.

또한, reddit 사람들은 "더 이상 구체적인 것을 언급 할 필요가없는"최적화 목적을위한 주장을 던졌습니다. 나는 "최적화 때문에"답안에서보다 구체적인 무엇인가를 기대합니다. 예를 들어, 한 Redditor는 별칭 지정 최적화에 대해 언급했지만 참조 해제 할 수없는 포인터에 적용되는 별칭 최적화의 종류는 누구에게도 설명 할 수 없었습니다. 따라서 최적화에 대해 언급 할 예정이라면이를 보여주는 작은 예제가 토론을 풍부하게 만들 것입니다.


문제는 C ++의 객체 (크기에 관계없이) 가 고유 한 ID를 가져야한다는 것 입니다. 동일한 크기로 비교되는 두 개의 포인터가 동일한 객체를 가리 키도록 가정되므로 다른 공존 객체 (크기에 관계없이)가 다른 주소를 가져야 합니다 .

크기가 0 인 객체가 동일한 주소를 가질 수 있음을 인정하면 두 주소가 같은 객체인지 아닌지를 더 이상 구분할 수 없습니다.

"새로운 개체가 반환되지 않습니다"문제에 대한 많은 의견이 있습니다.

이 문맥에서 OOP 용어를 잊어 버리십시오.

C ++ 명세에는 "객체"라는 단어가 무엇을 의미하는지에 대한 정확한 정의가 있습니다.

CPP 참조 : 개체

특히:

C ++ 프로그램은 객체를 생성, 삭제, 참조, 액세스 및 조작합니다. C ++의 객체는 다음과 같은 저장소 영역입니다.

  • 크기 (sizeof로 결정될 수 있음);
  • 정렬 요구 사항 (alignof를 사용하여 결정할 수 있음).
  • 저장 기간 (자동, 정적, 동적, 스레드 로컬);
  • 일생 (저장 기간 또는 임시로 경계);
  • 유형;
  • 값 (예 : 불변 일 수도 있음, 예 : 기본 초기화 된 비 클래스 유형).
  • 선택적으로 이름.

다음 엔터티는 값, 참조, 함수, 열거 자, 형식, 비 정적 클래스 멤버, 비트 필드, 템플릿, 클래스 또는 함수 템플릿 특수화, 네임 스페이스, 매개 변수 팩 및이 개체와 같은 개체가 아닙니다.

변수는 선언에 의해 도입 된 비 정적 데이터 멤버가 아닌 객체 또는 참조입니다.

객체는 정의, 새 표현식, throw 표현식, 공용체의 활성 멤버 변경시 및 임시 객체가 필요한 경우에 만들어집니다.





new-operator