C99 said that the size of a structure that ends in a flexible array
member is the same as the offset of that flexible member in a
similar structure in which the array has some unspecified size.
The latest draft says that the size is calculated as if the flexible
array member were omitted, except that there may be more padding than
the omission would imply.
I can't think of a reasonable interpretation of the original wording
which would allow the size to be other than the offset of the array,
when the array is of a character type.
The current wording clearly does allow the size to go beyond the offset
in that case.
Don't get burned: don't rely on the size of a flexible array struct.
Use the offsetof that flexible member.
On Mon, 7 Oct 2024 02:32:13 -0000 (UTC), Kaz Kylheku wrote:
I can't think of a reasonable interpretation of the original wording
which would allow the size to be other than the offset of the array,
when the array is of a character type.
The current wording clearly does allow the size to go beyond the offset
in that case.
The original wording includes no requirement that the offset of the replacement array used for the size calculation has any relationship whatsoever with the offset of the flexible array member.
For example, in
struct foo { int a; char b; char c[]; };
in many real-world implementations the offset of c is 5 but the size
of the structure is 8.
On these implementations, the size matches the
offset of c in a similar structure where c is replaced by a length-1
array of int, and also matches the size of a similar structure with c deleted, so this is consistent with old and new wordings.
I don't think the updated wording alters any implementation requirement,
but it does seem quite a bit less complicated to explain.
Don't get burned: don't rely on the size of a flexible array struct.
Use the offsetof that flexible member.
An evil compiler could probably make the size less than the offset
of the flexible array member and be conforming, with both old and
new wordings. This would break some examples but an evil compiler
obviously won't care about non-normative trivialities like examples.
On 2024-10-07, Nick Bowler <nbowler@draconx.ca> wrote:
On Mon, 7 Oct 2024 02:32:13 -0000 (UTC), Kaz Kylheku wrote:What GCC seems to be doing is simply nothing special. When determining
the most strictly aligned member of the struct, it takes the flexible
array into account (the alignment of its element type). It otherwise
ignores it (or perhaps treats it as a size zero subobject). The
structure is padded after that for the sake of the most strictly
aligned member.
Don't get burned: don't rely on the size of a flexible array struct.
Use the offsetof that flexible member.
If the size is anything other than what the program expects, whether
it is larger or smaller, that breaks the program.
For instance, if the wrong value is used when displacing a pointer to
the flexible member to recover a pointer to the struct.
This issue showed up in exactly one program of mine in which I
experimented with using the flexible array member.
It was reported by a user who ran into a crash.
str 0x9d62b4old dstr_of 0x9d629c
On 2024-10-07, Kaz Kylheku wrote:
On 2024-10-07, Nick Bowler <nbowler@draconx.ca> wrote:
On Mon, 7 Oct 2024 02:32:13 -0000 (UTC), Kaz Kylheku wrote:What GCC seems to be doing is simply nothing special. When determining
the most strictly aligned member of the struct, it takes the flexible
array into account (the alignment of its element type). It otherwise
ignores it (or perhaps treats it as a size zero subobject). The
structure is padded after that for the sake of the most strictly
aligned member.
Don't get burned: don't rely on the size of a flexible array struct.
Use the offsetof that flexible member.
If the size is anything other than what the program expects, whether
it is larger or smaller, that breaks the program.
For instance, if the wrong value is used when displacing a pointer to
the flexible member to recover a pointer to the struct.
This issue showed up in exactly one program of mine in which I
experimented with using the flexible array member.
It was reported by a user who ran into a crash.
As the user who had the pleasure of running into said crash, here is a
brief demo of the sizes and addresses reported by my system (gcc 13.3.1) >using both methods of determining the start of the struct:
#include <stdio.h>
#include <stdlib.h>
#include <stddef.h>
typedef struct dstr {
int a;
size_t b;
int c;
char str[];
} dstr;
typedef struct ref {
int a;
size_t b;
int c;
} ref;
#define old_dstr_of(str) ((dstr *) ((str) - sizeof (dstr)))
#define new_dstr_of(s) ((dstr *) ((s) - offsetof (struct dstr, str)))
int main (int argc, char ** argv)
{
dstr *ds = malloc (sizeof (dstr));
printf ("sizeof(int) %zu\n", sizeof (int));
printf ("sizeof(char) %zu\n", sizeof (char));
printf ("sizeof(size_t) %zu\n", sizeof (size_t));
printf ("sizeof(dstr) %zu\n", sizeof (dstr));
printf ("sizeof(ref) %zu\n", sizeof (ref));
puts ("");
puts ("Addresses:");
printf ("ds %p\n", ds);
printf ("ds->str %p\n", ds->str);
printf ("old dstr_of %p\n", old_dstr_of(ds->str));
printf ("new dstr_of %p\n", new_dstr_of(ds->str));
}
And the output on my machine:
sizeof(int) 4
sizeof(char) 1
sizeof(size_t) 8
sizeof(dstr) 24
sizeof(ref) 24
Addresses:
ds 0x9d62a0
str 0x9d62b4old dstr_of 0x9d629c
new dstr_of 0x9d62a0
str 0x2350024old dstr_of 0x235000c
str 0xbce020old dstr_of 0xbce010
On Mon, 7 Oct 2024 02:32:13 -0000 (UTC), Kaz Kylheku wrote:
I can't think of a reasonable interpretation of the original
wording which would allow the size to be other than the offset
of the array, when the array is of a character type.
The current wording clearly does allow the size to go beyond
the offset in that case.
The original wording includes no requirement that the offset of
the replacement array used for the size calculation has any
relationship whatsoever with the offset of the flexible array
member.
Sysop: | DaiTengu |
---|---|
Location: | Appleton, WI |
Users: | 991 |
Nodes: | 10 (0 / 10) |
Uptime: | 120:05:38 |
Calls: | 12,958 |
Files: | 186,574 |
Messages: | 3,265,641 |