C ++ new int [0] — 메모리를 할당합니까?


241

간단한 테스트 앱 :

cout << new int[0] << endl;

출력 :

0x876c0b8

따라서 작동하는 것처럼 보입니다. 표준은 이것에 대해 무엇을 말합니까? 빈 메모리 블록을 "할당"하는 것이 항상 합법적인가요?


4
+1 매우 흥미로운 질문-실제 코드에서 얼마나 중요한지 잘 모르겠습니다.
Zifre

37
@Zifre : 호기심을 요구하지만 실제로 할당 된 메모리 블록의 크기가 어떤 식으로 계산되고 계산 결과가 0 일 때 예외를 추가 할 필요가없는 경우 실제로 중요 할 수 있습니다 크기가 0 인 블록은 할당하지 않습니다. 오류없이 할당 및 삭제해야하기 때문에 (크기가 0 인 블록 만 역 참조되지 않는 경우) 따라서 일반적으로 이것은 메모리 블록이 무엇인지에 대한 추상화를 넓 힙니다.

2
@ emg-2 : 예제 상황에서는 delete []가 NULL 포인터에서 완벽하게 합법적이기 때문에 실제로 중요하지 않습니다 :-).
Evan Teran

2
접선 적으로 만 관련되어 있으므로 여기에 언급하고 있습니다.하지만 C ++은 여러 가지 방법으로 고유 한 객체가 명시 적으로 스토리지가 필요하지 않더라도 고유 한 주소를 갖도록합니다. 관련 실험은 빈 구조체의 크기를 확인하는 것입니다. 또는 그 구조체의 배열입니다.
Drew Dormann

2
Shmoopty의 의견을 자세히 설명하려면 : 특히 템플릿 (예 : std :: allocator와 같은 정책 클래스 템플릿)을 사용하여 프로그래밍 할 때 크기가 0 인 객체를 갖는 것이 C ++에서 일반적입니다. 일반 코드는 이러한 객체를 동적으로 할당하고 객체 ID를 비교하기 위해 포인터를 사용해야합니다. 이것이 연산자 new ()가 크기가 0 인 요청에 대해 고유 한 포인터를 반환하는 이유입니다. 논쟁의 여지가 덜 중요하지만 공통적 인 이유는 배열 할당 및 연산자 new [] ()에 동일한 추론이 적용됩니다.
Trevor Robinson

답변:


233

5.3.4 / 7에서

direct-new-declarator의 표현식 값이 0이면, 할당 함수가 호출되어 요소가없는 배열을 할당합니다.

3.7.3.1/2에서

크기가 0 인 요청으로 반환 된 포인터를 역 참조하는 효과는 정의되어 있지 않습니다.

또한

새로 요청한 공간의 크기가 0 인 경우에도 요청이 실패 할 수 있습니다.

즉, 할 수는 있지만 합법적으로 (모든 플랫폼에서 잘 정의 된 방식으로) 얻은 메모리를 참조 해제 할 수는 없습니다. 배열 삭제에만 전달할 수 있으므로 삭제해야합니다.

3.7.3.1/2의 문장에 첨부 된 흥미로운 각주 (표준의 규범적인 부분은 아니지만 설명 목적으로 포함)

[32. 의도는 malloc () 또는 calloc ()을 호출하여 연산자 new ()를 구현할 수 있도록하는 것이므로 규칙은 실질적으로 동일합니다. C ++은 널이 아닌 포인터를 리턴하기 위해 제로 요청이 필요하다는 점에서 C와 다릅니다.]


삭제하지 않으면 메모리 누수가 발생합니다. 예상 되나요? 적어도 나는 기대하지 않았다.
EralpB

12
@EralpB : 반대로, 요청이 0이더라도이 할당은 힙에서 발생합니다. 한 요청은 할당 자에 의해 지정된 영역 전후에 힙 가드 할당 및 초기화, 프리리스트에 삽입 또는 다른 복잡한 끔찍한 구조. 해방한다는 것은 거꾸로 부기를하는 것을 의미합니다.
v.oddou 2016 년

3
@ EralpB 예 크기에 관계없이 a new[]와 균형을 맞출 때마다 메모리 누수가 발생할 수 있다고 생각합니다 delete[]. 특히, 호출 new[i]할 때 배열의 크기를 저장하기 위해 할당하려는 메모리보다 약간 더 많은 메모리가 필요합니다 (나중에 delete[]할당 해제 할 때 사용됨 )
pqnet

24

예, 이와 같이 0 크기의 배열을 할당하는 것이 합법적입니다. 그러나 삭제해야합니다.


1
이에 대한 인용이 있습니까? 우리 모두 int ar[0];는 새로운 것이 왜 불법 이라는 것을 알고 있습니다 .
Motti

4
크기가 0 인 객체를 금지하여 C ++이 그렇게 강력하지 않다는 것은 흥미 롭습니다. 빈 기본 클래스 최적화를 생각해보십시오. 여기에서도 빈 기본 클래스 하위 오브젝트의 크기는 0 일 수 있습니다. 대조적으로, C 표준은 크기가 0 인 객체가 생성되지 않도록하기 위해 많은 노력을 기울입니다. malloc (0)을 정의 할 때, 예를 들어 malloc을 0이 아닌 인수로 실행하면 그 효과가 나타납니다. 그리고 struct {...; T n []; }; 배열 (FAM)에 할당 된 공간이 없으면 "n"에 요소가 하나있는 것처럼 동작합니다. (두 경우 모두 xn [0]과 같이 어떤 식 으로든 객체를 사용하는 것은 UB입니다.)
Johannes Schaub-litb

1
나는 sizeof (type)결코 0을 반환하지 않을 것이라고 생각 합니다. 예를 들면 다음과 같습니다. stackoverflow.com/questions/2632021/can-sizeof-return-0-zero
pqnet

소위 "빈 기본 클래스 최적화"조차도 모든 객체 (객체 내의 모든 바이트와 반대)가 고유 한 주소를 가져야한다는 주장 때문에 관련이 있습니다. 크기가 0이 아닌 고유 주소를 갖는 객체를 실제로 처리하는 코드가 필요한 경우 C ++이 더 간단 해졌습니다.
supercat

15

표준은 이것에 대해 무엇을 말합니까? 빈 메모리 블록을 "할당"하는 것이 항상 합법적인가요?

모든 객체에는 고유 한 ID, 즉 고유 한 주소가 있으며 길이가 0이 아닌 것을 의미합니다 (0 바이트를 요청하면 실제 메모리 양이 자동으로 증가 함).

이러한 개체 중 하나 이상을 할당 한 경우 주소가 다른 것을 알 수 있습니다.


"모든 객체는 고유 한 ID, 즉 길이가 0이 아닌 고유 한 주소를가집니다"-맞지만 틀립니다. 개체에 주소가 있지만 개체에 대한 포인터가 임의의 메모리를 가리킬 수 있습니다. "0 바이트를 요청하면 실제 메모리 양이 자동으로 증가합니다"-확실하지 않습니다. operator []또한 어딘가에 배열의 크기를 저장합니다 ( isocpp.org/wiki/faq/freestore-mgmt#num-elems-in-new-array 참조 ). 따라서 구현시 데이터와 함께 바이트 수를 할당하면 바이트 수와 데이터에 0 바이트를 할당하여 1-last-last 포인터를 반환합니다.
Steed

0이 아닌 길이의 할당 가능한 메모리가 아닌 0이 아닌 길이의 할당 된 메모리 내 요점은 두 개의 별개의 객체에 대한 두 개의 포인터가 동일한 주소를 가리켜서는 안된다는 것입니다.
ChrisW

1
표준 ( open-std.org/jtc1/sc22/wg21/docs/papers/2012/n3337.pdf )은 ( 3.7.4.1/2 ) 실제로 다른 호출 operator new[]은 다른 포인터를 반환해야 한다고 말합니다 . BTW new expression에는 몇 가지 추가 규칙 (5.3.4)이 있습니다. new크기가 0 인 것이 실제로 할당 해야하는 단서를 찾을 수 없었습니다. 죄송합니다. 귀하의 답변이 질문에 대한 답변이 아니라 논란의 여지가있는 진술을 제공하고 있음을 알게 되었기 때문에 공감했습니다.
Steed

@ 블록의 길이를 저장하기위한 메모리를 내장하여 주소 공간을 사용하여 암시 적으로 계산할 수 있습니다. 예를 들어 크기가 0 인 배열의 경우 new[]구현시 동적 메모리가 매핑되지 않은 범위에서 주소를 반환 할 수 있으므로 주소 공간을 사용하는 동안 실제로 메모리를 사용하지 않습니다.
pqnet

@ pqnet : 좋은 품질의 구현은 무제한의 새로운 / 삭제주기를 허용해야합니까? 64 비트 포인터가있는 플랫폼에서 포인터 while(!exitRequested) { char *p = new char[0]; delete [] p; }를 재활용하지 않고 루프를 실행하는 컴퓨터 는 주소 공간이 부족하기 전에 먼지로 붕괴되지만 32 비트 포인터가있는 플랫폼에서는 훨씬 덜 합리적인 가정.
supercat

14

으로 0크기가 지정된 블록 을 할당하는 것은 완전히 합법적 입니다 new. 액세스 할 유효한 데이터가 없기 때문에 유용한 정보를 얻을 수 없습니다.int[0] = 5;불법입니다.

그러나 나는 표준 malloc(0)이 돌아 오는 것과 같은 것들을 허용한다고 믿습니다.NULL .

delete []할당에서 다시 얻는 포인터 에도 여전히 필요합니다 .


5
malloc에 ​​관해서는 당신이 옳습니다. 구현이 정의되어 있습니다. 이것은 종종 잘못된 기능으로 간주됩니다.

흥미로운 질문은 다음과 같습니다 .0의 크기가 주어지면 새로운 nothrow 버전이 NULL을 반환 할 수 있습니까?
Evan Teran

1
new와 malloc의 의미는 적어도 표준에 의해 어떤 식 으로든 연결되어 있지 않습니다.

그들이 말하는 것은 아닙니다 ... 특별히 nothrow 버전의 새로운 것에 대해 묻고있었습니다.
Evan Teran

@Evan-new의 nothrow 버전은 요청이 실패한 경우에만 null을 반환합니다-요청 크기가 0 인 경우뿐만 아니라 @Neil-표준 적으로 연결되어 있지 않음-의도적으로 연결됨 주위에))-내 답변에 포함 된 각주 참조
Faisal Vali

1

흥미롭게도 C ++에서는 0 바이트가 요청 된 경우에도 new 연산자가 올바른 포인터를 반환해야합니다. (이 이상한 소리가 필요하면 언어의 다른 부분이 단순화됩니다.)

"항목 51 : 새로 작성 및 삭제시 규칙 준수" 에서 Effective C ++ Third Edition 이 이와 같이 말했습니다.


0

새로운 int [0]에서 테스트 한 이후 추가 공간이 필요하다는 것을 보증합니다.

예를 들어, 메모리 사용량

int **arr = new int*[1000000000];

보다 훨씬 작다

int **arr = new int*[1000000000];
for(int i =0; i < 1000000000; i++) {
    arr[i]=new int[0];
}

두 번째 코드 스 니펫의 메모리 사용량에서 첫 번째 코드 스 니펫의 메모리 사용량은 수많은 새로운 int [0]에 사용 된 메모리입니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.