Empty arrays in structs

R

Rennie deGraaf

A question regarding this code, which defines a struct containing a size
and a variable-sized array:

typedef struct
{
uint16_t count;
unsigned char bytes[];
} foo_t;

....

foo_t* foo = (foo_t*)malloc(100 + sizeof(foo_t));

GCC 3.3.2 accepts this, and returns '2' as the result of
'sizeof(foo_t)'. I realize that the size of any struct is
machine/compiler dependent, but can I reasonably expect this code to be
portable? My reading of K&R implies that an array with no size is an
incomplete type, and that you can't take the size of an incomplete type,
and also that structs can't contain objects of incomplete types.
However, I couldn't find anything in the GCC documentation stating that
this was one of their extensions.

If this is not portable, then what is the best way to define a portable
variable-sized array with a header in a contiguous block of memory?
Would I need to just malloc an array, cast the header bytes
appropriately when necessary, and offset everything?

Rennie
 
A

Artie Gold

Rennie said:
A question regarding this code, which defines a struct containing a size
and a variable-sized array:

typedef struct
{
uint16_t count;
unsigned char bytes[];
} foo_t;

...

foo_t* foo = (foo_t*)malloc(100 + sizeof(foo_t));

GCC 3.3.2 accepts this, and returns '2' as the result of
'sizeof(foo_t)'. I realize that the size of any struct is
machine/compiler dependent, but can I reasonably expect this code to be
portable? My reading of K&R implies that an array with no size is an
incomplete type, and that you can't take the size of an incomplete type,
and also that structs can't contain objects of incomplete types.
However, I couldn't find anything in the GCC documentation stating that
this was one of their extensions.

If this is not portable, then what is the best way to define a portable
variable-sized array with a header in a contiguous block of memory?
Would I need to just malloc an array, cast the header bytes
appropriately when necessary, and offset everything?

It is a C99-ism. See, for example:

http://home.tiscalinet.ch/t_wolf/tw/c/c9x_changes.html#Syntax

at number 28.

HTH,
-ag
 
M

Michael Mair

Artie said:
Rennie said:
A question regarding this code, which defines a struct containing a size
and a variable-sized array:

typedef struct
{
uint16_t count;
unsigned char bytes[];
} foo_t;

...

foo_t* foo = (foo_t*)malloc(100 + sizeof(foo_t));

GCC 3.3.2 accepts this, and returns '2' as the result of
'sizeof(foo_t)'. I realize that the size of any struct is
machine/compiler dependent, but can I reasonably expect this code to be
portable? My reading of K&R implies that an array with no size is an
incomplete type, and that you can't take the size of an incomplete type,
and also that structs can't contain objects of incomplete types.
However, I couldn't find anything in the GCC documentation stating that
this was one of their extensions.

As Artie stated: This is C99. However, variable length arrays
are among the things gcc does not do right (yet), especially
for flexible array members (there is an offsetof issue where
I think the standard has good semantics and is better than the
gcc extension for VLAs but it would break old gnu89 [C89 plus
GNU extensions]).
Have a look at
gcc.gnu.org/c99status.html
In the list at the bottom, you can find a link to a discussion
about flexible array members.


As for portability: Have a look around whether all compilers
on all intended systems will support a large enough subset of
C99 to make this work. Apart from that: Do not rely on a certain
memory representation of the struct. Another implementation may
introduce different padding and so on. So, "bitwise portability"
is not possible if that is what you have in mind.


In C89 there is also the struct hack to achieve roughly the
same but it is not considered to be conforming.
See FAQ 2.6 at
http://www.eskimo.com/~scs/C-faq/top.html
Note however, that the HTML version of the FAQ is not up to
date; get the ASCII version (linked to from there) to read
the current version.


Cheers
Michael
 
H

Herbert Rosenau

A question regarding this code, which defines a struct containing a size
and a variable-sized array:

typedef struct
{
uint16_t count;
unsigned char bytes[];
} foo_t;

...

foo_t* foo = (foo_t*)malloc(100 + sizeof(foo_t));

You are trying to enter the lands of undefined behavior. You should
know that a cast only to get the compiler silent is an error. There is
no need to cast the result of a function that returns pointer to void.
Either you casts garbidge or it would be superflous at all. #include
stdlib.h and you have done anything you need to satisfy the compiler -
and when the compiler gets not satisfyed you knows YOU've done some
mistake - but that mistake will be never a lacking cast.
GCC 3.3.2 accepts this, and returns '2' as the result of
'sizeof(foo_t)'. I realize that the size of any struct is
machine/compiler dependent, but can I reasonably expect this code to be
portable?

No. Its an GCC extension.

My reading of K&R implies that an array with no size is an
incomplete type, and that you can't take the size of an incomplete type,
and also that structs can't contain objects of incomplete types.
However, I couldn't find anything in the GCC documentation stating that
this was one of their extensions.

Yes. But ANSI 99/98 allows to create arrays of 0 bytes in size as
placeholder for arrays of variable size.

That means change bytes[] into bytes[0] for ANSI 999/98 or bytes[1]
for ANSI 89.
If this is not portable, then what is the best way to define a portable
variable-sized array with a header in a contiguous block of memory?
Would I need to just malloc an array, cast the header bytes
appropriately when necessary, and offset everything?

Rennie

struct s {
size_t cb; /* optionally, but often helpful */
....
char t[0]; /* ANSI 99/98 OR
char t[1]; ANSI 89 */
};

struct s *ps = malloc(sizeof(struct s) + strlen(my_new_t) + 1);
strcpy(ps->t, my_new_t);
ps->cb = sizeof(struct s) + strlen(my_new_t) + 1;

for ANSI C 89:
struct s {
size_t cb; /* optionally, but often helpful */
....
char t[1]; /* ANSI 99/98 OR
char t[1]; ANSI 89 */
};

struct s *ps = malloc(sizeof(struct s) + strlen(my_new_t));
strcpy(ps->t, my_new_t);
ps->cb = sizeof(struct s) + strlen(my_new_t);


--
Tschau/Bye
Herbert

Visit http://www.ecomstation.de the home of german eComStation
eComStation 1.2 Deutsch ist da!
 
M

Michael Mair

Herbert said:
A question regarding this code, which defines a struct containing a size
and a variable-sized array:

typedef struct
{
uint16_t count;
unsigned char bytes[];
} foo_t;

...

foo_t* foo = (foo_t*)malloc(100 + sizeof(foo_t));


You are trying to enter the lands of undefined behavior. You should
know that a cast only to get the compiler silent is an error. There is
no need to cast the result of a function that returns pointer to void.
Either you casts garbidge or it would be superflous at all. #include
stdlib.h and you have done anything you need to satisfy the compiler -
and when the compiler gets not satisfyed you knows YOU've done some
mistake - but that mistake will be never a lacking cast.

GCC 3.3.2 accepts this, and returns '2' as the result of
'sizeof(foo_t)'. I realize that the size of any struct is
machine/compiler dependent, but can I reasonably expect this code to be
portable?


No. Its an GCC extension.
Wrong.


My reading of K&R implies that an array with no size is an
incomplete type, and that you can't take the size of an incomplete type,
and also that structs can't contain objects of incomplete types.
However, I couldn't find anything in the GCC documentation stating that
this was one of their extensions.


Yes. But ANSI 99/98 allows to create arrays of 0 bytes in size as
placeholder for arrays of variable size.

That means change bytes[] into bytes[0] for ANSI 999/98 or bytes[1]
for ANSI 89.

Wrong. bytes[] for C99. Have a look at 6.7.2.1, especially at the
example (which is of course not part of the standard but illustrates
the point quickly).
And the C89 struct hack (FAQ 2.6) is _not_ conforming even though
working everywhere.
If this is not portable, then what is the best way to define a portable
variable-sized array with a header in a contiguous block of memory?
Would I need to just malloc an array, cast the header bytes
appropriately when necessary, and offset everything?

Rennie


struct s {
size_t cb; /* optionally, but often helpful */
....
char t[0]; /* ANSI 99/98 OR
char t[1]; ANSI 89 */
};

struct s *ps = malloc(sizeof(struct s) + strlen(my_new_t) + 1);
strcpy(ps->t, my_new_t);
ps->cb = sizeof(struct s) + strlen(my_new_t) + 1;

for ANSI C 89:
struct s {
size_t cb; /* optionally, but often helpful */
....
char t[1]; /* ANSI 99/98 OR
char t[1]; ANSI 89 */
};

struct s *ps = malloc(sizeof(struct s) + strlen(my_new_t));
strcpy(ps->t, my_new_t);
ps->cb = sizeof(struct s) + strlen(my_new_t);

The one thing is wrong, the other plainly non-conforming.


-Michael
 
M

Method Man

struct s {
size_t cb; /* optionally, but often helpful */
....
char t[0]; /* ANSI 99/98 OR
char t[1]; ANSI 89 */
};

struct s *ps = malloc(sizeof(struct s) + strlen(my_new_t) + 1);
strcpy(ps->t, my_new_t);
ps->cb = sizeof(struct s) + strlen(my_new_t) + 1;

for ANSI C 89:
struct s {
size_t cb; /* optionally, but often helpful */
....
char t[1]; /* ANSI 99/98 OR
char t[1]; ANSI 89 */
};

struct s *ps = malloc(sizeof(struct s) + strlen(my_new_t));
strcpy(ps->t, my_new_t);
ps->cb = sizeof(struct s) + strlen(my_new_t);

Is this code safe? I can see the purpose behind it, but I think accessing an
array beyond its bounds (regardless if there is allocated space to do so)
may cause problems. Can someone confirm or deny this?
 
H

Herbert Rosenau

struct s {
size_t cb; /* optionally, but often helpful */
....
char t[0]; /* ANSI 99/98 OR
char t[1]; ANSI 89 */
};

struct s *ps = malloc(sizeof(struct s) + strlen(my_new_t) + 1);
strcpy(ps->t, my_new_t);
ps->cb = sizeof(struct s) + strlen(my_new_t) + 1;

for ANSI C 89:
struct s {
size_t cb; /* optionally, but often helpful */
....
char t[1]; /* ANSI 99/98 OR
char t[1]; ANSI 89 */
};

struct s *ps = malloc(sizeof(struct s) + strlen(my_new_t));
strcpy(ps->t, my_new_t);
ps->cb = sizeof(struct s) + strlen(my_new_t);

Is this code safe? I can see the purpose behind it, but I think accessing an
array beyond its bounds (regardless if there is allocated space to do so)
may cause problems. Can someone confirm or deny this?

It would NOT work for static created structs!

so

struct s;
struct s[4711];

will NOT work as expected. Get memory dynamically using the malloc()
family or OS dependand memory allocation and you gets what you asks
for. C knows nothing about bounds checking - if you use a bounds
checker on your system it may cry - or may not.

--
Tschau/Bye
Herbert

Visit http://www.ecomstation.de the home of german eComStation
eComStation 1.2 Deutsch ist da!
 
K

kevin.bagust

struct s {
size_t cb; /* optionally, but often helpful */
....
char t[0]; /* ANSI 99/98 OR
char t[1]; ANSI 89 */
};

struct s *ps = malloc(sizeof(struct s) + strlen(my_new_t) + 1);
strcpy(ps->t, my_new_t);
ps->cb = sizeof(struct s) + strlen(my_new_t) + 1;

for ANSI C 89:
struct s {
size_t cb; /* optionally, but often helpful */
....
char t[1]; /* ANSI 99/98 OR
char t[1]; ANSI 89 */
};

struct s *ps = malloc(sizeof(struct s) + strlen(my_new_t));
strcpy(ps->t, my_new_t);
ps->cb = sizeof(struct s) + strlen(my_new_t);
Is this code safe? I can see the purpose behind it, but I think accessing an
array beyond its bounds (regardless if there is allocated space to do so)
may cause problems. Can someone confirm or deny this?

Officially in C89 accessing a array beyond its bounds causes undefined
behaviour, but it works on all C compilers I have seen. In C99 they added
the variable length field to make this defined behaviour.

I try to avoid this, but I have used it occasionally where memory was
extremely tight.

Kevin.
 

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

No members online now.

Forum statistics

Threads
473,774
Messages
2,569,596
Members
45,143
Latest member
SterlingLa
Top