이 구조는 어떻게 sizeof == 0을 가질 수 있습니까?


80

sizeof반환 할 구성을 요청하는 오래된 게시물이 있습니다 0. 표준에 따라 어떤 유형이나 변수도 0의 크기를 가질 수 없다는 높은 평판 사용자의 높은 점수 답변이 있습니다. 그리고 저는 그것에 100 % 동의합니다.

그러나이 솔루션을 제시하는 새로운 답변 이 있습니다.

struct ZeroMemory {
    int *a[0];
};

나는 막 반대표를 던지고 댓글을 달려고했지만 여기서 보내는 시간은 내가 100 % 확신하는 것조차 확인하는 법을 배웠다. 그래서 ... 놀랍게도 모두 gccclang동일한 결과를 보여줍니다 sizeof(ZeroMemory) == 0. 더욱이 변수의 크기는 0다음과 같습니다.

ZeroMemory z{};
static_assert(sizeof(z) == 0); // Awkward...

뭐 ...?

Godbolt 링크

이것이 어떻게 가능한지?


37
크기가 0 인 배열은 표준 C ++가 아닙니다. 그러나 확장.
Jarod42

@ Jarod42 이제 분명합니다.
bolov

1
또한 MSVC에서 컴파일되지 않습니다
UnholySheep

7
이제 배열에 넣고 포인터 산술을 수행하십시오.
CodesInChaos 2017

흠 ... 0이 아니라면 몇 바이트를 차지하지 않나요?
Gerhardh

답변:


52

C가 표준화되기 전에는 코드에서 크기가 0 인 유형에 대한 포인터 하나를 다른 유형에서 빼려고 시도하지 않는 한 많은 컴파일러가 크기가 0 인 유형을 처리하는 데 어려움이 없었을 것입니다. 이러한 유형은 유용했으며 지원하는 것이 금지하는 것보다 쉽고 저렴했습니다. 그러나 다른 컴파일러는 이러한 유형을 금지하기로 결정했으며 일부 정적 어설 션 코드는 코드가 크기가 0 인 배열을 만들려고하면 삐걱 거리는 사실에 의존했을 수 있습니다. 표준 작성자는 다음과 같은 선택에 직면했습니다.

  1. 이러한 선언의 목적이 진단을 트리거하고 컴파일을 중단하는 경우에도 컴파일러가 크기가 0 인 배열 선언을 자동으로 수락하도록 허용하고 모든 컴파일러가 크기가 0 인 개체를 생성하는 것처럼 이러한 선언을 수락하도록 요구합니다 (반드시 침묵하지는 않음). .

  2. 이러한 선언의 목적이 진단을 트리거하고 컴파일을 중단하는 경우에도 컴파일러가 크기가 0 인 배열 선언을 자동으로 받아들이도록 허용하고 이러한 선언을 접하는 컴파일러가 컴파일을 중단하거나 여유 시간에 계속할 수 있도록합니다.

  3. 코드가 크기가 0 인 배열을 선언하는 경우 구현에서 진단을 발행하도록 요구하지만, 구현에서 컴파일을 중단하거나 (적합하다고 생각하는 의미에 관계없이) 계속 진행할 수 있습니다.

표준의 저자는 # 3을 선택했습니다. 결과적으로 표준이 금지하기 전에 이러한 구성이 널리 지원 되었음에도 불구하고 표준 "확장"에서 크기가 0 인 배열 선언을 간주합니다.

C ++ 표준은 빈 개체의 존재를 허용하지만 빈 개체의 주소를 토큰으로 사용할 수 있도록하기 위해 최소 크기 1을 요구합니다. 구성원이없는 개체의 경우 크기는 따라서 0은 표준을 위반합니다. 그러나 개체에 크기가 0 인 멤버가 포함 된 경우 C ++ 표준은 이러한 선언을 포함하는 프로그램이 진단을 트리거해야한다는 사실 외에는 처리 방법에 대한 요구 사항을 부과하지 않습니다. 이러한 선언을 사용하는 대부분의 코드는 결과 객체의 크기가 0 일 것으로 예상하므로 이러한 코드를 수신하는 컴파일러에게 가장 유용한 동작은 이러한 방식으로 처리하는 것입니다.


"C가 표준화되기 전"이란 C99가 존재하기 전을 의미합니까?
Stargateur 2017

1
@Stargateur : C가 발명되고 C89 표준이 작성 될 때까지 대략 15 년이 소요됩니다. C99는 크기가 0 인 배열의 금지 문제를 해결하는 데 필요한 일부 kludges를 피하기 위해 제한된 형태의 0 크기 개체를 다시 추가했지만 C89가 0 크기 배열을 성가 시게 금지하지 않았다면 이러한 kludges는 필요하지 않았을 것입니다. 첫 번째 장소.
supercat

"이러한 유형은 유용했습니다."어떻게 유용 했습니까?
user109923

1
@ user109923 : 예 : struct polygon { int count; POINT sides[0];}; struct { struct polygon poly; POINT pts[3]; } myTriangle = { {3}, {{1,1},{2,2},{2,1} };. 정렬이 문제를 일으키지 않도록주의해야하지만, 서로 바꿔서 처리 할 수있는 다양한 크기의 정적 지속 시간의 개체가있을 수 있습니다.
supercat

42

Jarod42가 지적한 대로 크기가 0 인 배열은 표준 C ++가 아니라 GCC 및 Clang 확장입니다.

추가 -pedantic하면 다음 경고가 생성됩니다.

5 : <source>:5:12: warning: zero size arrays are an extension [-Wzero-length-array]
    int *a[0];
           ^

난 항상 잊어 버려 std=c++XX(대신std=gnu++XX ) 모든 확장 기능을 비활성화하지 않는다는 .

이것은 여전히 sizeof동작을 설명하지 않습니다 . 하지만 적어도 우리는 그것이 표준이 아니라는 것을 알고 있습니다.


1
어느 쪽이든 빈 구조체조차도 0이 아닌 sizeof 값을 생성해야하므로 놀랍습니다 ...
WF

2
흥미롭게도 두 개의 자동 인스턴스를 생성하면 두 개의 인스턴스가 sizeof(int*)따로 저장됩니다 (내 생각에는) : coliru.stacked-crooked.com/a/e9a3038bf587b65c
eerorika

9
이것은 크기가 0 인 배열이 표준이 아니라는 답변 만 제공하지만 질문 한 내용을 설명하지 않습니다.
haccks

1
이 동작은 표준에 따라 어떤 식 으로든 의미가있을 필요는 없습니다. 컴파일러 구현 자에게만 의미가 있으면됩니다. 컴파일러는 이미 표준에서 금지하는 방식으로 유형을 정의 할 수 있도록 허용하고 있습니다. 따라서 sizeof해당 유형에 대한 결과에 대한 표준의 요구 사항 도 적용되지 않습니다. 따라서 동작은 컴파일러 개발자가 내린 결정입니다.
Peter

1
아이디어는 ZeroMemory* z = (ZeroMemory*)malloc(sizeof(ZeroMemory) + 2 * sizeof(int*)); z.a[1] = new int{1}; /* or whatever, just use indices into a to access dynamic memory after the previous members (in this case: none) */물론,이 호환 표준을하지 않습니다! (예를 들어 구조체로 캐스팅하여 동적 길이 네트워크 메시지를 쉽게 구문 분석하는 데 사용할 수 있습니다.)
hoffmale

19

C ++에서 크기가 0 인 배열은 불법입니다.

ISO / IEC 14882 : 2003 8.3.4 / 1 :

[..] 상수 표현식 (5.19)이 존재 하면 정수 상수 표현식이고 그 값은 0보다 커야한다 . 상수 표현식은 배열의 경계 (요소 수)를 지정합니다. 상수 식의 값이있는 경우 N, 어레이 갖는다 N번째 요소 0N-1, 그리고 상기 식별자의 종류 D인 " 유도 - 선언자 형리스트 배열 NT를". [..]

g ++에서는 -pedantic크기가 0 인 배열에 경고를 표시 하는 플래그가 필요합니다 .


5

길이가 0 인 배열은 GCC 및 Clang의 확장입니다. sizeof길이가 0 인 배열에 적용 하면 0으로 평가됩니다 .

C ++ 클래스 (비어 있음)는 size를 가질 수 없지만 0클래스 ZeroMemory가 비어 있지 않습니다. 크기가있는 명명 된 멤버가 0있으며 적용 sizeof하면 0이 반환됩니다.


5
그래서 ... 빈 클래스는 크기를 가질 수 없지만 0비어 있지 않은 클래스는 크기를 가질 수 있습니다 0... 이것은 의미가 없습니다. 그러나 이것이 비표준 확장으로 얻는 충돌의 종류라고 생각합니다.
bolov

@bolov; C ++ 클래스 (비어 있음)는 크기가 0 일 수 없습니다 . 빈 클래스의 표준에 따릅니다. C ++ 표준에 따르면 size 구조체를 만드는 다른 방법이 없기 때문입니다 0. 이 특별한 경우에, struct it self의 멤버는 크기가 0이고 이것은 struct 0의 크기를 만듭니다. 혼란 스럽지만 설명하려고 최선을 다했습니다.
haccks

3
@bodov :하지만 이것은 C ++ 클래스가 아니고 G ++ 클래스이므로 C ++ 규칙을 적용 할 필요가 없습니다. 특히 이러한 유형의 배열을 가질 수 없거나 포인터로 수학을 수행 할 수 없으므로 크기가 0이 될 수 없다는 일반적인 추론도 마찬가지입니다.
Ben Voigt 2017
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.