Sunday, February 17, 2008

Zero-sized arrays

It is valid to do the below:

    T * ptr = new T[0];

0 is a perfectly valid input to the array form of new expression. What is the use of it, you may ask. No use as far as the array is concerned. What use is an array if it does not have any members? What can you possibly do with it? Nothing! Right, you cannot do anything with it but the statement above being valid is atleast a little bit helpful.

It allows you to have code like this:

void f(size_t n)
{
     int * ptr = new int[n];
     //...
     delete[] ptr;
}

instead of:

void f(size_t n)
{
     if (n>0)
     {
         int * ptr = new int[n];
         //...
         delete[] ptr;
     }
}

No other use though as said earlier, because since it has zero elements, you cannot do anything with it.

This reminds of another peculiar extension related to zero sized/unsized arrays. VC++ has an extension which makes use of zero sized arrays as the last member of structures. For example, see this:

#include<cstdlib>
#include<iostream>

struct test
{
     int cb;
     int buf[];
};

int main()
{
     test* bb;
     int length = 10;
     bb = (test*) malloc(sizeof(test)+sizeof(int)*length);
     bb->cb = length;
     //fill buf with length int items or can copy from another array for length elements
     bb->buf[i]=10; //i should be less than length
     //OR--------
     test aa = {10, {1,10,22,32}};
     std::cout << aa.buf[2];
}

Since the zero size member is in there, there are few restrictions as well. You cannot create an array of the test struct. You can mimic that though like this:

    //create an array of length 10 of test pointers
    test * ptr[10];
    //set each of the pointers to individual elements created as above

Generally, you would be better off keeping a pointer member but it is
worth noting the presence of such an extension. See for details :
Unsized arrays.

The rationale as per msdn is saving the runtime pointer dereferences and that is not specific to C. How big that overhead is, with respect to the application in order to make a choice to this extension, is a different story altogether. You could even implement std::vector<T> using this extension rather than having a pointer to type T! That is, std::vector<T> implementation with VC++ might use this extension but they don't do so. :-)

Interesting one, this one... isn't it? :-)

8 comments:

chris kruger said...

Actually when you create a zero sized array using new, it's the same as new'ing a scalar, or one item of the
array. That way you always get a valid pointer.

pradeep said...

wow such useful info..


http://www.money-issue.com

Anonymous said...

nice.. useful info.. thanks :)

Ripper

Anonymous said...

You can use it for compilation time assertions, like:
char test[(sizeof(sth1)==sizeof(sth2))-1];
It will generate size of array is negative error if sizes mismatch.

Alexey Lebedev said...

It's a useful fature, but if you're talking about C++ (not C), then it's not portable because ISO C++ forbids zero-size arrays. I know that almost all implementations supoprt that but it's still not in standard even in c++0x.

Onkar Rajopadhye said...

Although allocating 0 sized arrays is allowed, does it create some error (heap memory overflow, etc)?

I know that allocating 0 sized memory with malloc and copying something to that memory can create serious security threats.
Please refer: https://www.securecoding.cert.org/confluence/display/seccode/MEM04-C.+Do+not+perform+zero+length+allocations

Henning said...

Another interesting property is that in g++ this seems to be the only way to create a member that really has zero size. (sizeof an empty struct always seems to yield 1). If however a struct contains a zero-sized array, the size of the struct will also be zero.

So say you are writing a templated class and the user should be able to provide an allocator (or some other thingy).
So that would look like this:

template
struct Foo {
int x, y;
AllocRefT allocator;
};

Sometimes the user might have multiple allocators in use, in which case the allocator reference member is worth the effort, but often enough there will be only a single allocator object.

In the latter case you can save the space for the allocator reference by defining AllocRefT like this

struct AllocRefT {
int zeroer[0]; // makes struct size 0

static Something operator->(...);
static Something operator*(...);
};

this way your references take up no space although you can still call static methods on them.

Note (as said earlier) that this is not portable. Also it breaks in certain situations where the compiler will calculate the address of a member following a zero-sized member (so its better to have this as last member in the struct always).
To make this clear: I don't know if this behaviour of g++ is documented anywere or if it could be considered a bug of some sort. However I totally love it because you can really do awesome things with it :)

Spudo said...

I had a stackoverflow question on this: http://stackoverflow.com/questions/8271673/can-there-be-a-c-type-that-takes-0-bytes. They said that zero length arrays are illegal by the standard, but are a common extension. I used them under GCC, so they must be relatively common if GCC and VC++ support them.