tags:

views:

932

answers:

3

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.

+6  A: 

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
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
@MSN -- added that a few seconds before your comment was posted :)
dirkgently
That's not always a compile-time error. gcc only warns about it, and that's only with -Wall and -pedantic flags.
Matt Kane
I said ISO C at the very top. For me -std=c99 or -std=c89 is a must along with -ansi.
dirkgently
given straight gcc *will* compile it, are all my assessments correct (regarding addressing and whose pointed where)?
jdizzle
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
I can compile my examples with gcc. Are my interpretations of the result correct. That's what I'm wondering.
jdizzle
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
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
@litb: One of my favorite links. Thanks!
dirkgently
+1  A: 

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
+1  A: 

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)).

MSN