この構造はどのようにして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...

わぁぁぁぁぁ…?

ゴッドボルトリンク

これはどのように可能ですか?


37
ゼロサイズの配列は標準のC ++ではありません。しかし、拡張。
jarod42 2017年

@ Jarod42今では明らかです。
bolov 2017年

1
また、MSVCではコンパイルされません
UnholySheep 2017年

7
次に、それらを配列に入れて、それらに対してポインター演算を実行します。
CodesInChaos 2017年

うーん... 0でない場合、何バイトを取るべきではありませんか?
Gerhardh 2017年

回答:


52

Cが標準化される前は、コードがゼロサイズ型へのポインタを別のポインタから減算しようとしない限り、多くのコンパイラはゼロサイズ型の処理に問題はありませんでした。そのようなタイプは有用であり、それらをサポートすることはそれらを禁止するよりも簡単で安価でした。ただし、他のコンパイラはそのような型を禁止することを決定しました。一部の静的アサーションコードは、コードがゼロサイズの配列を作成しようとするとスクォークするという事実に依存している可能性があります。標準の作成者は、次の選択肢に直面しました。

  1. コンパイラがゼロサイズの配列宣言をサイレントに受け入れることを許可します。そのような宣言の目的が診断をトリガーしてコンパイルを中止する場合でも、すべてのコンパイラがゼロサイズのオブジェクトを生成するものとしてそのような宣言を受け入れることを要求します(必ずしもサイレントではありません)。 。

  2. コンパイラーがゼロサイズの配列宣言をサイレントに受け入れることを許可します。そのような宣言の目的が診断をトリガーしてコンパイルを中止する場合でも、そのような宣言に遭遇したコンパイラーはコンパイルを中止するか、自由に続行できます。

  3. コードがゼロサイズの配列を宣言している場合、実装が診断を発行することを要求しますが、その後、実装がコンパイルを中止するか、(適切と思われるセマンティクスで)自由にコンパイルを続行できるようにします。

標準の作成者は#3を選択しました。その結果、ゼロサイズの配列宣言は、標準がそれらを禁止する前にそのような構造が広くサポートされていたとしても、標準の「拡張」によって見なされます。

C ++標準では、空のオブジェクトの存在が許可されていますが、空のオブジェクトのアドレスをトークンとして使用できるようにするために、最小サイズは1であることが義務付けられています。メンバーがないオブジェクトの場合、したがって、0は標準に違反します。ただし、オブジェクトにゼロサイズのメンバーが含まれている場合、C ++標準では、そのような宣言を含むプログラムが診断をトリガーする必要があるという事実以外に、オブジェクトの処理方法に関する要件はありません。このような宣言を使用するほとんどのコードは、結果のオブジェクトのサイズがゼロであることを想定しているため、このようなコードを受け取るコンパイラーにとって最も有用な動作は、それらをそのように扱うことです。


「Cが標準化される前」とは、C99が存在する前のことですか?
Stargateur

1
@Stargateur:Cが発明されてからC89標準が作成されるまでの約15年を意味します。C99は、ゼロサイズ配列の禁止を回避するために必要ないくつかの応急修理を回避するために、限定された形式のゼロサイズオブジェクトを追加し直しましたが、C89が迷惑なことにゼロサイズ配列を禁止していなければ、そのような応急修理は必要ありませんでした。そもそも。
スーパーキャット2017年

「そのようなタイプは役に立ちました」それらはどのように役に立ちましたか?
user1099 2319

1
@ user109923:例として:struct polygon { int count; POINT sides[0];}; struct { struct polygon poly; POINT pts[3]; } myTriangle = { {3}, {{1,1},{2,2},{2,1} };。位置合わせによって問題が発生しないように注意する必要がありますが、同じように処理できるさまざまなサイズの静的期間のオブジェクトを使用できます。
スーパーキャット

42

Jarod42が指摘しているように、ゼロサイズの配列は標準の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
いずれにせよ、空の構造体でもゼロ以外のsizeof値を生成するはずなので、驚くべきことです...
WF

2
興味深いことに、2つの自動インスタンスを作成すると、それらはsizeof(int*)別々に保存されます(私は推測します):coliru.stacked-crooked.com/a/e9a3038bf587b65c
eerorika

9
これは、ゼロサイズの配列が非標準であると答えるだけですが、あなたが尋ねた質問を説明するものではありません。
haccks 2017年

1
この動作は、標準に従って意味をなす必要はありません。コンパイラの実装者にとって意味があるだけです。コンパイラーは、標準で禁止されている方法で型を定義することをすでに許可しています。そのsizeofため、そのタイプの結果が何を与えるかに関する規格の要件も適用されません。したがって、この動作はコンパイラ開発者が決定したものです。
ピーター

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 2017年

19

C ++では、サイズがゼロの配列は無効です。

ISO / IEC 14882:2003 8.3.4 / 1:

[..]定数式(5.19)が存在する場合、それは整数定数式であり、その値はゼロより大きくなければなりません。定数式は、配列の境界(要素数)を指定します。定数式の値が、の場合、N配列にはにN番号が付けられ0た要素がN-1あり、の識別子の型Dは「Tの派生宣言子型リスト配列N」です。[..]

g ++では、-pedanticサイズがゼロの配列に警告を出すためのフラグが必要です。


5

長さゼロの配列は、GCCとClangによる拡張です。sizeofさがゼロの配列に適用すると、ゼロと評価されます

C ++クラス(空)はサイズを持つことができませんが0、クラスZeroMemoryは空ではないことに注意してください。サイズのある名前付きメンバーが0あり、適用するsizeofとゼロが返されます。


5
したがって...空のクラスはサイズを持つことができませんが0、空でないクラスはサイズを持つことができます0...これはあまり意味がありません。しかし、これは、非標準の拡張機能で発生する一種の競合するエッジケースだと思います。
bolov 2017年

@bolov; c ++クラス(空)のサイズを0にすることはできません。これは、空のクラスの標準に従います。C ++標準では、サイズの構造体を作成する方法は他にないため0です。この特定のケースでは、struct it自体のメンバーのサイズは0であり、これによりstruct 0のサイズになります。混乱することはわかっていますが、説明するために最善を尽くしました。
haccks 2017年

3
@bodov:しかし、これはC ++クラスではなく、G ++クラスであるため、C ++ルールを適用する必要はありません。特に、このタイプの配列を作成したり、そのポインターを使用して計算したりすることはできないため、サイズをゼロにできないという通常の理由も当てはまらないことに注意してください。
Ben Voigt 2017年
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.