22

EDIT: apparently some of this isn't allowed/has changed in various C standards. For my own hypothetical benefit, let's pretend we're using gcc test.c with no standard or warning options.

In particular I'm looking at the under-the-hood specifics. I've added my current understanding. Am I right?

char **c1;   //Size for a pointer is allocated on the stack. sizeof(c1) == sizeof(void*)
char *c2[0]; //Nothing is allocated on the stack.  sizeof(c2) == 0

is there some other difference between these two cases I'm not aware of (besides sizeof)?

struct a {
   int i;
   char c[0]; //sizeof(a) is sizeof(int)?  a.c == (&i)+1?
};

As I understand it, this is typically used for variable length arrays at the end of structures. But what about

struct b {
   char *c[0] //sizeof(b) is 0?  where does c point?
};

int j;
struct b myb; //myb.c == (&j)+1 == $esp?

Furthermore, how is the address of a zero length array known if space for its pointer is never allocated anywhere? I suppose the same way a regular array's address is known, but I'm struggling to wrap my mind around it at the moment.

jdizzle
  • 4,078
  • 1
  • 30
  • 38

5 Answers5

12

The only time I've ever seen zero-length arrays actually used is when you want to have a variable length structure.

As in this example taken from here

    struct line {
       int length;
       char contents[0];
     };

     struct line *thisline = (struct line *)
       malloc (sizeof (struct line) + this_length);
     thisline->length = this_length;

You don't want to use a pointer in the above struct as you want the contents to be part of the allocated memory of the structure.

If you do a sizeof on the above struct it doesn't include any space for the contents. I'm not sure if this ever made it into a standard, it gives warnings on various compilers.

Andrew Barrett
  • 19,721
  • 4
  • 47
  • 52
  • Thank you for explaining the practical use of this idiom. Whether it's the right thing to do is beside the point if one is trying to understand code that makes use of it. – Eric Walker Mar 24 '13 at 20:38
10

ISO C forbids 0-length arrays.

char **c1;

This defines an object c1 of type pointer-to-pointer-to-char.

char *c2[0];

This is a compile-error. Not allowed. Not in C, not in C++.

struct a {
  int i;
  char c[0]; //sizeof(a) is sizeof(int)?  a.c == (&i)+1?
};

Error as noted previously -- the size of an array must be greater than zero.

 struct a {
  int i;
  char c[1]; 
};

Also known as the struct-hack. Abused in low-level code on nearly all OSes -- Linux, Windows.

C99 does give us a sizeless array better known as flexible-array member:

struct a {
  int i;
  char c[]; /* note: no size */ 
};
dirkgently
  • 108,024
  • 16
  • 131
  • 187
  • Ahh, but if you retyped it ias char c[]; and had c as the last member of a, it would be legal as of C99. Whee! – MSN Mar 09 '09 at 18:20
  • @MSN -- added that a few seconds before your comment was posted :) – dirkgently Mar 09 '09 at 18:21
  • That's not always a compile-time error. gcc only warns about it, and that's only with -Wall and -pedantic flags. – Matt K Mar 09 '09 at 18:22
  • I said ISO C at the very top. For me -std=c99 or -std=c89 is a must along with -ansi. – dirkgently Mar 09 '09 at 18:27
  • 1
    given straight gcc *will* compile it, are all my assessments correct (regarding addressing and whose pointed where)? – jdizzle Mar 09 '09 at 19:45
  • I don't know what you mean by straight GCC. And what you mean by correct. If you take the definition of correctness to be what the C language standard says, then no -- there are errors, as I've pointed out. Let me know if you have more questions. – dirkgently Mar 09 '09 at 19:48
  • I can compile my examples with gcc. Are my interpretations of the result correct. That's what I'm wondering. – jdizzle Mar 09 '09 at 19:58
  • You can compile them because GCC accepts an extended version of the language. Extensions do not define the language. The standard does. So, if you think the C language allows 0-sized arrays -- you are wrong. – dirkgently Mar 09 '09 at 20:21
  • of course this is THE answer to the question :) T t[0] is forbidden, because there are no zero size objects. For the rationale in times when even T t[] as a FAM was disallowed: http://www.lysator.liu.se/c/rat/c5.html#3-5-4-2 +1 :D – Johannes Schaub - litb Mar 09 '09 at 21:07
  • @litb: One of my favorite links. Thanks! – dirkgently Mar 10 '09 at 06:21
  • 1
    @litb: I disagree with that rationale, since from what I understand a compiler would be free to assume that if an array has a declared size of one, any accesses to the array access the first element (any other access would be Undefined Behavior, even if there's allocated space beyond the end of the structure). Having a special case for zero-sized arrays (declaring that a compiler must not make assumptions about the 'real' size) seems cleaner than requiring programmers to either use a non-standards-conforming hack or guess at a maximum size. – supercat Jul 18 '11 at 20:57
  • 5
    I think this answer is a little pedantic. The OP seems to be trying to understand why a certain idiom is being used. Whether that idiom is legal according to C99 is a nice detail to add to a complete explanation. But the fact is some code (e.g., Git) makes use of it, and people want to understand what the code is doing. – Eric Walker Mar 24 '13 at 20:40
  • 1
    @EricWalker, agreed. Zero length arrays are completely valid in GNU C, so this answer feels incomplete. I have seen this used in the Wi-Fi driver code in the Linux kernel. Also, see http://gcc.gnu.org/onlinedocs/gcc/Zero-Length.html – AriX Apr 04 '13 at 01:30
2

You might get some answers here from the GCC docs. But that focuses on the topic of having a zero-length array as the last members of the struct. It doesn't give direct answers regarding sizeof.

(As has been made clear by others, zero-length arrays aren't in standard C, but they were in GNU C.)

Aaron McDaid
  • 26,501
  • 9
  • 66
  • 88
1

From the C99 standard (7.20.3), dealing with allocation functions:

Each such allocation shall yield a pointer to an object disjoint from any other object.

[...]

If the size of the space requested is zero, the behavior is implementation defined: either a null pointer is returned, or the behavior is as if the size were some nonzero value, except that the returned pointer shall not be used to access an object.

In other words, in the case of declaring b on the stack without an initializer (where b is has c declared as c[] instead of c[0]), the actual size of b in terms of space actually used will be > 0, since you cannot access any part of b. If it's allocated via malloc, it will either be returned as 0 or as some unique value that cannot be accessed (if you use sizeof(b)).

Jonathan Leffler
  • 730,956
  • 141
  • 904
  • 1,278
MSN
  • 53,214
  • 7
  • 75
  • 105
  • Surely that section of the standard applies only malloc? I can't see th relevance of the earlier part of this answer. What does "space actually used will be > 0, since you cannot access any part of b" – Aaron McDaid Dec 23 '11 at 19:58
0

An array cannot have zero size.

ISO 9899:2011 6.7.6.2:

If the expression is a constant expression, it shall have a value greater than zero.

The above text is true both for a plain array (§1) and a VLA (§5). This is normative text in the C standard. A compiler is not allowed to implement it differently.

gcc -std=c99 -pedantic gives a warning for this.

The Beast
  • 1,629
  • 2
  • 29
  • 42