83

There is an old post asking for a construct for which sizeof would return 0. There are some high score answers from high reputation users saying that by the standard no type or variable can have sizeof 0. And I agree 100% with that.

However there is this new answer which presents this solution:

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

I was just about to down-vote and comment on it, but time spent here taught me to check even the things that I am 100% sure on. So... to my surprise both gcc and clang show the same results: sizeof(ZeroMemory) == 0. Even more, sizeof a variable is 0:

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

Whaaaat...?

Godbolt link

How is this possible?

bolov
  • 72,283
  • 15
  • 145
  • 224
  • 37
    Zero size array is not standard C++. but extension. – Jarod42 Nov 17 '17 at 14:16
  • @Jarod42 now it's obvious. – bolov Nov 17 '17 at 14:17
  • 1
    Also it doesn't compile under MSVC – UnholySheep Nov 17 '17 at 14:18
  • 7
    Now put them in an array and do pointer arithmetic on them. – CodesInChaos Nov 17 '17 at 17:58
  • Hmmm... How many bytes should nothing take if not 0? – Gerhardh Nov 17 '17 at 20:54
  • @Gerhardh An empty structure has non zero size in C/C++. – curiousguy Nov 18 '17 at 04:08
  • @curiousguy Does it really make sense to stick to some requirement from the standard if you add a non standard extension? While sizeof==0 might be non standard, it is what I would expect from such a beast. – Gerhardh Nov 18 '17 at 06:16
  • @curiousguy: The C Standard requires that structures be non-empty; it says nothing about the size of structures violating that constraint. Because empty structures were meaningful in C++, it was necessary so specify how they should behave. Given a choice between allowing objects to have zero size or requiring that empty structs have non-zero size, the design of C++ (unfortunately, IMHO) opted for the latter approach. – supercat Nov 21 '17 at 18:37

4 Answers4

51

Before C was standardized, many compilers would have had no difficulty handling zero-size types as long as code never tried to subtract one pointer to a zero-size type from another. Such types were useful, and supporting them was easier and cheaper than forbidding them. Other compilers decided to forbid such types, however, and some static-assertion code may have relied upon the fact that they would squawk if code tried to create a zero-sized array. The authors of the Standard were faced with a choice:

  1. Allow compilers to silently accept zero-sized array declarations, even in cases where the purpose of such declarations would be to trigger a diagnostic and abort compilation, and require that all compilers accept such declarations (though not necessarily silently) as producing zero- sized objects.

  2. Allow compilers to silently accept zero-sized array declarations, even in cases where the purpose of such declarations would be to trigger a diagnostic and abort compilation, and allow compilers encountering such declarations to either abort compilation or continue it at their leisure.

  3. Require that implementations issue a diagnostic if code declares a zero-sized array, but then allow implementations to either abort compilation or continue it (with whatever semantics they see fit) at their leisure.

The authors of the Standard opted for #3. Consequently, zero-sized array declarations are regarded by the Standard "extension", even though such constructs were widely supported before the Standard forbade them.

The C++ Standard allows for the existence of empty objects, but in an effort to allow the addresses of empty objects to be usable as tokens it mandates that they have a minimum size of 1. For an object that has no members to have a size of 0 would thus violate the Standard. If an object contains zero-sized members, however, the C++ Standard imposes no requirements about how it is processed beyond the fact that a program containing such a declaration must trigger a diagnostic. Since most code that uses such declarations expects the resulting objects to have a size of zero, the most useful behavior for compilers receiving such code is to treat them that way.

supercat
  • 77,689
  • 9
  • 166
  • 211
  • "Before C was standardized", you mean before C99 exist ? – Stargateur Nov 21 '17 at 17:24
  • 1
    @Stargateur: I mean the roughly 15 years between when C was invented and when the C89 standard was written. C99 added back a limited form of zero-size object to avoid some of the kludges necessary to work around its banning of zero-size arrays, but such kludges wouldn't have been necessary if C89 hadn't annoyingly forbidden zero-size arrays in the first place. – supercat Nov 21 '17 at 18:26
  • "Such types were useful" How were they useful? – user109923 Jul 11 '19 at 23:08
  • 1
    @user109923: As an example: `struct polygon { int count; POINT sides[0];}; struct { struct polygon poly; POINT pts[3]; } myTriangle = { {3}, {{1,1},{2,2},{2,1} };`. One would need to use caution to ensure alignment didn't cause issues, but one could have objects of static duration of different sizes that could be processed interchangeably. – supercat Jul 11 '19 at 23:16
43

As pointed out by Jarod42 zero size arrays are not standard C++, but GCC and Clang extensions.

Adding -pedantic produces this warning:

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

I always forget that std=c++XX (instead of std=gnu++XX) doesn't disable all extensions.

This still doesn't explain the sizeof behavior. But at least we know it's not standard...

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
bolov
  • 72,283
  • 15
  • 145
  • 224
  • 1
    Either way it is surprising as even an empty struct should produce non-zero sizeof value... – W.F. Nov 17 '17 at 14:27
  • yes, and even more surprising to me is that sizeof variable is 0: `ZeroMemory z{};` `sizeof(z) == 0` – bolov Nov 17 '17 at 14:32
  • 2
    Interestingly, if you create two automatic instances, they are stored `sizeof(int*)` apart (I assume): http://coliru.stacked-crooked.com/a/e9a3038bf587b65c – eerorika Nov 17 '17 at 14:37
  • 9
    This only answers that zero size array are non standard but it doesn't explain the question you asked. – haccks Nov 17 '17 at 16:23
  • @haccks How doesn't it? It is impossible to have 0 sized data in C++, but C++ with extensions is no longer C++ so the puzzle is gone. – Yakk - Adam Nevraumont Nov 17 '17 at 17:03
  • 1
    The behaviour doesn't have to make sense in any way according to the standard - it only needs to make sense to the compiler implementers. The compiler is already allowing a type to be defined in a way that is forbidden by the standard. As such, requirements from the standard on what the result of `sizeof` gives for that type also do not apply. The behaviour is therefore a decision made by the compiler developers. – Peter Nov 17 '17 at 22:12
  • 1
    The idea is `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) */` Of course, this is not standard compatible! (This can be used for easily parsing dynamic length network messages by casting to a struct, for example). – hoffmale Nov 18 '17 at 06:44
19

In C++, a zero-size array is illegal.

ISO/IEC 14882:2003 8.3.4/1:

[..] If the constant-expression (5.19) is present, it shall be an integral constant expression and its value shall be greater than zero. The constant expression specifies the bound of (number of elements in) the array. If the value of the constant expression is N, the array has N elements numbered 0 to N-1, and the type of the identifier of D is “derived-declarator-type-list array of N T”. [..]

g++ requires the -pedantic flag to give a warning on a zero-sized array.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
msc
  • 33,420
  • 29
  • 119
  • 214
6

Zero length arrays are an extension by GCC and Clang. Applying sizeof to zero-length arrays evaluates to zero.

A C++ class (empty) can't have size 0, but note that the class ZeroMemory is not empty. It has a named member with size 0 and applying sizeof will return zero.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
haccks
  • 104,019
  • 25
  • 176
  • 264
  • 5
    so... an empty class cannot have size `0`, but a non-empty class can have size `0`... this doesn't make much sense. But I guess this is the kind of conflicting edge case you get with non-standard extensions. – bolov Nov 17 '17 at 17:49
  • @bolov; *A c++ class (empty) can't have size 0* is as per the standard for empty class. C++ standard says that because there is not other way to make a struct of size `0`. In this particular case, member of struct it self is of size 0 and this makes the size of struct 0. I know its confusing but I tried my best to explain. – haccks Nov 17 '17 at 17:57
  • 3
    @bodov: But this isn't a C++ class, it's a G++ class, so the C++ rules don't have to apply to it. Particularly note that you can't have an array of this type or do math with its pointers, so the usual reasoning that the size can't be zero doesn't hold either. – Ben Voigt Nov 18 '17 at 04:31