Jonas Lund of https://whizzter.woorlic.org/ mentioned this
trick in a HackerNews comment:
Given:
struct S {
// ...
T A[];
};
Don't do this:
malloc(offsetof(S, A) + n * sizeof (T));
But rather this:
malloc(offsetof(S, A[n]));
It's easy to forget that the second argument of offsetof is a
designator, not simply a member name.
Il 08/10/2025 08:35, Kaz Kylheku ha scritto:
Jonas Lund of https://whizzter.woorlic.org/ mentioned this
trick in a HackerNews comment:
Given:
struct S {
// ...
T A[];
};
Don't do this:
malloc(offsetof(S, A) + n * sizeof (T));
But rather this:
malloc(offsetof(S, A[n]));
It's easy to forget that the second argument of offsetof is a
designator, not simply a member name.
struct S {
unsigned int size;
unsigned char mode;
unsigned char array[];
}
In a 32-bits integer machine, sizeof(struct S) is 8, because there are
3 bytes of padding after mode and array is considered empty.
Now I want to store 9 bytes in array[]. I could use:
malloc(sizeof(struct S) + 9 * sizeof(unsigned char))=malloc(17)
or I could use:
malloc(offsetof(struct S, array[9]))=malloc(14)
Is the second better (and correct) than the first?
And another question. Suppose I need an array of struct S. All
elements have 7-bytes array[] member. How to allocate this array and
access each element?
I think I can't use the first malloc (17), neither the second
(14). Both aren't a multiple of alignment length.
BGB <cr88192@gmail.com> writes:
On 10/8/2025 1:35 AM, Kaz Kylheku wrote:
Jonas Lund of https://whizzter.woorlic.org/ mentioned this
trick in a HackerNews comment:
Given:
struct S {
// ...
T A[];
};
Don't do this:
malloc(offsetof(S, A) + n * sizeof (T));
But rather this:
malloc(offsetof(S, A[n]));
It's easy to forget that the second argument of offsetof is a
designator, not simply a member name.
This is assuming offsetof and can deal with general expressions (vs
just field names). IIRC, it is only required to work with field names
(and with plain structs).
I just read that part of the standard, and it's not clear whether
the second argument to offsetof() has to be a member name or whether
it can be something more elaborate.
Quoting the N3096 draft of C23, 7.21:
offsetof(type, member-designator)
which expands to an integer constant expression that has type
`size_t`, the value of which is the offset in bytes, to the
subobject (designated by *member-designator*), from the beginning
of any object of type *type*. The type and member designator
shall be such that given
static type t;
then the expression &(t. *member-designator*) evaluates to
an address constant. If the specified *type* defines a new
type or if the specified member is a bit-field, the behavior
is undefined.
The requirements imply that the type can be a struct or a union.
The term "member designator" is not used elsewhere in the standard.
If the term to be taken literally, then it has to designate a
*member*, not an element of a member. But the term "subobject",
along with the address constant requirement, could imply that it
could be an arbitrary sequence of members and array elements.
| Sysop: | DaiTengu |
|---|---|
| Location: | Appleton, WI |
| Users: | 1,090 |
| Nodes: | 10 (0 / 10) |
| Uptime: | 45:19:32 |
| Calls: | 13,946 |
| Calls today: | 3 |
| Files: | 187,034 |
| D/L today: |
8,061 files (2,942M bytes) |
| Messages: | 2,460,944 |