Zero length array declaration

C

Chris Torek

I'm not sure why you chose a pointer to long double over a
pointer to void (or to char). If long doubles need 8-byte
alignment, then a pointer to long double could in theory be
shorter than a pointer to void.

A good point. I wrote the text in ">>" above fairly hastily,
in the few minutes I had between various scheduled interruptions. :)
 
C

Christian Bau

I'm not sure why you chose a pointer to long double over a
pointer to void (or to char). If long doubles need 8-byte
alignment, then a pointer to long double could in theory be
shorter than a pointer to void.

A good point. I wrote the text in ">>" above fairly hastily,
in the few minutes I had between various scheduled interruptions. :)[/QUOTE]

It is quite possible that double is implemented in hardware and needs
eight byte alignment, while long double is for example 128 bit,
implemented in software using four 32 bit integers, and needs an
alignment of four byte only.
 
K

Keith Thompson

Spiro Trikaliotis said:
Well, a separate memory allocation is not needed. You can alloc
sizeof(struct) + extra bytes, and then have bar point to "after the
struct".

Or is there anything else in the standard that forbids this?

Yes, that's another possible approach. There are some drawbacks. If
you make a copy of the structure, the pointer still points back to the
original structure (for example if you want to expand the array with
realloc()). If the array is of something other than a character type,
there's no good way to align it properly.

Overall, I'd say the classic "struct hack" is a better approach. It's
effectively portable to most or all pre-C99 implementations, and it's
portable to any conforming C99 implementation with a slightly
different syntax. You could probably use it portably with a
preprocessor test for __STDC_VERSION__.

Here's a sample program, that I *think* is portable to all conforming
C99 implementations and to any C90 implementation that supports the
classic struct hack (though in the latter case it may allocate
slightly more memory than it needs to).

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#if defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901
#define FLEXIBLE /* empty definition */
#else
#define FLEXIBLE 1
#endif

struct hack {
int len;
char str[FLEXIBLE];
};

#define MESSAGE "hello, world"

int main(void)
{
struct hack *ptr = malloc(sizeof *ptr + sizeof MESSAGE);
ptr->len = strlen(MESSAGE);
strcpy(ptr->str, MESSAGE);
printf("ptr->len = %d\n", ptr->len);
printf("ptr->str = \"%s\"\n", ptr->str);
return 0;
}

As for why C90 didn't choose to bless existing practice, I suspect
that the committee wanted to allow for range-checking implementations.
The struct hack in its classic form depends on being able to access an
array beyond its declared bounds, which invokes undefined behavior but
happens to work. C99 avoided this by introducing a new syntax in
which the array doesn't have declared bounds, so a range-checking
implementation can still do its checking in the normal case.
 
C

CBFalconer

Keith said:
.... snip ...

Here's a sample program, that I *think* is portable to all conforming
C99 implementations and to any C90 implementation that supports the
classic struct hack (though in the latter case it may allocate
slightly more memory than it needs to).

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#if defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901
#define FLEXIBLE /* empty definition */
#else
#define FLEXIBLE 1
#endif

struct hack {
int len;
char str[FLEXIBLE];
};

#define MESSAGE "hello, world"

int main(void)
{
struct hack *ptr = malloc(sizeof *ptr + sizeof MESSAGE);
ptr->len = strlen(MESSAGE);
strcpy(ptr->str, MESSAGE);
printf("ptr->len = %d\n", ptr->len);
printf("ptr->str = \"%s\"\n", ptr->str);
return 0;
}

nitpick: I think you are over allocating space by one byte in C99.
 
K

Keith Thompson

CBFalconer said:
Keith said:
... snip ...

Here's a sample program, that I *think* is portable to all conforming
C99 implementations and to any C90 implementation that supports the
classic struct hack (though in the latter case it may allocate
slightly more memory than it needs to).

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#if defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901
#define FLEXIBLE /* empty definition */
#else
#define FLEXIBLE 1
#endif

struct hack {
int len;
char str[FLEXIBLE];
};

#define MESSAGE "hello, world"

int main(void)
{
struct hack *ptr = malloc(sizeof *ptr + sizeof MESSAGE);
ptr->len = strlen(MESSAGE);
strcpy(ptr->str, MESSAGE);
printf("ptr->len = %d\n", ptr->len);
printf("ptr->str = \"%s\"\n", ptr->str);
return 0;
}

nitpick: I think you are over allocating space by one byte in C99.

I don't think so. I know I'm over-allocating by at least one byte in
C90 (possibly more if there's padding at the end of the structure).
But if I'm wrong, can you explain?
 
C

CBFalconer

Keith said:
CBFalconer said:
Keith said:
... snip ...

Here's a sample program, that I *think* is portable to all conforming
C99 implementations and to any C90 implementation that supports the
classic struct hack (though in the latter case it may allocate
slightly more memory than it needs to).

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#if defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901
#define FLEXIBLE /* empty definition */
#else
#define FLEXIBLE 1
#endif

struct hack {
int len;
char str[FLEXIBLE];
};

#define MESSAGE "hello, world"

int main(void)
{
struct hack *ptr = malloc(sizeof *ptr + sizeof MESSAGE);
ptr->len = strlen(MESSAGE);
strcpy(ptr->str, MESSAGE);
printf("ptr->len = %d\n", ptr->len);
printf("ptr->str = \"%s\"\n", ptr->str);
return 0;
}

nitpick: I think you are over allocating space by one byte in C99.

I don't think so. I know I'm over-allocating by at least one byte in
C90 (possibly more if there's padding at the end of the structure).
But if I'm wrong, can you explain?

s/c99/c90/

You are overallocating somewhere :)
 
J

jacob navia

Eric said:
jacob said:
RS wrote:

Hi,

Looking to see if the following construct is valid:
typedef struct {
int foo;
char bar[0];
} foobar;

Basically, the idea is to have the structure above point to a message
buffer that has a 4-byte integer followed by a stream of variable
number of bytes. The structure member "bar" is used to reference the
stream of bytes. The above code compiles fine with a GNU based PPC
cross-compiler running on Solaris and seems to do the function
intended.

My question is: Is it legal to declare an array with zero length? Or
should bar have been declared to be at least one element in length?

Comments appreciated.

RS


The correct declaration in C99 is:

typedef struct {
int foo;
char bar[];
} foobar;

This will work as intended in any C99 compiler

sizeof(foobar) == sizeof(int);


No; sizeof(foobar) >= sizeof(int) is all you can
be sure of. The compiler may insert padding between
the `foo' and `bar' members, even though there seems
no obvious reason to do so in this case.

Yes, you are right.

A more portable expression would be:

sizeof(foobar) == offsetof(foobar,bar);

Thanks for the correction
 
X

xarax

jacob navia said:
Eric said:
jacob said:
RS wrote:


Hi,

Looking to see if the following construct is valid:
typedef struct {
int foo;
char bar[0];
} foobar;

Basically, the idea is to have the structure above point to a message
buffer that has a 4-byte integer followed by a stream of variable
number of bytes. The structure member "bar" is used to reference the
stream of bytes. The above code compiles fine with a GNU based PPC
cross-compiler running on Solaris and seems to do the function
intended.

My question is: Is it legal to declare an array with zero length? Or
should bar have been declared to be at least one element in length?

Comments appreciated.

RS


The correct declaration in C99 is:

typedef struct {
int foo;
char bar[];
} foobar;

This will work as intended in any C99 compiler

sizeof(foobar) == sizeof(int);


No; sizeof(foobar) >= sizeof(int) is all you can
be sure of. The compiler may insert padding between
the `foo' and `bar' members, even though there seems
no obvious reason to do so in this case.

Yes, you are right.

A more portable expression would be:

sizeof(foobar) == offsetof(foobar,bar);

Thanks for the correction

I don't think any conforming compiler will insert
padding bytes between "foo" and "bar", because padding
bytes are only allowed for alignment purposes. The
"bar" member has alignment 1, so it can always be
placed immediately adjacent to "foo" which may have
a larger alignment.
 
K

Keith Thompson

CBFalconer said:
s/c99/c90/

You are overallocating somewhere :)

Yes.

BTW, I acknowledged the C90 over-allocation when I posted the code.
It didn't seem worthwhile to complicate the code for the sake of such
a small over-allocation, but it could be significant if the array
element is bigger. Now that I think about it, it's easy enough to fix
by using offsetof() rather than sizeof.
 
K

Keith Thompson

xarax said:
I don't think any conforming compiler will insert
padding bytes between "foo" and "bar", because padding
bytes are only allowed for alignment purposes. The
"bar" member has alignment 1, so it can always be
placed immediately adjacent to "foo" which may have
a larger alignment.

Sure, but the struct hack isn't limited to character arrays.
 
D

Dan Pop

In said:
I don't think any conforming compiler will insert
padding bytes between "foo" and "bar", because padding
bytes are only allowed for alignment purposes.

Can I have a chapter and verse for that?
The
"bar" member has alignment 1, so it can always be
placed immediately adjacent to "foo" which may have
a larger alignment.

It is the implementor who decides the optimal alignment for the fields
of a struct, based on his own criteria (and he doesn't even have to
document them). On a platform that doesn't support byte accesses, it
*may* be more efficient to align even arrays of char on word boundaries.

Dan
 
T

those who know me have no need of my name

in comp.lang.c i read:
Realistically, intmax_t from C99 helps a lot, but just those three
-- intmax_t, "a" pointer, and long double -- is probably not
sufficient for existing implementations. You need at least two
pointers, one to data and one to code:

better toss in a long double _Complex too.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,769
Messages
2,569,581
Members
45,056
Latest member
GlycogenSupporthealth

Latest Threads

Top